@flowrelay/mcp-server 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -29,7 +29,7 @@ The server currently exposes these tools:
29
29
  - list_events
30
30
  - list_untracked_resources (lists event-producing resources not scoped to any project – useful for discovering untracked activity)
31
31
  - discord_list_channels
32
- - discord_send_message
32
+ - discord_send_message (plain text, or a handoff/insight by id or `last_*` rendered to Markdown and attached as a `.md` file)
33
33
 
34
34
  ## Environment Variables
35
35
 
package/dist/api.d.ts CHANGED
@@ -58,6 +58,8 @@ export interface HandoffResult {
58
58
  next_steps: string[];
59
59
  created_at: string;
60
60
  updated_at?: string;
61
+ /** Canonical Markdown rendered server-side – identical to the dashboard copy button. */
62
+ markdown?: string;
61
63
  }
62
64
  export interface InsightResult {
63
65
  id: string;
@@ -77,6 +79,8 @@ export interface InsightResult {
77
79
  related_event_ids: string[];
78
80
  created_at: string;
79
81
  updated_at?: string;
82
+ /** Canonical Markdown rendered server-side – identical to the dashboard copy button. */
83
+ markdown?: string;
80
84
  }
81
85
  export interface UntrackedResource {
82
86
  source: string;
@@ -180,7 +184,13 @@ export declare class FlowRelayAPI {
180
184
  topic: string;
181
185
  }>;
182
186
  }>;
183
- discordSendMessage(channelId: string, content: string): Promise<{
187
+ discordSendMessage(channelId: string, payload: {
188
+ content?: string;
189
+ handoff_id?: string;
190
+ insight_id?: string;
191
+ artifact?: 'last_handoff' | 'last_correlation' | 'last_onboarding' | 'last_architecture';
192
+ project_id?: string;
193
+ }): Promise<{
184
194
  ok: boolean;
185
195
  message_id: string;
186
196
  }>;
package/dist/api.js CHANGED
@@ -109,10 +109,10 @@ export class FlowRelayAPI {
109
109
  async discordListChannels() {
110
110
  return this.request('/discord/channels');
111
111
  }
112
- async discordSendMessage(channelId, content) {
112
+ async discordSendMessage(channelId, payload) {
113
113
  return this.request('/discord/send', {
114
114
  method: 'POST',
115
- body: JSON.stringify({ channel_id: channelId, content }),
115
+ body: JSON.stringify({ channel_id: channelId, ...payload }),
116
116
  });
117
117
  }
118
118
  async listUntrackedResources() {
package/dist/index.js CHANGED
@@ -222,16 +222,8 @@ server.tool('generate_handoff', 'Generate a new context handoff for personal sco
222
222
  return { content: [{ type: 'text', text: `Could not generate handoff: ${reason}` }] };
223
223
  }
224
224
  const handoff = result;
225
- let text = `# ${handoff.title}\n\n${handoff.summary}\n`;
226
- if (handoff.project_name)
227
- text += `\n**Project:** ${handoff.project_name}\n`;
228
- text += `\n**Sources:** ${handoff.sources.join(', ')}\n`;
229
- if (handoff.key_changes?.length)
230
- text += `\n**Key changes:**\n${handoff.key_changes.map((c) => `- ${c}`).join('\n')}\n`;
231
- if (handoff.decisions.length)
232
- text += `\n**Decisions:**\n${handoff.decisions.map((d) => `- ${d}`).join('\n')}\n`;
233
- if (handoff.next_steps.length)
234
- text += `\n**Next steps:**\n${handoff.next_steps.map((s) => `- ${s}`).join('\n')}\n`;
225
+ // Server renders the canonical Markdown; fall back for pre-markdown servers.
226
+ const text = handoff.markdown ?? `# ${handoff.title}\n\n${handoff.summary}\n`;
235
227
  return { content: [{ type: 'text', text }] };
236
228
  }
237
229
  catch (err) {
@@ -326,12 +318,22 @@ server.tool('discord_list_channels', 'List text channels in your connected Disco
326
318
  }
327
319
  });
328
320
  // ── Tool: discord send message ──────────────────────────────────────
329
- server.tool('discord_send_message', 'Send a message to a Discord channel in your connected server.', {
330
- channel_id: z.string().describe('The Discord channel ID to send the message to'),
331
- content: z.string().describe('The message content to send'),
332
- }, async ({ channel_id, content }) => {
321
+ server.tool('discord_send_message', 'Send to a Discord channel in your connected server. Provide exactly one of: content (inline text); handoff_id or insight_id (sends that artifact, rendered to Markdown, as a .md file attachment); or artifact (last_handoff / last_correlation / last_onboarding / last_architecture, with project_id) to send the latest active artifact of that kind.', {
322
+ channel_id: z.string().describe('The Discord channel ID to send to'),
323
+ content: z.string().optional().describe('Inline message text. Mutually exclusive with handoff_id / insight_id / artifact'),
324
+ handoff_id: z.string().optional().describe('UUID of a handoff to render and attach as a .md file'),
325
+ insight_id: z.string().optional().describe('UUID of an insight to render and attach as a .md file'),
326
+ artifact: z
327
+ .enum(['last_handoff', 'last_correlation', 'last_onboarding', 'last_architecture'])
328
+ .optional()
329
+ .describe('Send the latest active artifact of this kind. Requires project_id'),
330
+ project_id: z.string().optional().describe('Project UUID. Required only when artifact is set'),
331
+ }, async ({ channel_id, content, handoff_id, insight_id, artifact, project_id, }) => {
332
+ if (!content && !handoff_id && !insight_id && !artifact) {
333
+ return { content: [{ type: 'text', text: 'Provide content, handoff_id, insight_id, or artifact.' }] };
334
+ }
333
335
  try {
334
- const result = await api.discordSendMessage(channel_id, content);
336
+ const result = await api.discordSendMessage(channel_id, { content, handoff_id, insight_id, artifact, project_id });
335
337
  return { content: [{ type: 'text', text: `Message sent (ID: ${result.message_id})` }] };
336
338
  }
337
339
  catch (err) {
@@ -359,7 +361,7 @@ server.tool('generate_correlation_insight', 'Generate a cross-source correlation
359
361
  return { content: [{ type: 'text', text: `Could not generate correlation insight: ${reason}` }] };
360
362
  }
361
363
  const insight = result;
362
- return { content: [{ type: 'text', text: `# ${insight.title}\n\n${insight.summary}` }] };
364
+ return { content: [{ type: 'text', text: insight.markdown ?? `# ${insight.title}\n\n${insight.summary}` }] };
363
365
  }
364
366
  catch (err) {
365
367
  return { content: [{ type: 'text', text: `Failed: ${err.message}` }] };
@@ -390,7 +392,7 @@ server.tool('generate_onboarding_brief', 'Generate an onboarding brief AI insigh
390
392
  return { content: [{ type: 'text', text: `Could not generate onboarding brief: ${reason}` }] };
391
393
  }
392
394
  const insight = result;
393
- return { content: [{ type: 'text', text: `# ${insight.title}\n\n${insight.summary}` }] };
395
+ return { content: [{ type: 'text', text: insight.markdown ?? `# ${insight.title}\n\n${insight.summary}` }] };
394
396
  }
395
397
  catch (err) {
396
398
  return { content: [{ type: 'text', text: `Failed: ${err.message}` }] };
@@ -419,7 +421,7 @@ server.tool('generate_architecture_insight', 'Generate an architecture insight A
419
421
  return { content: [{ type: 'text', text: `Could not generate architecture insight: ${reason}` }] };
420
422
  }
421
423
  const insight = result;
422
- return { content: [{ type: 'text', text: `# ${insight.title}\n\n${insight.summary}` }] };
424
+ return { content: [{ type: 'text', text: insight.markdown ?? `# ${insight.title}\n\n${insight.summary}` }] };
423
425
  }
424
426
  catch (err) {
425
427
  return { content: [{ type: 'text', text: `Failed: ${err.message}` }] };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flowrelay/mcp-server",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "Flow Relay MCP Server for Claude Desktop and Claude Code – handoffs, integrations, and context events via natural conversation.",
5
5
  "type": "module",
6
6
  "license": "MIT",