Skip to content

Commit 9d047d5

Browse files
nearestnaborsclaude
andcommitted
Remove x-conversations recipe (MCP Apps incompatible with recipes)
MCP Apps (rich UI) don't work in Goose scheduled recipes due to 500 errors when loading UI resources. Removing the scheduled conversations option since it requires the rich UI to be useful. Users can still use x_conversations in regular chat where the MCP Apps UI works correctly. - Delete recipes/x-conversations.yaml - Remove x_list_conversations tool (was only for recipes) - Update README and CLAUDE.md - Remove x-conversations recipe tests Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent bd4eedf commit 9d047d5

5 files changed

Lines changed: 23 additions & 191 deletions

File tree

README.md

Lines changed: 22 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -102,16 +102,15 @@ Reply to @anthropic_devs saying I'll share slides after the talk
102102
103103
## Tools
104104
105-
| Tool | Description |
106-
| ------------------------ | ------------------------------------------------------------- |
107-
| `x_auth_status` | Check authentication, show connect button if needed |
108-
| `x_conversations` | Show unreplied mentions as a rich UI conversation inbox |
109-
| `x_list_conversations` | List unreplied mentions as text (for scheduled recipes) |
110-
| `x_dismiss_conversation` | Dismiss a conversation (reappears on new activity) |
111-
| `x_draft_tweet` | Create draft with preview |
112-
| `x_post_tweet` | Post after approval |
113-
| `x_timeline_digest` | Fetch and summarize your Following timeline (past 24h) |
114-
| `x_show_tweet` | Display a single tweet as a rich card with reply option |
105+
| Tool | Description |
106+
| ------------------------ | ------------------------------------------------------- |
107+
| `x_auth_status` | Check authentication, show connect button if needed |
108+
| `x_conversations` | Show unreplied mentions as a conversation inbox |
109+
| `x_dismiss_conversation` | Dismiss a conversation (reappears on new activity) |
110+
| `x_draft_tweet` | Create draft with preview |
111+
| `x_post_tweet` | Post after approval |
112+
| `x_timeline_digest` | Fetch and summarize your Following timeline (past 24h) |
113+
| `x_show_tweet` | Display a single tweet as a rich card with reply option |
115114
116115
## Timeline Digest Setup
117116
@@ -146,52 +145,46 @@ google-chrome --remote-debugging-port=9222
146145

147146
Then make sure you're logged into X in that browser window. (I keep one open and minimized on startup.)
148147

149-
### Goose Recipes
148+
### Goose Recipe
150149

151-
ASSA includes two recipes for scheduled automation:
150+
ASSA includes a recipe for scheduled timeline digests:
152151

153-
| Recipe | Description |
154-
| ---------------------- | ------------------------------------------------------------------------------- |
155-
| `x-news-digest.yaml` | Daily digest of your Following timeline (requires Chrome with remote debugging) |
156-
| `x-conversations.yaml` | Check and respond to X mentions/conversations |
152+
| Recipe | Description |
153+
| -------------------- | ------------------------------------------------------------------------------- |
154+
| `x-news-digest.yaml` | Daily digest of your Following timeline (requires Chrome with remote debugging) |
157155

158-
#### Import a Recipe
156+
#### Import the Recipe
159157

160-
**Option 1: Follow a Deep Link**
158+
**Option 1: Generate a Deep Link**
161159

162160
```bash
163161
goose recipe deeplink recipes/x-news-digest.yaml
164-
goose recipe deeplink recipes/x-conversations.yaml
165162
```
166163

167-
This outputs a `goose://recipe?config=...` URL. Paste it in a browser to preview what it will output in Goose Desktop.
164+
This outputs a `goose://recipe?config=...` URL. Paste it in a browser to import into Goose Desktop.
168165

169166
**Option 2: Import in Goose Desktop**
170167

171168
1. Open **Goose Desktop**
172169
2. Click **Recipes** in the sidebar
173170
3. Click **Import Recipe** or browse for file
174-
4. Either past the link generated in Option 1 in the **Recipe Deeplink** input box, or choose a recipe from the `recipes/` folder in the **Recipe File** input field.
171+
4. Choose `recipes/x-news-digest.yaml` from the **Recipe File** input field
175172

176173
**Option 3: Run via CLI**
177174

178-
The outputs are in Markdown, enjoy!
179-
180175
```bash
181176
goose run --recipe recipes/x-news-digest.yaml
182177
```
183178

184-
(Not recommended for `x-conversations.yaml`, which return nothing because MCP Apps are iframes.)
185-
186-
#### Schedule a Recipe
179+
#### Schedule the Recipe
187180

188181
After importing, on the **Recipes** screen:
189182

190-
1. Click the little clock icon next the the recipe you want to automate.
191-
2. In the **Add Schedule** modal, select the frequency and time you want the recipe to run.
183+
1. Click the clock icon next to the recipe
184+
2. Select the frequency and time in the **Add Schedule** modal
192185
3. Click **Save**
193186

194-
**Note:** X News Digest requires Chrome running with `--remote-debugging-port=9222`.
187+
**Note:** Requires Chrome running with `--remote-debugging-port=9222`.
195188

196189
## Development
197190

recipes/x-conversations.yaml

Lines changed: 0 additions & 32 deletions
This file was deleted.

src/__tests__/recipes/recipes.test.ts

Lines changed: 0 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -93,38 +93,6 @@ describe("Goose Recipes", () => {
9393
});
9494
});
9595

