@mushi-mushi/mcp 0.11.0 → 0.13.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/dist/index.js +225 -6
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -183,14 +183,35 @@ var TOOL_CATALOG = [
|
|
|
183
183
|
{
|
|
184
184
|
name: "transition_status",
|
|
185
185
|
title: "Move report between states",
|
|
186
|
-
description: "Move a report between workflow states (new \u2192 classified \u2192 grouped \u2192 fixing \u2192 fixed \u2192 dismissed). Enforces the same transition rules as the admin UI.",
|
|
186
|
+
description: "Move a report between workflow states (new \u2192 classified \u2192 grouped \u2192 fixing \u2192 fixed \u2192 verified \u2192 reopened \u2192 dismissed). Enforces the same transition rules as the admin UI.",
|
|
187
187
|
scope: "mcp:write",
|
|
188
|
-
// Transitioning to `dismissed` is destructive by intent — it removes the
|
|
189
|
-
// report from triage queues. Flag the whole tool as destructive so the
|
|
190
|
-
// client prompts the user on every call.
|
|
191
188
|
hints: { readOnly: false, destructive: true, idempotent: true, openWorld: true },
|
|
192
189
|
useCase: "Dismiss this duplicate / mark it fixed."
|
|
193
190
|
},
|
|
191
|
+
{
|
|
192
|
+
name: "merge_fix",
|
|
193
|
+
title: "Merge fix PR",
|
|
194
|
+
description: "Squash-merge a fix attempt PR and mark the linked report fixed.",
|
|
195
|
+
scope: "mcp:write",
|
|
196
|
+
hints: { readOnly: false, destructive: true, idempotent: true, openWorld: true },
|
|
197
|
+
useCase: "Merge the draft PR and notify the reporter."
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
name: "refresh_ci",
|
|
201
|
+
title: "Refresh fix CI status",
|
|
202
|
+
description: "Pull the latest GitHub check-run status for a fix attempt.",
|
|
203
|
+
scope: "mcp:read",
|
|
204
|
+
hints: { readOnly: true, idempotent: true, openWorld: true },
|
|
205
|
+
useCase: "Check whether CI is green before merging."
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
name: "reopen_report",
|
|
209
|
+
title: "Reopen report (operator)",
|
|
210
|
+
description: "Move a report to reopened for regression triage.",
|
|
211
|
+
scope: "mcp:write",
|
|
212
|
+
hints: { readOnly: false, destructive: false, idempotent: true, openWorld: true },
|
|
213
|
+
useCase: "Reopen a regression the reporter flagged as not fixed."
|
|
214
|
+
},
|
|
194
215
|
// --- Rewards (P3) -------------------------------------------------------
|
|
195
216
|
{
|
|
196
217
|
name: "list_top_contributors",
|
|
@@ -330,6 +351,47 @@ var TDD_TOOL_CATALOG = [
|
|
|
330
351
|
hints: { readOnly: false, destructive: false, idempotent: false, openWorld: true },
|
|
331
352
|
useCase: 'Did "Add to Slack" work? Send a test ping to confirm.'
|
|
332
353
|
},
|
|
354
|
+
// ── Skill Pipeline tools ─────────────────────────────────────────────────────
|
|
355
|
+
{
|
|
356
|
+
name: "list_skills",
|
|
357
|
+
title: "List agent skills",
|
|
358
|
+
description: "List the agent skills available in the catalog, optionally filtered by category. Returns slug, title, description, category, and chain_slugs for each skill. Use before start_skill_pipeline to find the right skill slug for a given type of work.",
|
|
359
|
+
scope: "mcp:read",
|
|
360
|
+
hints: { readOnly: true, idempotent: true, openWorld: true },
|
|
361
|
+
useCase: "What skills are available for debugging production errors?"
|
|
362
|
+
},
|
|
363
|
+
{
|
|
364
|
+
name: "get_skill",
|
|
365
|
+
title: "Get skill detail",
|
|
366
|
+
description: "Fetch the full detail for a single agent skill by slug, including the complete SKILL.md body and resolved chain. Use before executing a step to understand what the skill expects you to do.",
|
|
367
|
+
scope: "mcp:read",
|
|
368
|
+
hints: { readOnly: true, idempotent: true, openWorld: true },
|
|
369
|
+
useCase: "What does workflow-fix-and-ship actually instruct me to do?"
|
|
370
|
+
},
|
|
371
|
+
{
|
|
372
|
+
name: "start_skill_pipeline",
|
|
373
|
+
title: "Start a skill pipeline",
|
|
374
|
+
description: "Start a new skill pipeline run for a report. Pass root_skill_slug and optionally report_id. Returns run_id, context_packet (full instructions + report context), and step list. Read the context_packet \u2014 it contains skill instructions plus full report context (repro steps, root cause, RAG files). After executing each step, call checkin_pipeline_step. The PM watching the console sees progress live.",
|
|
375
|
+
scope: "mcp:write",
|
|
376
|
+
hints: { readOnly: false, destructive: false, idempotent: false, openWorld: true },
|
|
377
|
+
useCase: "Start the workflow-fix-and-ship pipeline for this bug report."
|
|
378
|
+
},
|
|
379
|
+
{
|
|
380
|
+
name: "get_pipeline_run",
|
|
381
|
+
title: "Get pipeline run detail",
|
|
382
|
+
description: "Fetch the full detail for a skill pipeline run: status, context_packet, and all step statuses. Call to retrieve the context_packet after a pipeline was started from the console or another agent.",
|
|
383
|
+
scope: "mcp:read",
|
|
384
|
+
hints: { readOnly: true, idempotent: true, openWorld: true },
|
|
385
|
+
useCase: "Give me the context packet and step status for this pipeline run."
|
|
386
|
+
},
|
|
387
|
+
{
|
|
388
|
+
name: "checkin_pipeline_step",
|
|
389
|
+
title: "Check in a pipeline step",
|
|
390
|
+
description: "Report the completion status of a pipeline step (passed, failed, running, or skipped). Optionally include notes, a PR URL, or the Cursor agentId. Updates the live React Flow canvas in the Mushi console so PMs see real-time progress.",
|
|
391
|
+
scope: "mcp:write",
|
|
392
|
+
hints: { readOnly: false, destructive: false, idempotent: true, openWorld: true },
|
|
393
|
+
useCase: "I just opened the PR \u2014 mark step 2 as passed and link the PR."
|
|
394
|
+
},
|
|
333
395
|
// ── Full-Stack Audit tools (Phase 5) ────────────────────────────────────────
|
|
334
396
|
{
|
|
335
397
|
name: "run_fullstack_audit",
|
|
@@ -795,12 +857,15 @@ function createMushiServer(config) {
|
|
|
795
857
|
prUrl: z.string().optional().describe("GitHub PR URL"),
|
|
796
858
|
filesChanged: z.array(z.string()).describe("Files modified"),
|
|
797
859
|
linesChanged: z.number().describe("Total lines changed"),
|
|
798
|
-
summary: z.string().describe("Fix summary")
|
|
860
|
+
summary: z.string().describe("Fix summary"),
|
|
861
|
+
idempotencyKey: z.string().uuid().optional().describe("Optional UUID \u2014 resend the same key to safely retry without creating duplicate fix rows")
|
|
799
862
|
}
|
|
800
863
|
},
|
|
801
864
|
async (args) => {
|
|
865
|
+
const idemKey = args.idempotencyKey ?? crypto.randomUUID();
|
|
802
866
|
const created = await apiCall("/v1/admin/fixes", {
|
|
803
867
|
method: "POST",
|
|
868
|
+
headers: { "Idempotency-Key": idemKey },
|
|
804
869
|
body: JSON.stringify({ reportId: args.reportId, agent: "mcp" })
|
|
805
870
|
});
|
|
806
871
|
await apiCall(`/v1/admin/fixes/${created.fixId}`, {
|
|
@@ -906,6 +971,59 @@ function createMushiServer(config) {
|
|
|
906
971
|
return jsonText(data);
|
|
907
972
|
}
|
|
908
973
|
);
|
|
974
|
+
server.registerTool(
|
|
975
|
+
"merge_fix",
|
|
976
|
+
{
|
|
977
|
+
title: "Merge fix PR",
|
|
978
|
+
description: "Squash-merge a fix attempt PR and mark the linked report fixed.",
|
|
979
|
+
annotations: annotationsFor("merge_fix"),
|
|
980
|
+
inputSchema: {
|
|
981
|
+
fixId: z.string().describe("Fix attempt UUID"),
|
|
982
|
+
mergeMethod: z.enum(["squash", "merge", "rebase"]).optional().describe("GitHub merge method")
|
|
983
|
+
}
|
|
984
|
+
},
|
|
985
|
+
async (args) => {
|
|
986
|
+
const data = await apiCall(`/v1/admin/fixes/${args.fixId}/merge`, {
|
|
987
|
+
method: "POST",
|
|
988
|
+
body: JSON.stringify({ mergeMethod: args.mergeMethod ?? "squash" })
|
|
989
|
+
});
|
|
990
|
+
return jsonText(data);
|
|
991
|
+
}
|
|
992
|
+
);
|
|
993
|
+
server.registerTool(
|
|
994
|
+
"refresh_ci",
|
|
995
|
+
{
|
|
996
|
+
title: "Refresh fix CI status",
|
|
997
|
+
description: "Pull the latest GitHub check-run status for a fix attempt.",
|
|
998
|
+
annotations: annotationsFor("refresh_ci"),
|
|
999
|
+
inputSchema: {
|
|
1000
|
+
fixId: z.string().describe("Fix attempt UUID")
|
|
1001
|
+
}
|
|
1002
|
+
},
|
|
1003
|
+
async (args) => {
|
|
1004
|
+
const data = await apiCall(`/v1/admin/fixes/${args.fixId}/refresh-ci`, { method: "POST" });
|
|
1005
|
+
return jsonText(data);
|
|
1006
|
+
}
|
|
1007
|
+
);
|
|
1008
|
+
server.registerTool(
|
|
1009
|
+
"reopen_report",
|
|
1010
|
+
{
|
|
1011
|
+
title: "Reopen report (operator)",
|
|
1012
|
+
description: "Operator alias to move a report back to new for regression triage.",
|
|
1013
|
+
annotations: annotationsFor("reopen_report"),
|
|
1014
|
+
inputSchema: {
|
|
1015
|
+
reportId: z.string().describe("Report UUID"),
|
|
1016
|
+
note: z.string().optional().describe("Triage note")
|
|
1017
|
+
}
|
|
1018
|
+
},
|
|
1019
|
+
async (args) => {
|
|
1020
|
+
const data = await apiCall(`/v1/sync/reports/${args.reportId}`, {
|
|
1021
|
+
method: "PATCH",
|
|
1022
|
+
body: JSON.stringify({ status: "reopened", note: args.note })
|
|
1023
|
+
});
|
|
1024
|
+
return jsonText(data);
|
|
1025
|
+
}
|
|
1026
|
+
);
|
|
909
1027
|
server.registerTool(
|
|
910
1028
|
"transition_status",
|
|
911
1029
|
{
|
|
@@ -914,7 +1032,7 @@ function createMushiServer(config) {
|
|
|
914
1032
|
annotations: annotationsFor("transition_status"),
|
|
915
1033
|
inputSchema: {
|
|
916
1034
|
reportId: z.string().describe("Report UUID"),
|
|
917
|
-
status: z.enum(["pending", "classified", "grouped", "fixing", "fixed", "dismissed"]).describe("Target status"),
|
|
1035
|
+
status: z.enum(["pending", "classified", "grouped", "fixing", "fixed", "resolved", "verified", "reopened", "dismissed"]).describe("Target status"),
|
|
918
1036
|
reason: z.string().optional().describe("Reason for the transition (audit trail)")
|
|
919
1037
|
}
|
|
920
1038
|
},
|
|
@@ -1468,6 +1586,107 @@ Prefer items that are bottlenecks or critical severity. Skip filler.`
|
|
|
1468
1586
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
1469
1587
|
}
|
|
1470
1588
|
);
|
|
1589
|
+
server.registerTool(
|
|
1590
|
+
"list_skills",
|
|
1591
|
+
{
|
|
1592
|
+
title: titleOf("list_skills"),
|
|
1593
|
+
description: descOf("list_skills"),
|
|
1594
|
+
annotations: annotationsFor("list_skills"),
|
|
1595
|
+
inputSchema: {
|
|
1596
|
+
category: z.string().optional().describe("Filter by category: workflow, debug, test, audit, enhance, \u2026"),
|
|
1597
|
+
search: z.string().optional().describe("Free-text search across slug, title, description"),
|
|
1598
|
+
page: z.number().optional().describe("Page number (default 1)"),
|
|
1599
|
+
limit: z.number().optional().describe("Max results per page (default 200, max 200)")
|
|
1600
|
+
}
|
|
1601
|
+
},
|
|
1602
|
+
async (args) => {
|
|
1603
|
+
const qs = new URLSearchParams();
|
|
1604
|
+
if (args.category) qs.set("category", args.category);
|
|
1605
|
+
if (args.search) qs.set("q", args.search);
|
|
1606
|
+
if (args.page) qs.set("page", String(args.page));
|
|
1607
|
+
qs.set("limit", String(Math.min(args.limit ?? 200, 200)));
|
|
1608
|
+
const data = await apiCall(`/v1/admin/skills?${qs}`);
|
|
1609
|
+
const skills = Array.isArray(data) ? data : [];
|
|
1610
|
+
return jsonText({ skills, count: skills.length });
|
|
1611
|
+
}
|
|
1612
|
+
);
|
|
1613
|
+
server.registerTool(
|
|
1614
|
+
"get_skill",
|
|
1615
|
+
{
|
|
1616
|
+
title: titleOf("get_skill"),
|
|
1617
|
+
description: descOf("get_skill"),
|
|
1618
|
+
annotations: annotationsFor("get_skill"),
|
|
1619
|
+
inputSchema: {
|
|
1620
|
+
slug: z.string().describe('Skill slug, e.g. "workflow-fix-and-ship"')
|
|
1621
|
+
}
|
|
1622
|
+
},
|
|
1623
|
+
async (args) => {
|
|
1624
|
+
const data = await apiCall(`/v1/admin/skills/${args.slug}`);
|
|
1625
|
+
return jsonText(data);
|
|
1626
|
+
}
|
|
1627
|
+
);
|
|
1628
|
+
server.registerTool(
|
|
1629
|
+
"start_skill_pipeline",
|
|
1630
|
+
{
|
|
1631
|
+
title: titleOf("start_skill_pipeline"),
|
|
1632
|
+
description: descOf("start_skill_pipeline"),
|
|
1633
|
+
annotations: annotationsFor("start_skill_pipeline"),
|
|
1634
|
+
inputSchema: {
|
|
1635
|
+
root_skill_slug: z.string().describe('Root skill slug to run, e.g. "workflow-fix-and-ship"'),
|
|
1636
|
+
report_id: z.string().optional().describe("Report UUID to attach the pipeline to"),
|
|
1637
|
+
mode: z.enum(["handoff", "cloud"]).optional().describe("handoff (default): get context packet for local agent. cloud: auto-dispatch via Cursor Cloud."),
|
|
1638
|
+
project_id: z.string().optional().describe("Project UUID. Falls back to the configured project.")
|
|
1639
|
+
}
|
|
1640
|
+
},
|
|
1641
|
+
async (args) => {
|
|
1642
|
+
const resolvedProjectId = args.project_id ?? projectId;
|
|
1643
|
+
if (!resolvedProjectId) return jsonText({ error: "No project_id provided or configured." });
|
|
1644
|
+
const data = await apiCall(
|
|
1645
|
+
"/v1/admin/skills/pipelines",
|
|
1646
|
+
{ method: "POST", body: JSON.stringify({ ...args, project_id: resolvedProjectId }) }
|
|
1647
|
+
);
|
|
1648
|
+
return jsonText(data);
|
|
1649
|
+
}
|
|
1650
|
+
);
|
|
1651
|
+
server.registerTool(
|
|
1652
|
+
"get_pipeline_run",
|
|
1653
|
+
{
|
|
1654
|
+
title: titleOf("get_pipeline_run"),
|
|
1655
|
+
description: descOf("get_pipeline_run"),
|
|
1656
|
+
annotations: annotationsFor("get_pipeline_run"),
|
|
1657
|
+
inputSchema: {
|
|
1658
|
+
run_id: z.string().describe("Pipeline run UUID")
|
|
1659
|
+
}
|
|
1660
|
+
},
|
|
1661
|
+
async (args) => {
|
|
1662
|
+
const data = await apiCall(`/v1/admin/skills/pipelines/${args.run_id}`);
|
|
1663
|
+
return jsonText(data);
|
|
1664
|
+
}
|
|
1665
|
+
);
|
|
1666
|
+
server.registerTool(
|
|
1667
|
+
"checkin_pipeline_step",
|
|
1668
|
+
{
|
|
1669
|
+
title: titleOf("checkin_pipeline_step"),
|
|
1670
|
+
description: descOf("checkin_pipeline_step"),
|
|
1671
|
+
annotations: annotationsFor("checkin_pipeline_step"),
|
|
1672
|
+
inputSchema: {
|
|
1673
|
+
run_id: z.string().describe("Pipeline run UUID"),
|
|
1674
|
+
step_index: z.number().describe("Step index (0-based)"),
|
|
1675
|
+
status: z.enum(["running", "passed", "failed", "skipped"]).describe("Step status"),
|
|
1676
|
+
notes: z.string().optional().describe("Optional notes or output summary"),
|
|
1677
|
+
pr_url: z.string().optional().describe("PR URL opened during this step"),
|
|
1678
|
+
agent_ref: z.string().optional().describe("Cursor agentId or external agent reference")
|
|
1679
|
+
}
|
|
1680
|
+
},
|
|
1681
|
+
async (args) => {
|
|
1682
|
+
const { run_id, step_index, ...body } = args;
|
|
1683
|
+
await apiCall(
|
|
1684
|
+
`/v1/admin/skills/pipelines/${run_id}/steps/${step_index}/checkin`,
|
|
1685
|
+
{ method: "POST", body: JSON.stringify(body) }
|
|
1686
|
+
);
|
|
1687
|
+
return jsonText({ ok: true, message: `Step ${step_index} \u2192 ${args.status}` });
|
|
1688
|
+
}
|
|
1689
|
+
);
|
|
1471
1690
|
const grantedScopes = config.scopes;
|
|
1472
1691
|
if (grantedScopes !== void 0) {
|
|
1473
1692
|
const toolRegistry = server._registeredTools;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mushi-mushi/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "MCP server exposing Mushi Mushi reports to coding agents",
|
|
6
6
|
"type": "module",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
],
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
28
|
-
"@mushi-mushi/core": "^1.
|
|
28
|
+
"@mushi-mushi/core": "^1.11.0",
|
|
29
29
|
"zod": "^4.4.2"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|