@mushi-mushi/mcp 0.9.0 → 0.10.1
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 +10 -2
- package/dist/index.js +324 -9
- package/package.json +17 -17
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# mushi-mcp
|
|
1
|
+
# @mushi-mushi/mcp
|
|
2
2
|
|
|
3
3
|
> **Sentry sees what code throws. Mushi sees what users feel — and closes the loop with AI.**
|
|
4
4
|
|
|
@@ -20,7 +20,7 @@ That command reads `~/.mushirc`, writes `.cursor/mcp.json` with the `mushi` serv
|
|
|
20
20
|
|
|
21
21
|
> **What this is, and what it isn't**
|
|
22
22
|
>
|
|
23
|
-
> - **This package** (
|
|
23
|
+
> - **This package** (`@mushi-mushi/mcp`) is the MCP **server** — runs locally next to your editor, talks to the Mushi API, and presents bug reports as MCP tools/resources to your coding agent. Always install it by its scoped name (`npx -y @mushi-mushi/mcp@latest`) — the bare `mushi-mcp` name was never published to npm.
|
|
24
24
|
> - **`@mushi-mushi/agents`** ships the MCP **client adapter** — used by the autofix orchestrator when your project's `autofix_agent = 'mcp'`. See `packages/agents/src/adapters/mcp.ts`.
|
|
25
25
|
> - The `generic_mcp` adapter shipped before V5.3 was a misnomer (it spoke plain REST). It is now `RestFixWorkerAgent`; the old export is kept as a deprecated alias for one more minor.
|
|
26
26
|
|
|
@@ -130,6 +130,8 @@ The endpoint accepts JSON-RPC 2.0 over POST (returns `application/json` or `text
|
|
|
130
130
|
| `get_fix_timeline` | Ordered timeline of a fix attempt (dispatched → started → branch → commit → PR → CI → completed/failed) |
|
|
131
131
|
| `get_blast_radius` | Graph traversal showing other components a bug group touches |
|
|
132
132
|
| `get_knowledge_graph` | Traverse the knowledge graph from a seed component or page |
|
|
133
|
+
| `setup_check` | The 4 **dispatch-readiness** checks (GitHub repo, codebase indexed, Anthropic key, autofix enabled) — run before `dispatch_fix` |
|
|
134
|
+
| `ingest_setup_check` | The 4 **required ingest** checks (project, active API key, SDK heartbeat, first report) + `last_sdk_seen_at` diagnostics — run after wiring env vars to confirm the SDK is reporting |
|
|
133
135
|
|
|
134
136
|
### Write / agentic
|
|
135
137
|
|
|
@@ -366,3 +368,9 @@ In Cursor chat, type `/` — you should see the Mushi Mushi slash-prompts (`/sum
|
|
|
366
368
|
## License
|
|
367
369
|
|
|
368
370
|
MIT
|
|
371
|
+
|
|
372
|
+
|
|
373
|
+
<!-- mushi-readme-stats-footer -->
|
|
374
|
+
---
|
|
375
|
+
|
|
376
|
+
<sub>Monorepo scale (June 2026): 43 edge functions · 234 SQL migrations · 13 outbound plugins · 11 inbound adapters. Canonical counts: <a href="https://github.com/kensaurus/mushi-mushi/blob/master/docs/stats.md">docs/stats.md</a> · <code>pnpm docs-stats</code></sub>
|
package/dist/index.js
CHANGED
|
@@ -133,10 +133,18 @@ var TOOL_CATALOG = [
|
|
|
133
133
|
{
|
|
134
134
|
name: "setup_check",
|
|
135
135
|
title: "Dispatch preflight check",
|
|
136
|
-
description: "Run the 4 dispatch-readiness checks for a project and return their pass/fail status (GitHub repo connected, codebase indexed, Anthropic BYOK key present, autofix enabled). Also returns the target repo URL when GitHub is connected. Use this before calling dispatch_fix to understand why a dispatch might fail
|
|
136
|
+
description: "Run the 4 **dispatch-readiness** checks for a project and return their pass/fail status (GitHub repo connected, codebase indexed, Anthropic BYOK key present, autofix enabled). Also returns the target repo URL when GitHub is connected. Use this before calling dispatch_fix to understand why a dispatch might fail. For SDK ingest health (API key \u2192 heartbeat \u2192 first report), call ingest_setup_check instead.",
|
|
137
137
|
scope: "mcp:read",
|
|
138
138
|
hints: { readOnly: true, idempotent: true, openWorld: true },
|
|
139
|
-
useCase: "Is this project ready to auto-fix bugs? What is blocking
|
|
139
|
+
useCase: "Is this project ready to auto-fix bugs? What is blocking dispatch?"
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
name: "ingest_setup_check",
|
|
143
|
+
title: "Ingest setup check",
|
|
144
|
+
description: "Run the 4 **required ingest** checks for the project tied to this API key: project exists, active API key, SDK heartbeat (or real report), and at least one ingested report. Returns per-step pass/fail plus last_sdk_seen_at and endpoint host diagnostics. Use after wiring env vars or pasting the SDK snippet to confirm the banner will work.",
|
|
145
|
+
scope: "mcp:read",
|
|
146
|
+
hints: { readOnly: true, idempotent: true, openWorld: true },
|
|
147
|
+
useCase: "Is the SDK installed and ingesting reports? Why is my banner still missing?"
|
|
140
148
|
},
|
|
141
149
|
// --- Write / agentic ----------------------------------------------------
|
|
142
150
|
{
|
|
@@ -217,6 +225,88 @@ var TOOL_CATALOG = [
|
|
|
217
225
|
useCase: "Set up this repo for the Mushi evolution loop in one step."
|
|
218
226
|
}
|
|
219
227
|
];
|
|
228
|
+
var TDD_TOOL_CATALOG = [
|
|
229
|
+
{
|
|
230
|
+
name: "map_user_stories",
|
|
231
|
+
title: "Map user stories from live app",
|
|
232
|
+
description: 'Crawl a live application URL with Firecrawl/Browserbase and ask Claude to draft an inventory.yaml with pages and user stories. Creates a story_map_run row for progress tracking, then writes an inventory_proposals row (source=live_crawl). Optionally dispatches a Cursor Cloud agent to refine the draft and open a PR. Returns { runId, status: "pending" } immediately \u2014 poll get_map_run_status for progress.',
|
|
233
|
+
scope: "mcp:write",
|
|
234
|
+
hints: { readOnly: false, destructive: false, idempotent: false, openWorld: true },
|
|
235
|
+
useCase: "Map the user stories in my live app automatically without writing YAML by hand."
|
|
236
|
+
},
|
|
237
|
+
{
|
|
238
|
+
name: "get_map_run_status",
|
|
239
|
+
title: "Story map run status",
|
|
240
|
+
description: "Get the status and results of a story_map_run (pending \u2192 running \u2192 completed/failed). Returns pages_crawled, proposal_id (once done), and cursor_pr_url if Cursor Cloud refined the draft.",
|
|
241
|
+
scope: "mcp:read",
|
|
242
|
+
hints: { readOnly: true, idempotent: true, openWorld: true },
|
|
243
|
+
useCase: "Is my story mapping crawl done yet?"
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
name: "generate_tdd_from_story",
|
|
247
|
+
title: "Generate TDD test from user story",
|
|
248
|
+
description: "Given a user story id (from the accepted inventory), ask Claude to write a full Playwright TypeScript test. Inserts a qa_stories row (source=test_gen_from_story) with approval_status driven by automation_mode. Optionally opens a draft GitHub PR. Returns { qaStoryId, prUrl, approvalStatus, needsHumanReview }.",
|
|
249
|
+
scope: "mcp:write",
|
|
250
|
+
hints: { readOnly: false, destructive: false, idempotent: false, openWorld: true },
|
|
251
|
+
useCase: "Generate a Playwright test for this user story."
|
|
252
|
+
},
|
|
253
|
+
{
|
|
254
|
+
name: "improve_qa_story",
|
|
255
|
+
title: "PDCA auto-improve a failing QA story",
|
|
256
|
+
description: "Trigger the PDCA improver for a specific project. Finds recently failed qa_story_runs and uses Claude to write improved test scripts. New tests are created with source=pdca and approval gated by the original story's automation_mode.",
|
|
257
|
+
scope: "mcp:write",
|
|
258
|
+
hints: { readOnly: false, destructive: false, idempotent: false, openWorld: true },
|
|
259
|
+
useCase: "Fix my failing QA tests automatically."
|
|
260
|
+
},
|
|
261
|
+
{
|
|
262
|
+
name: "run_qa_story",
|
|
263
|
+
title: "Trigger a manual QA story run",
|
|
264
|
+
description: 'Queue a manual run for an enabled + approved qa_story. Returns the run id immediately; poll qa_story_runs or use get_report_detail for progress. Equivalent to "Run now" in the console.',
|
|
265
|
+
scope: "mcp:write",
|
|
266
|
+
hints: { readOnly: false, destructive: false, idempotent: false, openWorld: true },
|
|
267
|
+
useCase: "Run the login flow test right now."
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
name: "list_byok_keys",
|
|
271
|
+
title: "List BYOK API key pool",
|
|
272
|
+
description: "List all BYOK API keys for the project, grouped by provider. Shows label, priority, status, and cooldown. Never returns the raw key value \u2014 only metadata. Use this to see which keys are active or exhausted.",
|
|
273
|
+
scope: "mcp:read",
|
|
274
|
+
hints: { readOnly: true, idempotent: true, openWorld: true },
|
|
275
|
+
useCase: "Which API keys are active and which are rate-limited?"
|
|
276
|
+
},
|
|
277
|
+
{
|
|
278
|
+
name: "add_byok_key",
|
|
279
|
+
title: "Add a BYOK API key",
|
|
280
|
+
description: "Add a new API key to the project's BYOK pool for a given provider (anthropic, openai, firecrawl, browserbase, cursor). Specify label and priority for ordering. The key is stored encrypted in Supabase Vault.",
|
|
281
|
+
scope: "mcp:write",
|
|
282
|
+
hints: { readOnly: false, destructive: false, idempotent: false, openWorld: true },
|
|
283
|
+
useCase: "Add a backup Anthropic key to the pool."
|
|
284
|
+
},
|
|
285
|
+
{
|
|
286
|
+
name: "list_pending_review_stories",
|
|
287
|
+
title: "List QA stories pending review",
|
|
288
|
+
description: "Get the queue of TDD tests that were auto-generated and are waiting for human approval before they run in the QA schedule.",
|
|
289
|
+
scope: "mcp:read",
|
|
290
|
+
hints: { readOnly: true, idempotent: true, openWorld: true },
|
|
291
|
+
useCase: "What TDD tests need my approval today?"
|
|
292
|
+
},
|
|
293
|
+
{
|
|
294
|
+
name: "approve_qa_story",
|
|
295
|
+
title: "Approve or reject a pending QA story",
|
|
296
|
+
description: "Approve or reject a qa_story that is in pending_review. Approved stories are enabled in the QA schedule immediately.",
|
|
297
|
+
scope: "mcp:write",
|
|
298
|
+
hints: { readOnly: false, destructive: false, idempotent: true, openWorld: true },
|
|
299
|
+
useCase: "Approve this auto-generated test."
|
|
300
|
+
},
|
|
301
|
+
{
|
|
302
|
+
name: "reply_to_reporter",
|
|
303
|
+
title: "Reply to a reporter",
|
|
304
|
+
description: "Send a visible message to the end-user who filed a bug report. The reply appears in the in-app Mushi widget as an admin comment and creates an unread notification badge so the reporter sees it immediately. Use this to answer questions, request reproduction steps, or confirm a fix \u2014 without leaving the Cursor IDE.",
|
|
305
|
+
scope: "mcp:write",
|
|
306
|
+
hints: { readOnly: false, destructive: false, idempotent: false, openWorld: false },
|
|
307
|
+
useCase: "Reply to a reporter asking for more info or confirming a fix."
|
|
308
|
+
}
|
|
309
|
+
];
|
|
220
310
|
|
|
221
311
|
// src/server.ts
|
|
222
312
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
@@ -292,8 +382,9 @@ function createMushiServer(config) {
|
|
|
292
382
|
name: "mushi-mushi",
|
|
293
383
|
version
|
|
294
384
|
});
|
|
295
|
-
|
|
296
|
-
|
|
385
|
+
const ALL_TOOL_CATALOG = [...TOOL_CATALOG, ...TDD_TOOL_CATALOG];
|
|
386
|
+
function annotationsFor(name, catalog = ALL_TOOL_CATALOG) {
|
|
387
|
+
const spec = catalog.find((t) => t.name === name);
|
|
297
388
|
if (!spec) throw new Error(`[mushi-mcp] tool "${name}" is missing from TOOL_CATALOG`);
|
|
298
389
|
const a = {
|
|
299
390
|
title: spec.title,
|
|
@@ -304,13 +395,13 @@ function createMushiServer(config) {
|
|
|
304
395
|
if (spec.hints.openWorld !== void 0) a.openWorldHint = spec.hints.openWorld;
|
|
305
396
|
return a;
|
|
306
397
|
}
|
|
307
|
-
function descOf(name) {
|
|
308
|
-
const spec =
|
|
398
|
+
function descOf(name, catalog = ALL_TOOL_CATALOG) {
|
|
399
|
+
const spec = catalog.find((t) => t.name === name);
|
|
309
400
|
if (!spec) throw new Error(`[mushi-mcp] tool "${name}" is missing from TOOL_CATALOG`);
|
|
310
401
|
return spec.description;
|
|
311
402
|
}
|
|
312
|
-
function titleOf(name) {
|
|
313
|
-
const spec =
|
|
403
|
+
function titleOf(name, catalog = ALL_TOOL_CATALOG) {
|
|
404
|
+
const spec = catalog.find((t) => t.name === name);
|
|
314
405
|
if (!spec) throw new Error(`[mushi-mcp] tool "${name}" is missing from TOOL_CATALOG`);
|
|
315
406
|
return spec.title;
|
|
316
407
|
}
|
|
@@ -621,6 +712,36 @@ function createMushiServer(config) {
|
|
|
621
712
|
});
|
|
622
713
|
}
|
|
623
714
|
);
|
|
715
|
+
server.registerTool(
|
|
716
|
+
"ingest_setup_check",
|
|
717
|
+
{
|
|
718
|
+
title: titleOf("ingest_setup_check"),
|
|
719
|
+
description: descOf("ingest_setup_check"),
|
|
720
|
+
annotations: annotationsFor("ingest_setup_check"),
|
|
721
|
+
inputSchema: {}
|
|
722
|
+
},
|
|
723
|
+
async () => {
|
|
724
|
+
const data = await apiCall("/v1/sync/ingest-setup");
|
|
725
|
+
const failed = data.steps.filter((s) => s.required && !s.complete);
|
|
726
|
+
const summary = data.ready ? `Ingest setup complete (${data.required_complete}/${data.required_total}) for ${data.project_name}.` : `Ingest incomplete (${data.required_complete}/${data.required_total}) \u2014 still need: ${failed.map((s) => s.label).join(", ")}.`;
|
|
727
|
+
return jsonText({
|
|
728
|
+
ready: data.ready,
|
|
729
|
+
projectId: data.project_id,
|
|
730
|
+
projectName: data.project_name,
|
|
731
|
+
requiredComplete: data.required_complete,
|
|
732
|
+
requiredTotal: data.required_total,
|
|
733
|
+
steps: data.steps.map((s) => ({
|
|
734
|
+
id: s.id,
|
|
735
|
+
label: s.label,
|
|
736
|
+
passed: s.complete,
|
|
737
|
+
required: s.required,
|
|
738
|
+
hint: s.hint
|
|
739
|
+
})),
|
|
740
|
+
diagnostic: data.diagnostic ?? null,
|
|
741
|
+
summary
|
|
742
|
+
});
|
|
743
|
+
}
|
|
744
|
+
);
|
|
624
745
|
server.registerTool(
|
|
625
746
|
"submit_fix_result",
|
|
626
747
|
{
|
|
@@ -1004,10 +1125,204 @@ Prefer items that are bottlenecks or critical severity. Skip filler.`
|
|
|
1004
1125
|
}]
|
|
1005
1126
|
})
|
|
1006
1127
|
);
|
|
1128
|
+
server.registerTool(
|
|
1129
|
+
"map_user_stories",
|
|
1130
|
+
{
|
|
1131
|
+
title: titleOf("map_user_stories", TDD_TOOL_CATALOG),
|
|
1132
|
+
description: descOf("map_user_stories", TDD_TOOL_CATALOG),
|
|
1133
|
+
annotations: annotationsFor("map_user_stories", TDD_TOOL_CATALOG),
|
|
1134
|
+
inputSchema: {
|
|
1135
|
+
projectId: z.string().describe("Project id to map stories for"),
|
|
1136
|
+
baseUrl: z.string().url().describe("Live app URL to crawl"),
|
|
1137
|
+
maxPages: z.number().int().min(1).max(50).optional().describe("Max pages to crawl (default 20)"),
|
|
1138
|
+
provider: z.enum(["firecrawl", "browserbase"]).optional().describe("Crawl provider (default: firecrawl)"),
|
|
1139
|
+
cursorCloudRefine: z.boolean().optional().describe("Dispatch Cursor Cloud agent to refine and open a PR")
|
|
1140
|
+
}
|
|
1141
|
+
},
|
|
1142
|
+
async ({ projectId: projectId2, baseUrl, maxPages, provider, cursorCloudRefine }) => {
|
|
1143
|
+
if (!projectId2) throw new MushiApiError(400, "MISSING_PROJECT", "projectId is required");
|
|
1144
|
+
const data = await apiCall(
|
|
1145
|
+
`/v1/admin/inventory/${projectId2}/map-from-live`,
|
|
1146
|
+
{ method: "POST", body: JSON.stringify({ base_url: baseUrl, max_pages: maxPages, provider, cursor_cloud_refine: cursorCloudRefine }) }
|
|
1147
|
+
);
|
|
1148
|
+
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
1149
|
+
}
|
|
1150
|
+
);
|
|
1151
|
+
server.registerTool(
|
|
1152
|
+
"get_map_run_status",
|
|
1153
|
+
{
|
|
1154
|
+
title: titleOf("get_map_run_status", TDD_TOOL_CATALOG),
|
|
1155
|
+
description: descOf("get_map_run_status", TDD_TOOL_CATALOG),
|
|
1156
|
+
annotations: annotationsFor("get_map_run_status", TDD_TOOL_CATALOG),
|
|
1157
|
+
inputSchema: {
|
|
1158
|
+
projectId: z.string().describe("Project id")
|
|
1159
|
+
}
|
|
1160
|
+
},
|
|
1161
|
+
async ({ projectId: projectId2 }) => {
|
|
1162
|
+
const data = await apiCall(`/v1/admin/inventory/${projectId2}/map-runs`);
|
|
1163
|
+
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
1164
|
+
}
|
|
1165
|
+
);
|
|
1166
|
+
server.registerTool(
|
|
1167
|
+
"generate_tdd_from_story",
|
|
1168
|
+
{
|
|
1169
|
+
title: titleOf("generate_tdd_from_story", TDD_TOOL_CATALOG),
|
|
1170
|
+
description: descOf("generate_tdd_from_story", TDD_TOOL_CATALOG),
|
|
1171
|
+
annotations: annotationsFor("generate_tdd_from_story", TDD_TOOL_CATALOG),
|
|
1172
|
+
inputSchema: {
|
|
1173
|
+
projectId: z.string().describe("Project id"),
|
|
1174
|
+
storyNodeId: z.string().describe("User story id slug from the accepted inventory"),
|
|
1175
|
+
automationMode: z.enum(["auto", "review", "approve"]).optional().describe("Gate mode for the generated test (default: review)"),
|
|
1176
|
+
baseUrl: z.string().url().optional().describe("Override the app base URL"),
|
|
1177
|
+
openPr: z.boolean().optional().describe("Open a draft GitHub PR (default: true)")
|
|
1178
|
+
}
|
|
1179
|
+
},
|
|
1180
|
+
async ({ projectId: projectId2, storyNodeId, automationMode, baseUrl, openPr }) => {
|
|
1181
|
+
if (!projectId2) throw new MushiApiError(400, "MISSING_PROJECT", "projectId is required");
|
|
1182
|
+
const data = await apiCall(
|
|
1183
|
+
`/v1/admin/inventory/${projectId2}/stories/${storyNodeId}/generate-test`,
|
|
1184
|
+
{ method: "POST", body: JSON.stringify({ automation_mode: automationMode, base_url: baseUrl, open_pr: openPr }) }
|
|
1185
|
+
);
|
|
1186
|
+
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
1187
|
+
}
|
|
1188
|
+
);
|
|
1189
|
+
server.registerTool(
|
|
1190
|
+
"improve_qa_story",
|
|
1191
|
+
{
|
|
1192
|
+
title: titleOf("improve_qa_story", TDD_TOOL_CATALOG),
|
|
1193
|
+
description: descOf("improve_qa_story", TDD_TOOL_CATALOG),
|
|
1194
|
+
annotations: annotationsFor("improve_qa_story", TDD_TOOL_CATALOG),
|
|
1195
|
+
inputSchema: {
|
|
1196
|
+
projectId: z.string().optional().describe("Project id (omit to run across all projects)")
|
|
1197
|
+
}
|
|
1198
|
+
},
|
|
1199
|
+
async ({ projectId: projectId2 }) => {
|
|
1200
|
+
const data = await apiCall(
|
|
1201
|
+
"/v1/admin/pdca/improve-qa-stories",
|
|
1202
|
+
{ method: "POST", body: JSON.stringify({ project_id: projectId2 }) }
|
|
1203
|
+
);
|
|
1204
|
+
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
1205
|
+
}
|
|
1206
|
+
);
|
|
1207
|
+
server.registerTool(
|
|
1208
|
+
"run_qa_story",
|
|
1209
|
+
{
|
|
1210
|
+
title: titleOf("run_qa_story", TDD_TOOL_CATALOG),
|
|
1211
|
+
description: descOf("run_qa_story", TDD_TOOL_CATALOG),
|
|
1212
|
+
annotations: annotationsFor("run_qa_story", TDD_TOOL_CATALOG),
|
|
1213
|
+
inputSchema: {
|
|
1214
|
+
projectId: z.string().describe("Project id"),
|
|
1215
|
+
qaStoryId: z.string().describe("qa_story id to run")
|
|
1216
|
+
}
|
|
1217
|
+
},
|
|
1218
|
+
async ({ projectId: projectId2, qaStoryId }) => {
|
|
1219
|
+
const data = await apiCall(
|
|
1220
|
+
`/v1/admin/projects/${projectId2}/qa-stories/${qaStoryId}/run`,
|
|
1221
|
+
{ method: "POST" }
|
|
1222
|
+
);
|
|
1223
|
+
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
1224
|
+
}
|
|
1225
|
+
);
|
|
1226
|
+
server.registerTool(
|
|
1227
|
+
"list_byok_keys",
|
|
1228
|
+
{
|
|
1229
|
+
title: titleOf("list_byok_keys", TDD_TOOL_CATALOG),
|
|
1230
|
+
description: descOf("list_byok_keys", TDD_TOOL_CATALOG),
|
|
1231
|
+
annotations: annotationsFor("list_byok_keys", TDD_TOOL_CATALOG),
|
|
1232
|
+
inputSchema: {
|
|
1233
|
+
projectId: z.string().describe("Project id")
|
|
1234
|
+
}
|
|
1235
|
+
},
|
|
1236
|
+
async ({ projectId: projectId2 }) => {
|
|
1237
|
+
const data = await apiCall(`/v1/admin/byok/keys?project_id=${encodeURIComponent(projectId2)}`);
|
|
1238
|
+
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
1239
|
+
}
|
|
1240
|
+
);
|
|
1241
|
+
server.registerTool(
|
|
1242
|
+
"add_byok_key",
|
|
1243
|
+
{
|
|
1244
|
+
title: titleOf("add_byok_key", TDD_TOOL_CATALOG),
|
|
1245
|
+
description: descOf("add_byok_key", TDD_TOOL_CATALOG),
|
|
1246
|
+
annotations: annotationsFor("add_byok_key", TDD_TOOL_CATALOG),
|
|
1247
|
+
inputSchema: {
|
|
1248
|
+
projectId: z.string().describe("Project id"),
|
|
1249
|
+
provider: z.enum(["anthropic", "openai", "firecrawl", "browserbase", "cursor"]).describe("Provider slug"),
|
|
1250
|
+
key: z.string().min(10).describe("The API key value to add"),
|
|
1251
|
+
label: z.string().optional().describe("Human-readable label for this key"),
|
|
1252
|
+
priority: z.number().int().min(1).max(999).optional().describe("Priority for ordering (lower = higher priority)")
|
|
1253
|
+
}
|
|
1254
|
+
},
|
|
1255
|
+
async ({ projectId: projectId2, provider, key, label, priority }) => {
|
|
1256
|
+
const data = await apiCall(
|
|
1257
|
+
"/v1/admin/byok/keys",
|
|
1258
|
+
{ method: "POST", body: JSON.stringify({ project_id: projectId2, provider_slug: provider, key, label, priority }) }
|
|
1259
|
+
);
|
|
1260
|
+
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
1261
|
+
}
|
|
1262
|
+
);
|
|
1263
|
+
server.registerTool(
|
|
1264
|
+
"list_pending_review_stories",
|
|
1265
|
+
{
|
|
1266
|
+
title: titleOf("list_pending_review_stories", TDD_TOOL_CATALOG),
|
|
1267
|
+
description: descOf("list_pending_review_stories", TDD_TOOL_CATALOG),
|
|
1268
|
+
annotations: annotationsFor("list_pending_review_stories", TDD_TOOL_CATALOG),
|
|
1269
|
+
inputSchema: {
|
|
1270
|
+
projectId: z.string().describe("Project id")
|
|
1271
|
+
}
|
|
1272
|
+
},
|
|
1273
|
+
async ({ projectId: projectId2 }) => {
|
|
1274
|
+
const data = await apiCall(`/v1/admin/inventory/${projectId2}/stories/pending-review`);
|
|
1275
|
+
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
1276
|
+
}
|
|
1277
|
+
);
|
|
1278
|
+
server.registerTool(
|
|
1279
|
+
"approve_qa_story",
|
|
1280
|
+
{
|
|
1281
|
+
title: titleOf("approve_qa_story", TDD_TOOL_CATALOG),
|
|
1282
|
+
description: descOf("approve_qa_story", TDD_TOOL_CATALOG),
|
|
1283
|
+
annotations: annotationsFor("approve_qa_story", TDD_TOOL_CATALOG),
|
|
1284
|
+
inputSchema: {
|
|
1285
|
+
projectId: z.string().describe("Project id"),
|
|
1286
|
+
qaStoryId: z.string().describe("QA story id to approve or reject"),
|
|
1287
|
+
status: z.enum(["approved", "rejected"]).describe("New approval status")
|
|
1288
|
+
}
|
|
1289
|
+
},
|
|
1290
|
+
async ({ projectId: projectId2, qaStoryId, status }) => {
|
|
1291
|
+
const data = await apiCall(
|
|
1292
|
+
`/v1/admin/inventory/${projectId2}/stories/${qaStoryId}/approval`,
|
|
1293
|
+
{ method: "PATCH", body: JSON.stringify({ status }) }
|
|
1294
|
+
);
|
|
1295
|
+
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
1296
|
+
}
|
|
1297
|
+
);
|
|
1298
|
+
server.registerTool(
|
|
1299
|
+
"reply_to_reporter",
|
|
1300
|
+
{
|
|
1301
|
+
title: titleOf("reply_to_reporter", TDD_TOOL_CATALOG),
|
|
1302
|
+
description: descOf("reply_to_reporter", TDD_TOOL_CATALOG),
|
|
1303
|
+
annotations: annotationsFor("reply_to_reporter", TDD_TOOL_CATALOG),
|
|
1304
|
+
inputSchema: {
|
|
1305
|
+
reportId: z.string().describe("Report id to reply to"),
|
|
1306
|
+
message: z.string().min(1).max(1e4).describe("Message text to send to the reporter"),
|
|
1307
|
+
authorName: z.string().optional().describe('Display name for the admin sender (default: "Mushi Admin")')
|
|
1308
|
+
}
|
|
1309
|
+
},
|
|
1310
|
+
async ({ reportId, message, authorName }) => {
|
|
1311
|
+
const data = await apiCall(
|
|
1312
|
+
`/v1/sync/reports/${reportId}/reply`,
|
|
1313
|
+
{
|
|
1314
|
+
method: "POST",
|
|
1315
|
+
body: JSON.stringify({ message, author_name: authorName })
|
|
1316
|
+
}
|
|
1317
|
+
);
|
|
1318
|
+
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
1319
|
+
}
|
|
1320
|
+
);
|
|
1007
1321
|
const grantedScopes = config.scopes;
|
|
1008
1322
|
if (grantedScopes !== void 0) {
|
|
1009
1323
|
const toolRegistry = server._registeredTools;
|
|
1010
|
-
|
|
1324
|
+
const allSpecs = [...TOOL_CATALOG, ...TDD_TOOL_CATALOG];
|
|
1325
|
+
for (const spec of allSpecs) {
|
|
1011
1326
|
if (!grantedScopes.includes(spec.scope)) {
|
|
1012
1327
|
toolRegistry[spec.name]?.remove();
|
|
1013
1328
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mushi-mushi/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.1",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "MCP server exposing Mushi Mushi reports to coding agents",
|
|
6
6
|
"type": "module",
|
|
@@ -23,30 +23,18 @@
|
|
|
23
23
|
"CODE_OF_CONDUCT.md",
|
|
24
24
|
"SECURITY.md"
|
|
25
25
|
],
|
|
26
|
-
"scripts": {
|
|
27
|
-
"build": "tsup",
|
|
28
|
-
"clean:types": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\"",
|
|
29
|
-
"dev": "tsup --watch",
|
|
30
|
-
"lint": "eslint src/",
|
|
31
|
-
"test": "vitest run",
|
|
32
|
-
"test:smoke": "node scripts/smoke-stdio.mjs",
|
|
33
|
-
"test:localhost": "node scripts/localhost-e2e.mjs",
|
|
34
|
-
"demo": "node scripts/demo-terminal-config.mjs",
|
|
35
|
-
"inspector": "npx --yes @modelcontextprotocol/inspector@latest node ./dist/index.js",
|
|
36
|
-
"typecheck": "tsc --noEmit"
|
|
37
|
-
},
|
|
38
26
|
"dependencies": {
|
|
39
27
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
40
|
-
"@mushi-mushi/core": "
|
|
28
|
+
"@mushi-mushi/core": "^1.7.5",
|
|
41
29
|
"zod": "^4.4.2"
|
|
42
30
|
},
|
|
43
31
|
"devDependencies": {
|
|
44
|
-
"@mushi-mushi/eslint-config": "workspace:*",
|
|
45
32
|
"@types/node": "^22.19.17",
|
|
46
33
|
"eslint": "^10.3.0",
|
|
47
34
|
"tsup": "^8.5.1",
|
|
48
35
|
"typescript": "^6.0.3",
|
|
49
|
-
"vitest": "^4.1.5"
|
|
36
|
+
"vitest": "^4.1.5",
|
|
37
|
+
"@mushi-mushi/eslint-config": "0.0.0"
|
|
50
38
|
},
|
|
51
39
|
"repository": {
|
|
52
40
|
"type": "git",
|
|
@@ -85,5 +73,17 @@
|
|
|
85
73
|
],
|
|
86
74
|
"engines": {
|
|
87
75
|
"node": ">=20"
|
|
76
|
+
},
|
|
77
|
+
"scripts": {
|
|
78
|
+
"build": "tsup",
|
|
79
|
+
"clean:types": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\"",
|
|
80
|
+
"dev": "tsup --watch",
|
|
81
|
+
"lint": "eslint src/",
|
|
82
|
+
"test": "vitest run",
|
|
83
|
+
"test:smoke": "node scripts/smoke-stdio.mjs",
|
|
84
|
+
"test:localhost": "node scripts/localhost-e2e.mjs",
|
|
85
|
+
"demo": "node scripts/demo-terminal-config.mjs",
|
|
86
|
+
"inspector": "npx --yes @modelcontextprotocol/inspector@latest node ./dist/index.js",
|
|
87
|
+
"typecheck": "tsc --noEmit"
|
|
88
88
|
}
|
|
89
|
-
}
|
|
89
|
+
}
|