96-
describe("x-conversations.yaml", () => {
97-
const recipe = parseRecipe("x-conversations.yaml");
98-
99-
test("has required title field", () => {
100-
expect(recipe.title).toBeDefined();
101-
expect(typeof recipe.title).toBe("string");
102-
expect(recipe.title!.length).toBeGreaterThan(0);
103-
});
104-
105-
test("has required description field", () => {
106-
expect(recipe.description).toBeDefined();
107-
expect(typeof recipe.description).toBe("string");
108-
expect(recipe.description!.length).toBeGreaterThan(0);
109-
});
110-
111-
test("has instructions or prompt", () => {
112-
const hasInstructions =
113-
recipe.instructions && recipe.instructions.length > 0;
114-
const hasPrompt = recipe.prompt && recipe.prompt.length > 0;
115-
expect(hasInstructions || hasPrompt).toBe(true);
116-
});
117-
118-
test("instructions mention x_list_conversations tool", () => {
119-
expect(recipe.instructions).toContain("x_list_conversations");
120-
});
121-
122-
test("has activities array", () => {
123-
expect(Array.isArray(recipe.activities)).toBe(true);
124-
expect(recipe.activities!.length).toBeGreaterThan(0);
125-
});
126-
});
127-
12896
// Generic validation for all recipe files
12997
describe("all recipes", () => {
13098
for (const filename of recipeFiles) {

src/server.ts

Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,7 @@ import {
2323

2424
// X tools
2525
import { xAuthStatus } from "./tools/auth-status.js";
26-
import {
27-
xConversations,
28-
xGetConversations,
29-
xListConversations,
30-
} from "./tools/conversations.js";
26+
import { xConversations, xGetConversations } from "./tools/conversations.js";
3127
import { xDismissConversation } from "./tools/dismiss-conversation.js";
3228
import { xDraftTweet } from "./tools/draft-tweet.js";
3329
import { xPostTweet } from "./tools/post-tweet.js";
@@ -217,19 +213,6 @@ const TOOLS: Tool[] = [
217213
},
218214
},
219215
},
220-
{
221-
name: "x_list_conversations",
222-
description:
223-
"List X conversations awaiting your reply as formatted text. " +
224-
"Use this in scheduled recipes or contexts where UI rendering is not available. " +
225-
"Returns conversations with links for easy access.",
226-
inputSchema: {
227-
type: "object",
228-
properties: {},
229-
required: [],
230-
},
231-
// No UI - returns text for recipes and scheduled tasks
232-
},
233216
];
234217

235218
// Tool handler dispatch
@@ -240,7 +223,6 @@ const toolHandlers: Record<string, ToolHandler> = {
240223
x_post_tweet: xPostTweet,
241224
x_conversations: xConversations,
242225
x_get_conversations: xGetConversations, // UI-only, returns full data
243-
x_list_conversations: xListConversations, // Text-only, for recipes
244226
x_dismiss_conversation: xDismissConversation,
245227
x_show_tweet: xShowTweet,
246228
x_timeline_digest: xTimelineDigest,

src/tools/conversations.ts

Lines changed: 0 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -467,82 +467,3 @@ export async function xGetConversations(params?: {
467467
],
468468
};
469469
}
470-
471-
/**
472-
* Format relative time for display
473-
*/
474-
function formatTimeAgo(isoDate: string): string {
475-
const date = new Date(isoDate);
476-
const now = new Date();
477-
const diffMs = now.getTime() - date.getTime();
478-
const diffMins = Math.floor(diffMs / 60_000);
479-
const diffHours = Math.floor(diffMins / 60);
480-
const diffDays = Math.floor(diffHours / 24);
481-
482-
if (diffMins < 60) {
483-
return `${diffMins}m ago`;
484-
}
485-
if (diffHours < 24) {
486-
return `${diffHours}h ago`;
487-
}
488-
return `${diffDays}d ago`;
489-
}
490-
491-
/**
492-
* Tool: x_list_conversations (text-only, for recipes)
493-
* Returns conversation data as formatted text instead of UI
494-
* Use this in recipes where MCP Apps are not supported
495-
*/
496-
export async function xListConversations(): Promise<unknown> {
497-
const result = await fetchConversations();
498-
499-
if (!result.success) {
500-
return result.content;
501-
}
502-
503-
const { conversations, username } = result.data;
504-
505-
if (conversations.length === 0) {
506-
return {
507-
content: [
508-
{
509-
type: "text",
510-
text: `No conversations awaiting reply for @${username}.`,
511-
},
512-
],
513-
};
514-
}
515-
516-
// Format conversations as text with links
517-
const lines: string[] = [
518-
`Found ${conversations.length} conversation${conversations.length === 1 ? "" : "s"} awaiting your reply (@${username}):`,
519-
"",
520-
];
521-
522-
for (const conv of conversations) {
523-
const timeAgo = formatTimeAgo(conv.created_at);
524-
const tweetUrl = `https://x.com/${conv.author_username}/status/${conv.tweet_id}`;
525-
const engagement = `${conv.like_count} likes, ${conv.retweet_count} RTs, ${conv.reply_count} replies`;
526-
527-
lines.push("---");
528-
lines.push(
529-
`[@${conv.author_username}](${tweetUrl}) (${conv.author_display_name}) - ${timeAgo}`
530-
);
531-
lines.push(conv.text);
532-
lines.push(`Engagement: ${engagement}`);
533-
}
534-
535-
lines.push("");
536-
lines.push(
537-
"To reply to a conversation, use the x_draft_tweet tool with the reply_to_id parameter."
538-
);
539-
540-
return {
541-
content: [
542-
{
543-
type: "text",
544-
text: lines.join("\n"),
545-
},
546-
],
547-
};
548-
}

0 commit comments

Comments
 (0)