@flowrelay/mcp-server 0.8.0 → 0.9.0
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 +8 -8
- package/dist/api.d.ts +7 -6
- package/dist/api.js +1 -4
- package/dist/index.js +28 -23
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@ It connects to Flow Relay API v1 using an API key and supports:
|
|
|
9
9
|
## Package
|
|
10
10
|
|
|
11
11
|
- Name: @flowrelay/mcp-server
|
|
12
|
-
- Version: 0.
|
|
12
|
+
- Version: 0.9.0
|
|
13
13
|
|
|
14
14
|
## What Is Included
|
|
15
15
|
|
|
@@ -18,15 +18,15 @@ The server currently exposes these tools:
|
|
|
18
18
|
- get_workspace_context
|
|
19
19
|
- list_projects
|
|
20
20
|
- set_active_project
|
|
21
|
-
- list_handoffs (returns project-specific handoffs, or an aggregated view of all accessible project handoffs if no project is active)
|
|
22
|
-
- generate_handoff (processed asynchronously, and the server automatically polls until the job finishes. Requires active project or project_id.)
|
|
23
|
-
- generate_correlation_insight
|
|
24
|
-
- generate_onboarding_brief
|
|
25
|
-
- generate_architecture_insight
|
|
21
|
+
- list_handoffs (returns project-specific handoffs, or an aggregated view of all accessible project handoffs if no project is active; filter by status: active, archived, or all)
|
|
22
|
+
- generate_handoff (processed asynchronously, and the server automatically polls until the job finishes. Requires active project or project_id. Accepts per-source filters: projects, eventTypes, branches, priorities.)
|
|
23
|
+
- generate_correlation_insight (accepts per-source filters)
|
|
24
|
+
- generate_onboarding_brief (accepts per-source filters)
|
|
25
|
+
- generate_architecture_insight (accepts per-source filters)
|
|
26
26
|
- list_insights
|
|
27
27
|
- list_integrations
|
|
28
28
|
- list_events
|
|
29
|
-
- list_untracked_resources (lists event-producing resources not scoped to any project
|
|
29
|
+
- list_untracked_resources (lists event-producing resources not scoped to any project – useful for discovering untracked activity)
|
|
30
30
|
- discord_list_channels
|
|
31
31
|
- discord_send_message
|
|
32
32
|
|
|
@@ -55,7 +55,7 @@ Add this to your claude_desktop_config.json:
|
|
|
55
55
|
"args": ["-y", "@flowrelay/mcp-server"],
|
|
56
56
|
"env": {
|
|
57
57
|
"FLOWRELAY_API_KEY": "fr_your_api_key_here",
|
|
58
|
-
"FLOWRELAY_PROJECT_ID": "
|
|
58
|
+
"FLOWRELAY_PROJECT_ID": "your_project_id"
|
|
59
59
|
}
|
|
60
60
|
}
|
|
61
61
|
}
|
package/dist/api.d.ts
CHANGED
|
@@ -84,9 +84,13 @@ export interface UntrackedResource {
|
|
|
84
84
|
resource_name: string;
|
|
85
85
|
resource_type: string;
|
|
86
86
|
}
|
|
87
|
+
export interface SourceFilter {
|
|
88
|
+
projects?: string[];
|
|
89
|
+
eventTypes?: string[];
|
|
90
|
+
branches?: string[];
|
|
91
|
+
priorities?: string[];
|
|
92
|
+
}
|
|
87
93
|
export type GenerateHandoffResponse = {
|
|
88
|
-
handoff: HandoffResult;
|
|
89
|
-
} | {
|
|
90
94
|
jobId: string;
|
|
91
95
|
status: AiJobStatus;
|
|
92
96
|
};
|
|
@@ -104,10 +108,7 @@ export declare class FlowRelayAPI {
|
|
|
104
108
|
listHandoffs(status?: string, limit?: number, projectId?: string | null): Promise<{
|
|
105
109
|
handoffs: HandoffResult[];
|
|
106
110
|
}>;
|
|
107
|
-
generateHandoff(sources: string[] | undefined, filters: Record<string,
|
|
108
|
-
projects?: string[];
|
|
109
|
-
eventTypes?: string[];
|
|
110
|
-
}> | undefined, projectId: string): Promise<GenerateHandoffResponse>;
|
|
111
|
+
generateHandoff(sources: string[] | undefined, filters: Record<string, SourceFilter> | undefined, projectId: string): Promise<GenerateHandoffResponse>;
|
|
111
112
|
getJob(jobId: string): Promise<{
|
|
112
113
|
job: AiJob;
|
|
113
114
|
result: HandoffResult | InsightResult | null;
|
package/dist/api.js
CHANGED
|
@@ -49,13 +49,10 @@ export class FlowRelayAPI {
|
|
|
49
49
|
if (filters && Object.keys(filters).length > 0)
|
|
50
50
|
body.filters = filters;
|
|
51
51
|
body.project_id = projectId;
|
|
52
|
-
|
|
52
|
+
return this.request('/handoffs', {
|
|
53
53
|
method: 'POST',
|
|
54
54
|
body: JSON.stringify(body),
|
|
55
55
|
});
|
|
56
|
-
// 202 → async job; 200 → inline handoff. Caller branches on the shape.
|
|
57
|
-
void status;
|
|
58
|
-
return data;
|
|
59
56
|
}
|
|
60
57
|
async getJob(jobId) {
|
|
61
58
|
return (await this.requestRaw(`/jobs/${jobId}`)).body;
|
package/dist/index.js
CHANGED
|
@@ -21,6 +21,15 @@ const SOURCES = [
|
|
|
21
21
|
'datadog',
|
|
22
22
|
];
|
|
23
23
|
const SourceEnum = z.enum(SOURCES);
|
|
24
|
+
const SourceFilterSchema = z.object({
|
|
25
|
+
projects: z.array(z.string()).optional().describe('Filter to specific repos, channels, projects, or teams'),
|
|
26
|
+
eventTypes: z.array(z.string()).optional().describe('Filter to specific event types (e.g. "push", "issue_created")'),
|
|
27
|
+
branches: z.array(z.string()).optional().describe('Git branch names (e.g. "main", "develop") – versioning sources only'),
|
|
28
|
+
priorities: z.array(z.string()).optional().describe('Priority levels (e.g. "high", "urgent") – Jira, Linear, Sentry only'),
|
|
29
|
+
});
|
|
30
|
+
const FiltersSchema = z.record(z.string(), SourceFilterSchema)
|
|
31
|
+
.optional()
|
|
32
|
+
.describe('Per-source advanced filters, AND-combined across dimensions');
|
|
24
33
|
function normalizeProjectId(value) {
|
|
25
34
|
if (typeof value !== 'string')
|
|
26
35
|
return null;
|
|
@@ -50,7 +59,7 @@ const api = new FlowRelayAPI(apiKey, process.env.FLOWRELAY_BASE_URL);
|
|
|
50
59
|
let activeProjectId = normalizeProjectId(process.env.FLOWRELAY_PROJECT_ID);
|
|
51
60
|
const server = new McpServer({
|
|
52
61
|
name: 'flowrelay',
|
|
53
|
-
version: '0.
|
|
62
|
+
version: '0.9.0',
|
|
54
63
|
});
|
|
55
64
|
async function getTenantContext() {
|
|
56
65
|
const context = await api.listProjects();
|
|
@@ -160,7 +169,7 @@ server.tool('set_active_project', 'Set or clear the active project context used
|
|
|
160
169
|
});
|
|
161
170
|
// ── Tool: list handoffs ──────────────────────────────────────────────
|
|
162
171
|
server.tool('list_handoffs', 'List Flow Relay handoffs in the current tenant scope (all accessible projects or selected project).', {
|
|
163
|
-
status: z.enum(['active', 'archived', '
|
|
172
|
+
status: z.enum(['active', 'archived', 'all']).default('active').describe('Filter by status ("all" returns every status)'),
|
|
164
173
|
limit: z.number().min(1).max(50).default(10).describe('Max number of handoffs to return'),
|
|
165
174
|
project_id: z.string().optional().describe('Optional project scope override for this call.'),
|
|
166
175
|
}, async ({ status, limit, project_id }) => {
|
|
@@ -201,28 +210,18 @@ server.tool('generate_handoff', 'Generate a new context handoff for personal sco
|
|
|
201
210
|
sources: z.array(SourceEnum)
|
|
202
211
|
.optional()
|
|
203
212
|
.describe('Specific sources to include (omit for all connected sources)'),
|
|
204
|
-
filters:
|
|
205
|
-
projects: z.array(z.string()).optional().describe('Filter to specific repos, channels, projects, or teams'),
|
|
206
|
-
eventTypes: z.array(z.string()).optional().describe('Filter to specific event types (e.g. "push", "issue_created")'),
|
|
207
|
-
})).optional().describe('Per-source advanced filters'),
|
|
213
|
+
filters: FiltersSchema,
|
|
208
214
|
project_id: z.string().optional().describe('Optional project scope override for this call.'),
|
|
209
215
|
}, async ({ sources, filters, project_id }) => {
|
|
210
216
|
try {
|
|
211
217
|
const resolved = await requireProject(project_id);
|
|
212
|
-
const
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
if (job.status === 'failed' || !result) {
|
|
218
|
-
const reason = job.error ?? 'unknown error';
|
|
219
|
-
return { content: [{ type: 'text', text: `Could not generate handoff: ${reason}` }] };
|
|
220
|
-
}
|
|
221
|
-
handoff = result;
|
|
222
|
-
}
|
|
223
|
-
else {
|
|
224
|
-
handoff = res.handoff;
|
|
218
|
+
const { jobId } = await api.generateHandoff(sources, filters, resolved.projectId);
|
|
219
|
+
const { job, result } = await api.waitForJob(jobId);
|
|
220
|
+
if (job.status === 'failed' || !result) {
|
|
221
|
+
const reason = job.error ?? 'unknown error';
|
|
222
|
+
return { content: [{ type: 'text', text: `Could not generate handoff: ${reason}` }] };
|
|
225
223
|
}
|
|
224
|
+
const handoff = result;
|
|
226
225
|
let text = `# ${handoff.title}\n\n${handoff.summary}\n`;
|
|
227
226
|
if (handoff.project_name)
|
|
228
227
|
text += `\n**Project:** ${handoff.project_name}\n`;
|
|
@@ -343,12 +342,14 @@ server.tool('discord_send_message', 'Send a message to a Discord channel in your
|
|
|
343
342
|
server.tool('generate_correlation_insight', 'Generate a cross-source correlation AI insight for the specified project scope.', {
|
|
344
343
|
project_id: z.string().describe('The project ID scope to generate the correlation insight for.'),
|
|
345
344
|
sources: z.array(SourceEnum).optional().describe('Filter events to specific sources (e.g. "github", "slack")'),
|
|
345
|
+
filters: FiltersSchema,
|
|
346
346
|
lookback_hours: z.number().optional().describe('Number of hours of activity to analyze'),
|
|
347
347
|
max_events: z.number().optional().describe('Maximum number of events to process'),
|
|
348
|
-
}, async ({ project_id, sources, lookback_hours, max_events }) => {
|
|
348
|
+
}, async ({ project_id, sources, filters, lookback_hours, max_events }) => {
|
|
349
349
|
try {
|
|
350
350
|
const res = await api.generateInsight(project_id, 'correlation', {
|
|
351
351
|
sources,
|
|
352
|
+
filters,
|
|
352
353
|
lookbackHours: lookback_hours,
|
|
353
354
|
maxEvents: max_events,
|
|
354
355
|
});
|
|
@@ -368,14 +369,16 @@ server.tool('generate_correlation_insight', 'Generate a cross-source correlation
|
|
|
368
369
|
server.tool('generate_onboarding_brief', 'Generate an onboarding brief AI insight for the specified project scope.', {
|
|
369
370
|
project_id: z.string().describe('The project ID scope to generate the onboarding brief for.'),
|
|
370
371
|
sources: z.array(SourceEnum).optional().describe('Filter events to specific sources'),
|
|
372
|
+
filters: FiltersSchema,
|
|
371
373
|
new_member_role: z.string().optional().describe('Expected role/focus of the new team member'),
|
|
372
374
|
focus_area: z.string().optional().describe('Specific repository or feature area they will work on'),
|
|
373
375
|
lookback_days: z.number().optional().describe('Number of days of history to review'),
|
|
374
376
|
max_events: z.number().optional().describe('Maximum events to process'),
|
|
375
|
-
}, async ({ project_id, sources, new_member_role, focus_area, lookback_days, max_events }) => {
|
|
377
|
+
}, async ({ project_id, sources, filters, new_member_role, focus_area, lookback_days, max_events }) => {
|
|
376
378
|
try {
|
|
377
379
|
const res = await api.generateInsight(project_id, 'onboarding', {
|
|
378
380
|
sources,
|
|
381
|
+
filters,
|
|
379
382
|
newMemberRole: new_member_role,
|
|
380
383
|
focusArea: focus_area,
|
|
381
384
|
lookbackDays: lookback_days,
|
|
@@ -397,13 +400,15 @@ server.tool('generate_onboarding_brief', 'Generate an onboarding brief AI insigh
|
|
|
397
400
|
server.tool('generate_architecture_insight', 'Generate an architecture insight AI insight for the specified project scope.', {
|
|
398
401
|
project_id: z.string().describe('The project ID scope to generate the architecture insight for.'),
|
|
399
402
|
sources: z.array(SourceEnum).optional().describe('Filter events to specific sources'),
|
|
403
|
+
filters: FiltersSchema,
|
|
400
404
|
focus_question: z.string().optional().describe('Specific architectural question or component to focus on'),
|
|
401
405
|
lookback_days: z.number().optional().describe('Number of days of history to review'),
|
|
402
406
|
max_events: z.number().optional().describe('Maximum events to process'),
|
|
403
|
-
}, async ({ project_id, sources, focus_question, lookback_days, max_events }) => {
|
|
407
|
+
}, async ({ project_id, sources, filters, focus_question, lookback_days, max_events }) => {
|
|
404
408
|
try {
|
|
405
409
|
const res = await api.generateInsight(project_id, 'architecture', {
|
|
406
410
|
sources,
|
|
411
|
+
filters,
|
|
407
412
|
focusQuestion: focus_question,
|
|
408
413
|
lookbackDays: lookback_days,
|
|
409
414
|
maxEvents: max_events,
|
|
@@ -424,7 +429,7 @@ server.tool('generate_architecture_insight', 'Generate an architecture insight A
|
|
|
424
429
|
server.tool('list_insights', 'List project AI insights for a selected project scope.', {
|
|
425
430
|
project_id: z.string().describe('The project ID context to list insights for.'),
|
|
426
431
|
kind: z.enum(['onboarding_brief', 'cross_source_correlation', 'architecture_insight']).optional().describe('Filter by insight kind'),
|
|
427
|
-
status: z.enum(['active', 'archived', '
|
|
432
|
+
status: z.enum(['active', 'archived', 'all']).default('active').describe('Filter by status ("all" returns every status)'),
|
|
428
433
|
limit: z.number().min(1).max(50).default(20).describe('Max number of insights to return'),
|
|
429
434
|
}, async ({ project_id, kind, status, limit }) => {
|
|
430
435
|
try {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flowrelay/mcp-server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
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",
|