@mushi-mushi/mcp 0.16.2 → 0.17.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 -4
- package/dist/branding.js +1 -1
- package/dist/catalog.js +1 -1
- package/dist/{chunk-MM3IMFM4.js → chunk-7UOTJ2LI.js} +21 -21
- package/dist/{chunk-EHB2BGWX.js → chunk-EANW5YTI.js} +3 -1
- package/dist/{chunk-ZX3XIJ7S.js → chunk-PW4AK6SV.js} +1 -0
- package/dist/clients.d.ts +115 -0
- package/dist/clients.js +271 -0
- package/dist/feature-groups.d.ts +1 -1
- package/dist/feature-groups.js +1 -1
- package/dist/index.js +104 -39
- package/dist/server.js +79 -25
- package/package.json +7 -2
package/README.md
CHANGED
|
@@ -2,7 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
> **Your AI wrote it. Mushi tells you why it broke.**
|
|
4
4
|
|
|
5
|
-
[
|
|
5
|
+
[](https://kensaur.us/mushi-mushi/docs/connect)
|
|
6
|
+
[](https://kensaur.us/mushi-mushi/docs/connect)
|
|
7
|
+
[](https://kensaur.us/mushi-mushi/docs/connect)
|
|
8
|
+
[](https://www.npmjs.com/package/@mushi-mushi/mcp)
|
|
9
|
+
|
|
10
|
+
[Model Context Protocol](https://spec.modelcontextprotocol.io/) server that wires Mushi into Cursor, Claude Code, and any MCP client. Pull fix context, review bugs, and dispatch fixes without a second LLM vendor key.
|
|
6
11
|
|
|
7
12
|
Wire it into Cursor, Claude Code, or any MCP client in one command:
|
|
8
13
|
|
|
@@ -259,7 +264,7 @@ Named templates the MCP client surfaces in its slash-menu. Each one bakes in the
|
|
|
259
264
|
|---|---|---|---|
|
|
260
265
|
| `MUSHI_API_KEY` | yes | — | Project API key with `mcp:read` or `mcp:write` scope. Mint one in the admin console → **Projects** (the one-time reveal card has a **Copy as `.env.local`** tab). |
|
|
261
266
|
| `MUSHI_PROJECT_ID` | yes | — | UUID from the admin console URL (`/projects/<uuid>/...`) or the reveal card. |
|
|
262
|
-
| `MUSHI_API_ENDPOINT` | no | `https://
|
|
267
|
+
| `MUSHI_API_ENDPOINT` | no | `https://dxptnwrhwsqckaftyymj.supabase.co/functions/v1/api` | Override only if you self-host. Localhost: `http://localhost:54321/functions/v1/api`. |
|
|
263
268
|
|
|
264
269
|
### Storing the key in `.env.local`
|
|
265
270
|
|
|
@@ -269,7 +274,7 @@ The MCP binary reads these three vars from `process.env` on spawn — that means
|
|
|
269
274
|
|
|
270
275
|
```bash
|
|
271
276
|
# Mushi MCP — drop into .env.local (gitignored). The MCP binary picks these up on spawn.
|
|
272
|
-
MUSHI_API_ENDPOINT=https://
|
|
277
|
+
MUSHI_API_ENDPOINT=https://dxptnwrhwsqckaftyymj.supabase.co/functions/v1/api
|
|
273
278
|
MUSHI_PROJECT_ID=<your-uuid>
|
|
274
279
|
MUSHI_API_KEY=mushi_live_…
|
|
275
280
|
```
|
|
@@ -459,6 +464,7 @@ Both are complementary — use Sentry for exception noise, Mushi for "the user s
|
|
|
459
464
|
|
|
460
465
|
## See also
|
|
461
466
|
|
|
467
|
+
- [GTM distribution runbook](../../docs/marketing/GTM-DISTRIBUTION.md) — MCP registry, Glama, Connect, cursor.directory, release checklist.
|
|
462
468
|
- [V5.3 whitepaper §2.10](../../MushiMushi_Whitepaper_V5.md) — the agentic fix architecture this server feeds into.
|
|
463
469
|
- [`@mushi-mushi/agents`](../agents/README.md) — orchestrator that consumes MCP-exposed fix workers.
|
|
464
470
|
|
|
@@ -470,4 +476,4 @@ MIT
|
|
|
470
476
|
<!-- mushi-readme-stats-footer -->
|
|
471
477
|
---
|
|
472
478
|
|
|
473
|
-
<sub>Monorepo scale (June 2026):
|
|
479
|
+
<sub>Monorepo scale (June 2026): 51 edge functions · 298 SQL migrations · 13 outbound plugins · 11 inbound adapters · 19 pipeline agents. 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/branding.js
CHANGED
package/dist/catalog.js
CHANGED
|
@@ -4,10 +4,10 @@ var TOOL_CATALOG = [
|
|
|
4
4
|
{
|
|
5
5
|
name: "get_recent_reports",
|
|
6
6
|
title: "Recent bug reports",
|
|
7
|
-
description: "List recent bug reports with optional filters (status / category / severity). Use this to
|
|
7
|
+
description: "List recent bug reports with optional filters (status / category / severity). Use this to see what is waiting in your reports inbox right now.",
|
|
8
8
|
scope: "mcp:read",
|
|
9
9
|
hints: { readOnly: true, idempotent: true, openWorld: true },
|
|
10
|
-
useCase: "What landed in my
|
|
10
|
+
useCase: "What landed in my bug queue today?"
|
|
11
11
|
},
|
|
12
12
|
{
|
|
13
13
|
name: "get_report_detail",
|
|
@@ -134,7 +134,7 @@ var TOOL_CATALOG = [
|
|
|
134
134
|
{
|
|
135
135
|
name: "setup_check",
|
|
136
136
|
title: "Dispatch preflight check",
|
|
137
|
-
description: "[Deprecated \u2014 use diagnose_setup with mode=dispatch or diagnose_connection.] Run the 4 **dispatch-readiness** checks for a project and return their pass/fail status (GitHub repo connected, codebase indexed, Anthropic
|
|
137
|
+
description: "[Deprecated \u2014 use diagnose_setup with mode=dispatch or diagnose_connection.] Run the 4 **dispatch-readiness** checks for a project and return their pass/fail status (GitHub repo connected, codebase indexed, your Anthropic API key present, autofix enabled). Also returns the target repo URL when GitHub is connected.",
|
|
138
138
|
scope: "mcp:read",
|
|
139
139
|
hints: { readOnly: true, idempotent: true, openWorld: true },
|
|
140
140
|
useCase: "Is this project ready to auto-fix bugs? What is blocking dispatch?"
|
|
@@ -184,7 +184,7 @@ var TOOL_CATALOG = [
|
|
|
184
184
|
{
|
|
185
185
|
name: "dispatch_fix",
|
|
186
186
|
title: "Dispatch Mushi fix agent",
|
|
187
|
-
description: '
|
|
187
|
+
description: 'Start the Mushi fix agent for a classified report. Set agent="cursor_cloud" to dispatch a Cursor Cloud Agent that opens a signed draft PR. Returns a fix_attempt id; poll get_fix_timeline for progress.',
|
|
188
188
|
scope: "mcp:write",
|
|
189
189
|
hints: { readOnly: false, destructive: false, idempotent: false, openWorld: true },
|
|
190
190
|
useCase: "Let the in-repo agent attempt this fix for me (or: dispatch a Cursor Cloud Agent)."
|
|
@@ -200,7 +200,7 @@ var TOOL_CATALOG = [
|
|
|
200
200
|
{
|
|
201
201
|
name: "test_gen_from_report",
|
|
202
202
|
title: "Generate Playwright test from report",
|
|
203
|
-
description: "
|
|
203
|
+
description: "Generate a Playwright spec from a classified report using your project LLM key and open a draft PR. Requires inventory_v2 + GitHub + LLM keys.",
|
|
204
204
|
scope: "mcp:write",
|
|
205
205
|
hints: { readOnly: false, destructive: false, idempotent: false, openWorld: true },
|
|
206
206
|
useCase: "Turn this regression report into an E2E test PR."
|
|
@@ -232,7 +232,7 @@ var TOOL_CATALOG = [
|
|
|
232
232
|
{
|
|
233
233
|
name: "reopen_report",
|
|
234
234
|
title: "Reopen report (operator)",
|
|
235
|
-
description: "Move a report to reopened for regression
|
|
235
|
+
description: "Move a report to reopened for regression review.",
|
|
236
236
|
scope: "mcp:write",
|
|
237
237
|
hints: { readOnly: false, destructive: false, idempotent: true, openWorld: true },
|
|
238
238
|
useCase: "Reopen a regression the reporter flagged as not fixed."
|
|
@@ -290,10 +290,10 @@ var TOOL_CATALOG = [
|
|
|
290
290
|
{
|
|
291
291
|
name: "get_project_context",
|
|
292
292
|
title: "Project context snapshot",
|
|
293
|
-
description: "Return a rich context snapshot for a project: name, repo URL, SDK heartbeat, ingest status, open report count, autofix readiness,
|
|
293
|
+
description: "Return a rich context snapshot for a project: name, repo URL, SDK heartbeat, ingest status, open report count, autofix readiness, your LLM key config, plan tier, and active integration health. Equivalent to a merged preflight + activation + settings read. Agents should call this at the start of a review session to orient themselves.",
|
|
294
294
|
scope: "mcp:read",
|
|
295
295
|
hints: { readOnly: true, idempotent: true, openWorld: true },
|
|
296
|
-
useCase: "Give me a status overview of this project before I start
|
|
296
|
+
useCase: "Give me a status overview of this project before I start reviewing bugs."
|
|
297
297
|
},
|
|
298
298
|
{
|
|
299
299
|
name: "get_pipeline_logs",
|
|
@@ -314,7 +314,7 @@ var TOOL_CATALOG = [
|
|
|
314
314
|
{
|
|
315
315
|
name: "triage_issue",
|
|
316
316
|
title: "Triage issue end-to-end",
|
|
317
|
-
description: 'Read-only
|
|
317
|
+
description: 'Read-only combined tool: merges report detail, evidence, similar bugs, fix context, blast radius, and recent pipeline logs for a report into a single structured review packet. Returns a prioritised list of recommended next actions (investigate, dispatch_fix, group_with, dismiss). Equivalent to a Sentry "Analyze with Seer" flow grounded in user-felt reports. Pass report_id to kick off review. Call this before dispatch_fix.',
|
|
318
318
|
scope: "mcp:read",
|
|
319
319
|
hints: { readOnly: true, idempotent: true, openWorld: true },
|
|
320
320
|
useCase: "Analyze this bug report end-to-end and tell me what to do."
|
|
@@ -341,8 +341,8 @@ var RESOURCE_CATALOG = [
|
|
|
341
341
|
{
|
|
342
342
|
name: "project_dashboard",
|
|
343
343
|
uri: "project://dashboard",
|
|
344
|
-
title: "
|
|
345
|
-
description: "
|
|
344
|
+
title: "Loop dashboard snapshot",
|
|
345
|
+
description: "Loop health snapshot \u2014 stage counts, bottleneck, recent activity (same payload the admin console polls).",
|
|
346
346
|
scope: "mcp:read"
|
|
347
347
|
},
|
|
348
348
|
{
|
|
@@ -363,14 +363,14 @@ var RESOURCE_CATALOG = [
|
|
|
363
363
|
name: "privacy_status",
|
|
364
364
|
uri: "privacy://status",
|
|
365
365
|
title: "Privacy posture",
|
|
366
|
-
description: "Returns the privacy posture for this project: storage region, LLM provider, whether
|
|
366
|
+
description: "Returns the privacy posture for this project: storage region, LLM provider, whether your own LLM key is configured, data retention window, and last audit timestamp. Agents should read this before dispatching a fix to confirm that client data stays within the expected boundary. Reads as project data does not leave the project's own LLM account when byok_configured=true.",
|
|
367
367
|
scope: "mcp:read"
|
|
368
368
|
},
|
|
369
369
|
{
|
|
370
370
|
name: "evolution_history",
|
|
371
371
|
uri: "evolution://history",
|
|
372
372
|
title: "Evolution history",
|
|
373
|
-
description: "Returns the project's last 30 days of judge scores, prompt promotions, fixed-bug count, and lesson inductions. Agents can read this to see whether the loop is converging (rising judge scores, falling recurrence) or stalling (flat scores, same bugs re-appearing). Use before
|
|
373
|
+
description: "Returns the project's last 30 days of judge scores, prompt promotions, fixed-bug count, and lesson inductions. Agents can read this to see whether the loop is converging (rising judge scores, falling recurrence) or stalling (flat scores, same bugs re-appearing). Use before review to understand which bug classes the loop has already learned to handle, and which still need human attention.",
|
|
374
374
|
scope: "mcp:read"
|
|
375
375
|
},
|
|
376
376
|
{
|
|
@@ -384,7 +384,7 @@ var RESOURCE_CATALOG = [
|
|
|
384
384
|
name: "project_integration_health",
|
|
385
385
|
uri: "project://integration-health",
|
|
386
386
|
title: "Integration health",
|
|
387
|
-
description: "Live health status of every configured
|
|
387
|
+
description: "Live health status of every configured integration channel (Sentry, GitHub, LangFuse, PagerDuty, \u2026). Check this before dispatching a fix to fail-fast on broken channels rather than burning LLM budget and discovering the failure mid-run.",
|
|
388
388
|
scope: "mcp:read"
|
|
389
389
|
},
|
|
390
390
|
{
|
|
@@ -448,8 +448,8 @@ var TDD_TOOL_CATALOG = [
|
|
|
448
448
|
},
|
|
449
449
|
{
|
|
450
450
|
name: "improve_qa_story",
|
|
451
|
-
title: "
|
|
452
|
-
description: "
|
|
451
|
+
title: "Auto-improve a failing QA story",
|
|
452
|
+
description: "Find recently failed qa_story_runs and use Claude to write improved test scripts. New tests are created with source=pdca and approval gated by the original story's automation_mode.",
|
|
453
453
|
scope: "mcp:write",
|
|
454
454
|
hints: { readOnly: false, destructive: false, idempotent: false, openWorld: true },
|
|
455
455
|
useCase: "Fix my failing QA tests automatically."
|
|
@@ -464,16 +464,16 @@ var TDD_TOOL_CATALOG = [
|
|
|
464
464
|
},
|
|
465
465
|
{
|
|
466
466
|
name: "list_byok_keys",
|
|
467
|
-
title: "List
|
|
468
|
-
description: "List all
|
|
467
|
+
title: "List your API key pool",
|
|
468
|
+
description: "List all project API keys, 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.",
|
|
469
469
|
scope: "mcp:read",
|
|
470
470
|
hints: { readOnly: true, idempotent: true, openWorld: true },
|
|
471
471
|
useCase: "Which API keys are active and which are rate-limited?"
|
|
472
472
|
},
|
|
473
473
|
{
|
|
474
474
|
name: "add_byok_key",
|
|
475
|
-
title: "Add
|
|
476
|
-
description: "Add a new API key to the project
|
|
475
|
+
title: "Add an API key",
|
|
476
|
+
description: "Add a new API key to the project pool for a given provider (anthropic, openai, firecrawl, browserbase, cursor). Specify label and priority for ordering. The key is stored encrypted in Supabase Vault.",
|
|
477
477
|
scope: "mcp:write",
|
|
478
478
|
hints: { readOnly: false, destructive: false, idempotent: false, openWorld: true },
|
|
479
479
|
useCase: "Add a backup Anthropic key to the pool."
|
|
@@ -622,7 +622,7 @@ var CODEBASE_TOOL_CATALOG = [
|
|
|
622
622
|
{
|
|
623
623
|
name: "ask_codebase",
|
|
624
624
|
title: "Ask about the indexed codebase",
|
|
625
|
-
description: "Ask a plain-English question about the connected repo. Grounds on pgvector retrieval over project_codebase_files and returns an answer with file:line citations. Requires codebase indexing enabled and
|
|
625
|
+
description: "Ask a plain-English question about the connected repo. Grounds on pgvector retrieval over project_codebase_files and returns an answer with file:line citations. Requires codebase indexing enabled and your Anthropic or OpenAI API key.",
|
|
626
626
|
scope: "mcp:write",
|
|
627
627
|
hints: { readOnly: false, destructive: false, idempotent: false, openWorld: true },
|
|
628
628
|
useCase: "How does authentication work in this repo?"
|
|
@@ -3,7 +3,9 @@ var MUSHI_WEBSITE_URL = "https://kensaur.us/mushi-mushi";
|
|
|
3
3
|
var MUSHI_ICON_PNG_URL = `${MUSHI_WEBSITE_URL}/integrations/mushi-mark-512.png`;
|
|
4
4
|
var MUSHI_ICON_SVG_URL = `${MUSHI_WEBSITE_URL}/favicon.svg`;
|
|
5
5
|
function mcpIconUrl(mcpBaseUrl) {
|
|
6
|
-
|
|
6
|
+
let end = mcpBaseUrl.length;
|
|
7
|
+
while (end > 0 && mcpBaseUrl.charCodeAt(end - 1) === 47) end--;
|
|
8
|
+
const base = mcpBaseUrl.slice(0, end);
|
|
7
9
|
return `${base}?icon=1`;
|
|
8
10
|
}
|
|
9
11
|
var MUSHI_SERVER_ICONS = [
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { FeatureGroup } from './feature-groups.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* FILE: packages/mcp/src/clients.ts
|
|
5
|
+
* PURPOSE: Shared, pure (React-free) client registry + install-config builders for
|
|
6
|
+
* every supported AI agent / IDE client.
|
|
7
|
+
*
|
|
8
|
+
* OVERVIEW:
|
|
9
|
+
* - Exports `MCP_CLIENTS: McpClientDef[]` — one entry per supported client.
|
|
10
|
+
* - Each entry carries metadata (id, label, method, transport) and a pure `build()`
|
|
11
|
+
* function that returns the install artifact (deeplink URL, JSON config, or CLI command).
|
|
12
|
+
* - Both the admin console ConnectStudio and the public docs /connect landing consume
|
|
13
|
+
* this module — the single source of truth so the two surfaces never drift.
|
|
14
|
+
*
|
|
15
|
+
* DEPENDENCIES:
|
|
16
|
+
* - feature-groups.ts (DEFAULT_FEATURE_GROUPS, featuresQueryString, appendFeaturesToUrl)
|
|
17
|
+
* - branding.ts (MUSHI_ICON_PNG_URL)
|
|
18
|
+
*
|
|
19
|
+
* USAGE:
|
|
20
|
+
* import { MCP_CLIENTS } from '@mushi-mushi/mcp/clients'
|
|
21
|
+
* const cursor = MCP_CLIENTS.find(c => c.id === 'cursor')!
|
|
22
|
+
* const result = cursor.build({ projectId, projectName, apiKey, endpoint, mcpHttpUrl })
|
|
23
|
+
* // result.kind === 'deeplink' → window.open(result.url)
|
|
24
|
+
*
|
|
25
|
+
* CLIENT INSTALL MATRIX (verified Jun 2026):
|
|
26
|
+
* - cursor / vscode / vscode-insiders — one-click deeplink
|
|
27
|
+
* - windsurf — copy-config to ~/.codeium/windsurf/mcp_config.json
|
|
28
|
+
* (uses `serverUrl`, NOT `url` — common footgun)
|
|
29
|
+
* - cline — copy-config to cline_mcp_settings.json
|
|
30
|
+
* - claude-code — CLI command: claude mcp add --transport http …
|
|
31
|
+
* - claude-desktop — copy-config to claude_desktop_config.json
|
|
32
|
+
* - zed — copy-config to ~/.config/zed/settings.json
|
|
33
|
+
* - any — hosted Streamable HTTP URL + Bearer key
|
|
34
|
+
*
|
|
35
|
+
* NOTES:
|
|
36
|
+
* - All builders are pure functions — no side-effects, no DOM, no React.
|
|
37
|
+
* - Cursor base64-encodes the config object without a `name` key in the payload.
|
|
38
|
+
* - VS Code includes `type:'stdio'` in the config and `name` as a separate query param.
|
|
39
|
+
* - Windsurf uses `serverUrl` for remote HTTP (NOT `url`) — encoding both would break it.
|
|
40
|
+
* - The `cliIde` field drives `mushi setup --ide <cliIde>` in the CLI lane.
|
|
41
|
+
*/
|
|
42
|
+
|
|
43
|
+
interface StdioConfig {
|
|
44
|
+
command: string;
|
|
45
|
+
args: string[];
|
|
46
|
+
env: Record<string, string>;
|
|
47
|
+
icon?: string;
|
|
48
|
+
}
|
|
49
|
+
interface HttpConfig {
|
|
50
|
+
type: 'http';
|
|
51
|
+
url: string;
|
|
52
|
+
headers: Record<string, string>;
|
|
53
|
+
icon?: string;
|
|
54
|
+
}
|
|
55
|
+
type McpClientId = 'cursor' | 'vscode' | 'vscode-insiders' | 'windsurf' | 'cline' | 'claude-code' | 'claude-desktop' | 'zed' | 'any';
|
|
56
|
+
/** The install method determines which UI treatment the console/docs page renders. */
|
|
57
|
+
type McpInstallMethod = 'deeplink' | 'config-json' | 'cli-command' | 'remote-url';
|
|
58
|
+
interface McpBuildInput {
|
|
59
|
+
/** Mushi project UUID — optional when building org-level configs. */
|
|
60
|
+
projectId?: string;
|
|
61
|
+
projectName: string;
|
|
62
|
+
apiKey: string;
|
|
63
|
+
/** Supabase edge function base URL (`MUSHI_API_ENDPOINT`). */
|
|
64
|
+
endpoint: string;
|
|
65
|
+
/** Hosted Streamable HTTP MCP URL (`RESOLVED_MCP_HTTP_URL`). */
|
|
66
|
+
mcpHttpUrl: string;
|
|
67
|
+
/**
|
|
68
|
+
* Restrict the exposed tool groups. Omitted → each client's existing default
|
|
69
|
+
* (the lean `DEFAULT_FEATURE_GROUPS` for stdio deeplinks; all tools for the
|
|
70
|
+
* config-json clients that historically set no `MUSHI_FEATURES`). The public
|
|
71
|
+
* keyless demo passes an explicit read-only subset here.
|
|
72
|
+
*/
|
|
73
|
+
features?: readonly FeatureGroup[];
|
|
74
|
+
/**
|
|
75
|
+
* Hosted HTTP transport only: append `?read_only=1` so the server hides and
|
|
76
|
+
* blocks write tools regardless of key scope. Used by the public demo on top
|
|
77
|
+
* of an `mcp:read` key for defence in depth. No-op for stdio configs.
|
|
78
|
+
*/
|
|
79
|
+
readOnly?: boolean;
|
|
80
|
+
}
|
|
81
|
+
type McpBuildResult = {
|
|
82
|
+
kind: 'deeplink';
|
|
83
|
+
url: string;
|
|
84
|
+
} | {
|
|
85
|
+
kind: 'config';
|
|
86
|
+
filePath: string;
|
|
87
|
+
json: string;
|
|
88
|
+
} | {
|
|
89
|
+
kind: 'command';
|
|
90
|
+
text: string;
|
|
91
|
+
} | {
|
|
92
|
+
kind: 'remote-url';
|
|
93
|
+
url: string;
|
|
94
|
+
headerSnippet: string;
|
|
95
|
+
};
|
|
96
|
+
interface McpClientDef {
|
|
97
|
+
id: McpClientId;
|
|
98
|
+
label: string;
|
|
99
|
+
/** Short description shown in the client picker. */
|
|
100
|
+
description: string;
|
|
101
|
+
method: McpInstallMethod;
|
|
102
|
+
transport: 'stdio' | 'http';
|
|
103
|
+
/** Drives `mushi setup --ide <cliIde>` in the CLI lane. */
|
|
104
|
+
cliIde?: 'cursor' | 'claude' | 'continue' | 'zed';
|
|
105
|
+
build(input: McpBuildInput): McpBuildResult;
|
|
106
|
+
}
|
|
107
|
+
/** Build a stable, unique, human-readable MCP server slug for a project. */
|
|
108
|
+
declare function projectServerName(projectId: string, projectName: string): string;
|
|
109
|
+
declare function buildStdioConfigObj(projectId: string | undefined, apiKey: string, apiEndpoint: string, features?: readonly string[]): StdioConfig;
|
|
110
|
+
declare function buildHttpConfigObj(projectId: string | undefined, apiKey: string, mcpHttpUrl: string, features?: readonly string[], readOnly?: boolean): HttpConfig;
|
|
111
|
+
declare const MCP_CLIENTS: McpClientDef[];
|
|
112
|
+
/** Look up a client by id — throws if not found. */
|
|
113
|
+
declare function getMcpClient(id: McpClientId): McpClientDef;
|
|
114
|
+
|
|
115
|
+
export { MCP_CLIENTS, type McpBuildInput, type McpBuildResult, type McpClientDef, type McpClientId, type McpInstallMethod, buildHttpConfigObj as buildRawHttpConfig, buildStdioConfigObj as buildRawStdioConfig, getMcpClient, projectServerName };
|
package/dist/clients.js
ADDED
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
import {
|
|
2
|
+
MUSHI_ICON_PNG_URL
|
|
3
|
+
} from "./chunk-EANW5YTI.js";
|
|
4
|
+
import {
|
|
5
|
+
DEFAULT_FEATURE_GROUPS,
|
|
6
|
+
appendFeaturesToUrl,
|
|
7
|
+
featuresQueryString
|
|
8
|
+
} from "./chunk-PW4AK6SV.js";
|
|
9
|
+
|
|
10
|
+
// src/clients.ts
|
|
11
|
+
function projectServerName(projectId, projectName) {
|
|
12
|
+
const nameSlug = projectName.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 22);
|
|
13
|
+
const idSuffix = projectId.replace(/-/g, "").slice(0, 6);
|
|
14
|
+
return `mushi-${nameSlug}-${idSuffix}`;
|
|
15
|
+
}
|
|
16
|
+
function buildStdioConfigObj(projectId, apiKey, apiEndpoint, features = DEFAULT_FEATURE_GROUPS) {
|
|
17
|
+
const env = {
|
|
18
|
+
MUSHI_API_ENDPOINT: apiEndpoint,
|
|
19
|
+
MUSHI_API_KEY: apiKey,
|
|
20
|
+
MUSHI_FEATURES: featuresQueryString(features)
|
|
21
|
+
};
|
|
22
|
+
if (projectId) env.MUSHI_PROJECT_ID = projectId;
|
|
23
|
+
return {
|
|
24
|
+
command: "npx",
|
|
25
|
+
args: ["-y", "@mushi-mushi/mcp@latest"],
|
|
26
|
+
env,
|
|
27
|
+
icon: MUSHI_ICON_PNG_URL
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
function withReadOnly(baseUrl, readOnly) {
|
|
31
|
+
if (!readOnly) return baseUrl;
|
|
32
|
+
const url = new URL(baseUrl);
|
|
33
|
+
url.searchParams.set("read_only", "1");
|
|
34
|
+
return url.toString();
|
|
35
|
+
}
|
|
36
|
+
function buildHttpConfigObj(projectId, apiKey, mcpHttpUrl, features = DEFAULT_FEATURE_GROUPS, readOnly = false) {
|
|
37
|
+
const url = withReadOnly(
|
|
38
|
+
appendFeaturesToUrl(mcpHttpUrl, features),
|
|
39
|
+
readOnly
|
|
40
|
+
);
|
|
41
|
+
const headers = {
|
|
42
|
+
Authorization: `Bearer ${apiKey}`,
|
|
43
|
+
"X-Mushi-Api-Key": apiKey
|
|
44
|
+
};
|
|
45
|
+
if (projectId) headers["X-Mushi-Project-Id"] = projectId;
|
|
46
|
+
return { type: "http", url, headers, icon: MUSHI_ICON_PNG_URL };
|
|
47
|
+
}
|
|
48
|
+
function encodeCursorDeeplink(name, config) {
|
|
49
|
+
const encoded = btoa(JSON.stringify(config));
|
|
50
|
+
return `cursor://anysphere.cursor-deeplink/mcp/install?name=${encodeURIComponent(name)}&config=${encodeURIComponent(encoded)}`;
|
|
51
|
+
}
|
|
52
|
+
function toVsCodeConfig(config) {
|
|
53
|
+
if ("type" in config && config.type === "http") return config;
|
|
54
|
+
return { type: "stdio", ...config };
|
|
55
|
+
}
|
|
56
|
+
function encodeVsCodeDeeplink(scheme, name, config) {
|
|
57
|
+
const withType = toVsCodeConfig(config);
|
|
58
|
+
return `${scheme}:mcp/install?name=${encodeURIComponent(name)}&config=${encodeURIComponent(JSON.stringify(withType))}`;
|
|
59
|
+
}
|
|
60
|
+
var MCP_CLIENTS = [
|
|
61
|
+
// ── Cursor ──────────────────────────────────────────────────────────────────
|
|
62
|
+
{
|
|
63
|
+
id: "cursor",
|
|
64
|
+
label: "Cursor",
|
|
65
|
+
description: "One-click install into your project's .cursor/mcp.json",
|
|
66
|
+
method: "deeplink",
|
|
67
|
+
transport: "stdio",
|
|
68
|
+
cliIde: "cursor",
|
|
69
|
+
build({ projectId, projectName, apiKey, endpoint, features }) {
|
|
70
|
+
const name = projectId ? projectServerName(projectId, projectName) : `mushi-${projectName.toLowerCase().replace(/[^a-z0-9]+/g, "-").slice(0, 24)}`;
|
|
71
|
+
const config = buildStdioConfigObj(projectId, apiKey, endpoint, features);
|
|
72
|
+
return { kind: "deeplink", url: encodeCursorDeeplink(name, config) };
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
// ── VS Code ──────────────────────────────────────────────────────────────────
|
|
76
|
+
{
|
|
77
|
+
id: "vscode",
|
|
78
|
+
label: "VS Code",
|
|
79
|
+
description: "One-click install via VS Code MCP extension",
|
|
80
|
+
method: "deeplink",
|
|
81
|
+
transport: "stdio",
|
|
82
|
+
build({ projectId, projectName, apiKey, endpoint, features }) {
|
|
83
|
+
const name = projectId ? projectServerName(projectId, projectName) : `mushi-${projectName.toLowerCase().replace(/[^a-z0-9]+/g, "-").slice(0, 24)}`;
|
|
84
|
+
const config = buildStdioConfigObj(projectId, apiKey, endpoint, features);
|
|
85
|
+
return { kind: "deeplink", url: encodeVsCodeDeeplink("vscode", name, config) };
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
// ── VS Code Insiders ─────────────────────────────────────────────────────────
|
|
89
|
+
{
|
|
90
|
+
id: "vscode-insiders",
|
|
91
|
+
label: "VS Code Insiders",
|
|
92
|
+
description: "One-click install via VS Code Insiders MCP extension",
|
|
93
|
+
method: "deeplink",
|
|
94
|
+
transport: "stdio",
|
|
95
|
+
build({ projectId, projectName, apiKey, endpoint, features }) {
|
|
96
|
+
const name = projectId ? projectServerName(projectId, projectName) : `mushi-${projectName.toLowerCase().replace(/[^a-z0-9]+/g, "-").slice(0, 24)}`;
|
|
97
|
+
const config = buildStdioConfigObj(projectId, apiKey, endpoint, features);
|
|
98
|
+
return { kind: "deeplink", url: encodeVsCodeDeeplink("vscode-insiders", name, config) };
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
// ── Windsurf ─────────────────────────────────────────────────────────────────
|
|
102
|
+
{
|
|
103
|
+
id: "windsurf",
|
|
104
|
+
label: "Windsurf",
|
|
105
|
+
description: "Copy config into ~/.codeium/windsurf/mcp_config.json",
|
|
106
|
+
method: "config-json",
|
|
107
|
+
transport: "http",
|
|
108
|
+
build({ projectId, projectName, apiKey, mcpHttpUrl, features, readOnly }) {
|
|
109
|
+
const name = projectId ? projectServerName(projectId, projectName) : `mushi-${projectName.toLowerCase().replace(/[^a-z0-9]+/g, "-").slice(0, 24)}`;
|
|
110
|
+
const url = withReadOnly(
|
|
111
|
+
appendFeaturesToUrl(mcpHttpUrl, features ?? DEFAULT_FEATURE_GROUPS),
|
|
112
|
+
readOnly
|
|
113
|
+
);
|
|
114
|
+
const headers = {
|
|
115
|
+
Authorization: `Bearer ${apiKey}`,
|
|
116
|
+
"X-Mushi-Api-Key": apiKey
|
|
117
|
+
};
|
|
118
|
+
if (projectId) headers["X-Mushi-Project-Id"] = projectId;
|
|
119
|
+
const windsurfConfig = { type: "http", serverUrl: url, headers };
|
|
120
|
+
const configJson = JSON.stringify(
|
|
121
|
+
{ mcpServers: { [name]: windsurfConfig } },
|
|
122
|
+
null,
|
|
123
|
+
2
|
|
124
|
+
);
|
|
125
|
+
return {
|
|
126
|
+
kind: "config",
|
|
127
|
+
filePath: "~/.codeium/windsurf/mcp_config.json",
|
|
128
|
+
json: configJson
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
// ── Cline ────────────────────────────────────────────────────────────────────
|
|
133
|
+
{
|
|
134
|
+
id: "cline",
|
|
135
|
+
label: "Cline",
|
|
136
|
+
description: "Copy config into cline_mcp_settings.json",
|
|
137
|
+
method: "config-json",
|
|
138
|
+
transport: "stdio",
|
|
139
|
+
build({ projectId, projectName, apiKey, endpoint, features }) {
|
|
140
|
+
const name = projectId ? projectServerName(projectId, projectName) : `mushi-${projectName.toLowerCase().replace(/[^a-z0-9]+/g, "-").slice(0, 24)}`;
|
|
141
|
+
const env = {
|
|
142
|
+
MUSHI_API_ENDPOINT: endpoint,
|
|
143
|
+
MUSHI_API_KEY: apiKey
|
|
144
|
+
};
|
|
145
|
+
if (features) env.MUSHI_FEATURES = featuresQueryString(features);
|
|
146
|
+
if (projectId) env.MUSHI_PROJECT_ID = projectId;
|
|
147
|
+
const clineConfig = {
|
|
148
|
+
mcpServers: {
|
|
149
|
+
[name]: { command: "npx", args: ["-y", "@mushi-mushi/mcp@latest"], env }
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
return {
|
|
153
|
+
kind: "config",
|
|
154
|
+
// Cline stores config in the VS Code extension's globalStorage
|
|
155
|
+
filePath: "~/Library/Application Support/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json",
|
|
156
|
+
json: JSON.stringify(clineConfig, null, 2)
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
},
|
|
160
|
+
// ── Claude Code ──────────────────────────────────────────────────────────────
|
|
161
|
+
{
|
|
162
|
+
id: "claude-code",
|
|
163
|
+
label: "Claude Code",
|
|
164
|
+
description: "Add via claude CLI command (HTTP transport)",
|
|
165
|
+
method: "cli-command",
|
|
166
|
+
transport: "http",
|
|
167
|
+
cliIde: "claude",
|
|
168
|
+
build({ projectId, projectName, apiKey, mcpHttpUrl, features, readOnly }) {
|
|
169
|
+
const name = projectId ? projectServerName(projectId, projectName) : `mushi-${projectName.toLowerCase().replace(/[^a-z0-9]+/g, "-").slice(0, 24)}`;
|
|
170
|
+
const url = withReadOnly(
|
|
171
|
+
appendFeaturesToUrl(mcpHttpUrl, features ?? DEFAULT_FEATURE_GROUPS),
|
|
172
|
+
readOnly
|
|
173
|
+
);
|
|
174
|
+
const cmd = `claude mcp add --transport http ${name} "${url}" --header "Authorization: Bearer ${apiKey}"`;
|
|
175
|
+
return { kind: "command", text: cmd };
|
|
176
|
+
}
|
|
177
|
+
},
|
|
178
|
+
// ── Claude Desktop ───────────────────────────────────────────────────────────
|
|
179
|
+
{
|
|
180
|
+
id: "claude-desktop",
|
|
181
|
+
label: "Claude Desktop",
|
|
182
|
+
description: "Copy config into claude_desktop_config.json",
|
|
183
|
+
method: "config-json",
|
|
184
|
+
transport: "stdio",
|
|
185
|
+
build({ projectId, projectName, apiKey, endpoint, features }) {
|
|
186
|
+
const name = projectId ? projectServerName(projectId, projectName) : `mushi-${projectName.toLowerCase().replace(/[^a-z0-9]+/g, "-").slice(0, 24)}`;
|
|
187
|
+
const env = {
|
|
188
|
+
MUSHI_API_ENDPOINT: endpoint,
|
|
189
|
+
MUSHI_API_KEY: apiKey
|
|
190
|
+
};
|
|
191
|
+
if (features) env.MUSHI_FEATURES = featuresQueryString(features);
|
|
192
|
+
if (projectId) env.MUSHI_PROJECT_ID = projectId;
|
|
193
|
+
const desktopConfig = {
|
|
194
|
+
mcpServers: {
|
|
195
|
+
[name]: { command: "npx", args: ["-y", "@mushi-mushi/mcp@latest"], env }
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
return {
|
|
199
|
+
kind: "config",
|
|
200
|
+
filePath: "~/Library/Application Support/Claude/claude_desktop_config.json",
|
|
201
|
+
json: JSON.stringify(desktopConfig, null, 2)
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
},
|
|
205
|
+
// ── Zed ──────────────────────────────────────────────────────────────────────
|
|
206
|
+
{
|
|
207
|
+
id: "zed",
|
|
208
|
+
label: "Zed",
|
|
209
|
+
description: "Copy config into ~/.config/zed/settings.json",
|
|
210
|
+
method: "config-json",
|
|
211
|
+
transport: "stdio",
|
|
212
|
+
cliIde: "zed",
|
|
213
|
+
build({ projectId, projectName, apiKey, endpoint, features }) {
|
|
214
|
+
const name = projectId ? projectServerName(projectId, projectName) : `mushi-${projectName.toLowerCase().replace(/[^a-z0-9]+/g, "-").slice(0, 24)}`;
|
|
215
|
+
const env = {
|
|
216
|
+
MUSHI_API_ENDPOINT: endpoint,
|
|
217
|
+
MUSHI_API_KEY: apiKey
|
|
218
|
+
};
|
|
219
|
+
if (features) env.MUSHI_FEATURES = featuresQueryString(features);
|
|
220
|
+
if (projectId) env.MUSHI_PROJECT_ID = projectId;
|
|
221
|
+
const zedConfig = {
|
|
222
|
+
context_servers: {
|
|
223
|
+
[name]: {
|
|
224
|
+
command: {
|
|
225
|
+
path: "npx",
|
|
226
|
+
args: ["-y", "@mushi-mushi/mcp@latest"],
|
|
227
|
+
env
|
|
228
|
+
},
|
|
229
|
+
settings: {}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
};
|
|
233
|
+
return {
|
|
234
|
+
kind: "config",
|
|
235
|
+
filePath: "~/.config/zed/settings.json",
|
|
236
|
+
json: JSON.stringify(zedConfig, null, 2)
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
},
|
|
240
|
+
// ── Any MCP client ───────────────────────────────────────────────────────────
|
|
241
|
+
{
|
|
242
|
+
id: "any",
|
|
243
|
+
label: "Any MCP client",
|
|
244
|
+
description: "Streamable HTTP URL \u2014 works with OpenClaw, Hermes, Gemini CLI, ChatGPT, and more",
|
|
245
|
+
method: "remote-url",
|
|
246
|
+
transport: "http",
|
|
247
|
+
build({ projectId, apiKey, mcpHttpUrl, features, readOnly }) {
|
|
248
|
+
const url = withReadOnly(
|
|
249
|
+
appendFeaturesToUrl(mcpHttpUrl, features ?? DEFAULT_FEATURE_GROUPS),
|
|
250
|
+
readOnly
|
|
251
|
+
);
|
|
252
|
+
const headerSnippet = [
|
|
253
|
+
`Authorization: Bearer ${apiKey}`,
|
|
254
|
+
projectId ? `X-Mushi-Project-Id: ${projectId}` : ""
|
|
255
|
+
].filter(Boolean).join("\n");
|
|
256
|
+
return { kind: "remote-url", url, headerSnippet };
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
];
|
|
260
|
+
function getMcpClient(id) {
|
|
261
|
+
const client = MCP_CLIENTS.find((c) => c.id === id);
|
|
262
|
+
if (!client) throw new Error(`Unknown MCP client id: ${id}`);
|
|
263
|
+
return client;
|
|
264
|
+
}
|
|
265
|
+
export {
|
|
266
|
+
MCP_CLIENTS,
|
|
267
|
+
buildHttpConfigObj as buildRawHttpConfig,
|
|
268
|
+
buildStdioConfigObj as buildRawStdioConfig,
|
|
269
|
+
getMcpClient,
|
|
270
|
+
projectServerName
|
|
271
|
+
};
|
package/dist/feature-groups.d.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* When `features` is omitted → all tools (backward compatible).
|
|
5
5
|
* New installs default to DEFAULT_FEATURE_GROUPS via deeplink / docs.
|
|
6
6
|
*/
|
|
7
|
-
declare const FEATURE_GROUPS: readonly ["triage", "fixes", "inventory", "setup", "qa", "skills", "rewards", "admin", "audit", "docs", "usage", "legacy"];
|
|
7
|
+
declare const FEATURE_GROUPS: readonly ["triage", "fixes", "inventory", "setup", "qa", "skills", "rewards", "admin", "audit", "docs", "usage", "usage", "legacy"];
|
|
8
8
|
type FeatureGroup = (typeof FEATURE_GROUPS)[number];
|
|
9
9
|
/** Lean default for new Cursor installs (~45 tools vs 70 full). */
|
|
10
10
|
declare const DEFAULT_FEATURE_GROUPS: readonly FeatureGroup[];
|
package/dist/feature-groups.js
CHANGED
package/dist/index.js
CHANGED
|
@@ -11,10 +11,10 @@ var TOOL_CATALOG = [
|
|
|
11
11
|
{
|
|
12
12
|
name: "get_recent_reports",
|
|
13
13
|
title: "Recent bug reports",
|
|
14
|
-
description: "List recent bug reports with optional filters (status / category / severity). Use this to
|
|
14
|
+
description: "List recent bug reports with optional filters (status / category / severity). Use this to see what is waiting in your reports inbox right now.",
|
|
15
15
|
scope: "mcp:read",
|
|
16
16
|
hints: { readOnly: true, idempotent: true, openWorld: true },
|
|
17
|
-
useCase: "What landed in my
|
|
17
|
+
useCase: "What landed in my bug queue today?"
|
|
18
18
|
},
|
|
19
19
|
{
|
|
20
20
|
name: "get_report_detail",
|
|
@@ -141,7 +141,7 @@ var TOOL_CATALOG = [
|
|
|
141
141
|
{
|
|
142
142
|
name: "setup_check",
|
|
143
143
|
title: "Dispatch preflight check",
|
|
144
|
-
description: "[Deprecated \u2014 use diagnose_setup with mode=dispatch or diagnose_connection.] Run the 4 **dispatch-readiness** checks for a project and return their pass/fail status (GitHub repo connected, codebase indexed, Anthropic
|
|
144
|
+
description: "[Deprecated \u2014 use diagnose_setup with mode=dispatch or diagnose_connection.] Run the 4 **dispatch-readiness** checks for a project and return their pass/fail status (GitHub repo connected, codebase indexed, your Anthropic API key present, autofix enabled). Also returns the target repo URL when GitHub is connected.",
|
|
145
145
|
scope: "mcp:read",
|
|
146
146
|
hints: { readOnly: true, idempotent: true, openWorld: true },
|
|
147
147
|
useCase: "Is this project ready to auto-fix bugs? What is blocking dispatch?"
|
|
@@ -191,7 +191,7 @@ var TOOL_CATALOG = [
|
|
|
191
191
|
{
|
|
192
192
|
name: "dispatch_fix",
|
|
193
193
|
title: "Dispatch Mushi fix agent",
|
|
194
|
-
description: '
|
|
194
|
+
description: 'Start the Mushi fix agent for a classified report. Set agent="cursor_cloud" to dispatch a Cursor Cloud Agent that opens a signed draft PR. Returns a fix_attempt id; poll get_fix_timeline for progress.',
|
|
195
195
|
scope: "mcp:write",
|
|
196
196
|
hints: { readOnly: false, destructive: false, idempotent: false, openWorld: true },
|
|
197
197
|
useCase: "Let the in-repo agent attempt this fix for me (or: dispatch a Cursor Cloud Agent)."
|
|
@@ -207,7 +207,7 @@ var TOOL_CATALOG = [
|
|
|
207
207
|
{
|
|
208
208
|
name: "test_gen_from_report",
|
|
209
209
|
title: "Generate Playwright test from report",
|
|
210
|
-
description: "
|
|
210
|
+
description: "Generate a Playwright spec from a classified report using your project LLM key and open a draft PR. Requires inventory_v2 + GitHub + LLM keys.",
|
|
211
211
|
scope: "mcp:write",
|
|
212
212
|
hints: { readOnly: false, destructive: false, idempotent: false, openWorld: true },
|
|
213
213
|
useCase: "Turn this regression report into an E2E test PR."
|
|
@@ -239,7 +239,7 @@ var TOOL_CATALOG = [
|
|
|
239
239
|
{
|
|
240
240
|
name: "reopen_report",
|
|
241
241
|
title: "Reopen report (operator)",
|
|
242
|
-
description: "Move a report to reopened for regression
|
|
242
|
+
description: "Move a report to reopened for regression review.",
|
|
243
243
|
scope: "mcp:write",
|
|
244
244
|
hints: { readOnly: false, destructive: false, idempotent: true, openWorld: true },
|
|
245
245
|
useCase: "Reopen a regression the reporter flagged as not fixed."
|
|
@@ -297,10 +297,10 @@ var TOOL_CATALOG = [
|
|
|
297
297
|
{
|
|
298
298
|
name: "get_project_context",
|
|
299
299
|
title: "Project context snapshot",
|
|
300
|
-
description: "Return a rich context snapshot for a project: name, repo URL, SDK heartbeat, ingest status, open report count, autofix readiness,
|
|
300
|
+
description: "Return a rich context snapshot for a project: name, repo URL, SDK heartbeat, ingest status, open report count, autofix readiness, your LLM key config, plan tier, and active integration health. Equivalent to a merged preflight + activation + settings read. Agents should call this at the start of a review session to orient themselves.",
|
|
301
301
|
scope: "mcp:read",
|
|
302
302
|
hints: { readOnly: true, idempotent: true, openWorld: true },
|
|
303
|
-
useCase: "Give me a status overview of this project before I start
|
|
303
|
+
useCase: "Give me a status overview of this project before I start reviewing bugs."
|
|
304
304
|
},
|
|
305
305
|
{
|
|
306
306
|
name: "get_pipeline_logs",
|
|
@@ -321,7 +321,7 @@ var TOOL_CATALOG = [
|
|
|
321
321
|
{
|
|
322
322
|
name: "triage_issue",
|
|
323
323
|
title: "Triage issue end-to-end",
|
|
324
|
-
description: 'Read-only
|
|
324
|
+
description: 'Read-only combined tool: merges report detail, evidence, similar bugs, fix context, blast radius, and recent pipeline logs for a report into a single structured review packet. Returns a prioritised list of recommended next actions (investigate, dispatch_fix, group_with, dismiss). Equivalent to a Sentry "Analyze with Seer" flow grounded in user-felt reports. Pass report_id to kick off review. Call this before dispatch_fix.',
|
|
325
325
|
scope: "mcp:read",
|
|
326
326
|
hints: { readOnly: true, idempotent: true, openWorld: true },
|
|
327
327
|
useCase: "Analyze this bug report end-to-end and tell me what to do."
|
|
@@ -371,8 +371,8 @@ var TDD_TOOL_CATALOG = [
|
|
|
371
371
|
},
|
|
372
372
|
{
|
|
373
373
|
name: "improve_qa_story",
|
|
374
|
-
title: "
|
|
375
|
-
description: "
|
|
374
|
+
title: "Auto-improve a failing QA story",
|
|
375
|
+
description: "Find recently failed qa_story_runs and use Claude to write improved test scripts. New tests are created with source=pdca and approval gated by the original story's automation_mode.",
|
|
376
376
|
scope: "mcp:write",
|
|
377
377
|
hints: { readOnly: false, destructive: false, idempotent: false, openWorld: true },
|
|
378
378
|
useCase: "Fix my failing QA tests automatically."
|
|
@@ -387,16 +387,16 @@ var TDD_TOOL_CATALOG = [
|
|
|
387
387
|
},
|
|
388
388
|
{
|
|
389
389
|
name: "list_byok_keys",
|
|
390
|
-
title: "List
|
|
391
|
-
description: "List all
|
|
390
|
+
title: "List your API key pool",
|
|
391
|
+
description: "List all project API keys, 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.",
|
|
392
392
|
scope: "mcp:read",
|
|
393
393
|
hints: { readOnly: true, idempotent: true, openWorld: true },
|
|
394
394
|
useCase: "Which API keys are active and which are rate-limited?"
|
|
395
395
|
},
|
|
396
396
|
{
|
|
397
397
|
name: "add_byok_key",
|
|
398
|
-
title: "Add
|
|
399
|
-
description: "Add a new API key to the project
|
|
398
|
+
title: "Add an API key",
|
|
399
|
+
description: "Add a new API key to the project pool for a given provider (anthropic, openai, firecrawl, browserbase, cursor). Specify label and priority for ordering. The key is stored encrypted in Supabase Vault.",
|
|
400
400
|
scope: "mcp:write",
|
|
401
401
|
hints: { readOnly: false, destructive: false, idempotent: false, openWorld: true },
|
|
402
402
|
useCase: "Add a backup Anthropic key to the pool."
|
|
@@ -545,7 +545,7 @@ var CODEBASE_TOOL_CATALOG = [
|
|
|
545
545
|
{
|
|
546
546
|
name: "ask_codebase",
|
|
547
547
|
title: "Ask about the indexed codebase",
|
|
548
|
-
description: "Ask a plain-English question about the connected repo. Grounds on pgvector retrieval over project_codebase_files and returns an answer with file:line citations. Requires codebase indexing enabled and
|
|
548
|
+
description: "Ask a plain-English question about the connected repo. Grounds on pgvector retrieval over project_codebase_files and returns an answer with file:line citations. Requires codebase indexing enabled and your Anthropic or OpenAI API key.",
|
|
549
549
|
scope: "mcp:write",
|
|
550
550
|
hints: { readOnly: false, destructive: false, idempotent: false, openWorld: true },
|
|
551
551
|
useCase: "How does authentication work in this repo?"
|
|
@@ -613,6 +613,7 @@ var FEATURE_GROUPS = [
|
|
|
613
613
|
"audit",
|
|
614
614
|
"docs",
|
|
615
615
|
"usage",
|
|
616
|
+
"usage",
|
|
616
617
|
/** Deprecated aliases kept for one release — hidden unless `legacy` or `all`. */
|
|
617
618
|
"legacy"
|
|
618
619
|
];
|
|
@@ -857,6 +858,7 @@ function searchMushiDocs(query, limit = 8) {
|
|
|
857
858
|
}
|
|
858
859
|
|
|
859
860
|
// src/server.ts
|
|
861
|
+
var NO_ARG_INPUT = {};
|
|
860
862
|
var MushiApiError = class extends Error {
|
|
861
863
|
constructor(status, code, message) {
|
|
862
864
|
super(`[${code}] ${message}`);
|
|
@@ -933,7 +935,16 @@ function createMushiServer(config) {
|
|
|
933
935
|
return body;
|
|
934
936
|
}
|
|
935
937
|
async function resolveProjectId(explicitId) {
|
|
936
|
-
if (explicitId)
|
|
938
|
+
if (explicitId) {
|
|
939
|
+
if (projectId && explicitId !== projectId) {
|
|
940
|
+
throw new MushiApiError(
|
|
941
|
+
400,
|
|
942
|
+
"PROJECT_SCOPE_MISMATCH",
|
|
943
|
+
`This MCP entry is bound to project ${projectId}. Either pass projectId="${projectId}", add a second MCP server entry for ${explicitId} (Console \u2192 Connect \u2192 Add to Cursor while that project is selected), or run \`mushi setup --all-projects --ide cursor\` for one entry per repo.`
|
|
944
|
+
);
|
|
945
|
+
}
|
|
946
|
+
return explicitId;
|
|
947
|
+
}
|
|
937
948
|
if (projectId) return projectId;
|
|
938
949
|
try {
|
|
939
950
|
const data = await apiCall(
|
|
@@ -964,6 +975,11 @@ function createMushiServer(config) {
|
|
|
964
975
|
);
|
|
965
976
|
}
|
|
966
977
|
}
|
|
978
|
+
async function projectScopeHeaders(explicitId) {
|
|
979
|
+
const pid = await resolveProjectId(explicitId ?? void 0);
|
|
980
|
+
const headers = { "X-Mushi-Project-Id": pid };
|
|
981
|
+
return { projectId: pid, headers };
|
|
982
|
+
}
|
|
967
983
|
function jsonText(value) {
|
|
968
984
|
return {
|
|
969
985
|
content: [{ type: "text", text: JSON.stringify(value, null, 2) }]
|
|
@@ -1045,11 +1061,15 @@ function createMushiServer(config) {
|
|
|
1045
1061
|
title: titleOf("get_report_detail"),
|
|
1046
1062
|
description: descOf("get_report_detail"),
|
|
1047
1063
|
annotations: annotationsFor("get_report_detail"),
|
|
1048
|
-
inputSchema: {
|
|
1064
|
+
inputSchema: {
|
|
1065
|
+
reportId: z.string().describe("The report UUID"),
|
|
1066
|
+
project_id: z.string().optional().describe("Project UUID \u2014 required for org-scoped keys with multiple projects.")
|
|
1067
|
+
},
|
|
1049
1068
|
outputSchema: { report: z.unknown() }
|
|
1050
1069
|
},
|
|
1051
1070
|
async (args) => {
|
|
1052
|
-
const
|
|
1071
|
+
const { headers } = await projectScopeHeaders(args.project_id);
|
|
1072
|
+
const data = await apiCall(`/v1/admin/reports/${args.reportId}`, { headers });
|
|
1053
1073
|
const res = jsonResult({ report: data });
|
|
1054
1074
|
return {
|
|
1055
1075
|
...res,
|
|
@@ -1075,7 +1095,7 @@ function createMushiServer(config) {
|
|
|
1075
1095
|
title: titleOf("get_two_way_comms_health"),
|
|
1076
1096
|
description: descOf("get_two_way_comms_health"),
|
|
1077
1097
|
annotations: annotationsFor("get_two_way_comms_health"),
|
|
1078
|
-
inputSchema:
|
|
1098
|
+
inputSchema: NO_ARG_INPUT
|
|
1079
1099
|
},
|
|
1080
1100
|
async () => jsonText(await apiCall("/v1/sync/two-way-health"))
|
|
1081
1101
|
);
|
|
@@ -1140,7 +1160,10 @@ function createMushiServer(config) {
|
|
|
1140
1160
|
title: titleOf("get_fix_context"),
|
|
1141
1161
|
description: descOf("get_fix_context"),
|
|
1142
1162
|
annotations: annotationsFor("get_fix_context"),
|
|
1143
|
-
inputSchema: {
|
|
1163
|
+
inputSchema: {
|
|
1164
|
+
reportId: z.string().describe("The report UUID to fix"),
|
|
1165
|
+
project_id: z.string().optional().describe("Project UUID \u2014 required for org-scoped keys with multiple projects.")
|
|
1166
|
+
},
|
|
1144
1167
|
outputSchema: {
|
|
1145
1168
|
report: z.unknown(),
|
|
1146
1169
|
fixPrompt: z.unknown(),
|
|
@@ -1151,7 +1174,10 @@ function createMushiServer(config) {
|
|
|
1151
1174
|
}
|
|
1152
1175
|
},
|
|
1153
1176
|
async (args) => {
|
|
1154
|
-
const
|
|
1177
|
+
const { headers } = await projectScopeHeaders(args.project_id);
|
|
1178
|
+
const report = await apiCall(`/v1/admin/reports/${args.reportId}`, {
|
|
1179
|
+
headers
|
|
1180
|
+
});
|
|
1155
1181
|
return jsonResult({
|
|
1156
1182
|
report,
|
|
1157
1183
|
// Paste-ready fix prompt composed server-side by composeFixPacket()
|
|
@@ -1171,9 +1197,15 @@ function createMushiServer(config) {
|
|
|
1171
1197
|
title: titleOf("get_fix_timeline"),
|
|
1172
1198
|
description: descOf("get_fix_timeline"),
|
|
1173
1199
|
annotations: annotationsFor("get_fix_timeline"),
|
|
1174
|
-
inputSchema: {
|
|
1200
|
+
inputSchema: {
|
|
1201
|
+
fixId: z.string().describe("fix_attempt UUID"),
|
|
1202
|
+
project_id: z.string().optional().describe("Project UUID \u2014 required for org-scoped keys with multiple projects.")
|
|
1203
|
+
}
|
|
1175
1204
|
},
|
|
1176
|
-
async (args) =>
|
|
1205
|
+
async (args) => {
|
|
1206
|
+
const { headers } = await projectScopeHeaders(args.project_id);
|
|
1207
|
+
return jsonText(await apiCall(`/v1/admin/fixes/${args.fixId}/timeline`, { headers }));
|
|
1208
|
+
}
|
|
1177
1209
|
);
|
|
1178
1210
|
server.registerTool(
|
|
1179
1211
|
"get_blast_radius",
|
|
@@ -1370,7 +1402,7 @@ function createMushiServer(config) {
|
|
|
1370
1402
|
title: titleOf("ingest_setup_check"),
|
|
1371
1403
|
description: descOf("ingest_setup_check"),
|
|
1372
1404
|
annotations: annotationsFor("ingest_setup_check"),
|
|
1373
|
-
inputSchema:
|
|
1405
|
+
inputSchema: NO_ARG_INPUT
|
|
1374
1406
|
},
|
|
1375
1407
|
async () => {
|
|
1376
1408
|
const data = await apiCall("/v1/sync/ingest-setup");
|
|
@@ -1464,9 +1496,11 @@ function createMushiServer(config) {
|
|
|
1464
1496
|
const failed = preflight.checks.filter((c) => !c.ready);
|
|
1465
1497
|
dispatchBlock = failed[0]?.hint;
|
|
1466
1498
|
}
|
|
1467
|
-
} catch {
|
|
1499
|
+
} catch (err) {
|
|
1468
1500
|
dispatchReady = false;
|
|
1469
|
-
|
|
1501
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
1502
|
+
dispatchBlock = `Could not run dispatch preflight \u2014 ${detail}`;
|
|
1503
|
+
apiLog.warn("diagnose_setup dispatch preflight failed", { error: detail });
|
|
1470
1504
|
}
|
|
1471
1505
|
}
|
|
1472
1506
|
const ingestFailed = ingest.steps.filter((s) => s.required && !s.complete);
|
|
@@ -1490,7 +1524,7 @@ function createMushiServer(config) {
|
|
|
1490
1524
|
title: titleOf("diagnose_connection"),
|
|
1491
1525
|
description: descOf("diagnose_connection"),
|
|
1492
1526
|
annotations: annotationsFor("diagnose_connection"),
|
|
1493
|
-
inputSchema:
|
|
1527
|
+
inputSchema: NO_ARG_INPUT,
|
|
1494
1528
|
outputSchema: {
|
|
1495
1529
|
ready: z.boolean(),
|
|
1496
1530
|
healthOk: z.boolean(),
|
|
@@ -1643,7 +1677,7 @@ function createMushiServer(config) {
|
|
|
1643
1677
|
title: titleOf("list_projects"),
|
|
1644
1678
|
description: descOf("list_projects"),
|
|
1645
1679
|
annotations: annotationsFor("list_projects"),
|
|
1646
|
-
inputSchema:
|
|
1680
|
+
inputSchema: NO_ARG_INPUT,
|
|
1647
1681
|
outputSchema: {
|
|
1648
1682
|
projects: z.array(z.unknown()),
|
|
1649
1683
|
total: z.number().optional(),
|
|
@@ -1664,7 +1698,7 @@ function createMushiServer(config) {
|
|
|
1664
1698
|
title: titleOf("get_account_overview"),
|
|
1665
1699
|
description: descOf("get_account_overview"),
|
|
1666
1700
|
annotations: annotationsFor("get_account_overview"),
|
|
1667
|
-
inputSchema:
|
|
1701
|
+
inputSchema: NO_ARG_INPUT,
|
|
1668
1702
|
outputSchema: {
|
|
1669
1703
|
projects: z.array(z.unknown()),
|
|
1670
1704
|
total: z.number(),
|
|
@@ -1783,7 +1817,7 @@ function createMushiServer(config) {
|
|
|
1783
1817
|
description: descOf("triage_issue"),
|
|
1784
1818
|
annotations: annotationsFor("triage_issue"),
|
|
1785
1819
|
inputSchema: {
|
|
1786
|
-
report_id: z.string().describe("Report UUID to triage."),
|
|
1820
|
+
report_id: z.string().uuid().describe("Report UUID to triage."),
|
|
1787
1821
|
project_id: z.string().optional().describe("Project UUID. Defaults to configured project."),
|
|
1788
1822
|
include_logs: z.boolean().optional().describe("Include recent pipeline logs in triage packet (default: true).")
|
|
1789
1823
|
},
|
|
@@ -1803,18 +1837,19 @@ function createMushiServer(config) {
|
|
|
1803
1837
|
}
|
|
1804
1838
|
},
|
|
1805
1839
|
async (args) => {
|
|
1806
|
-
const pid = args.project_id
|
|
1840
|
+
const { projectId: pid, headers } = await projectScopeHeaders(args.project_id);
|
|
1807
1841
|
const includeLogs = args.include_logs !== false;
|
|
1808
1842
|
const [reportRes, evidenceRes, similarRes, fixCtxRes, blastRes, logsRes] = await Promise.allSettled([
|
|
1809
|
-
apiCall(`/v1/admin/reports/${args.report_id}
|
|
1810
|
-
apiCall(`/v1/admin/reports/${args.report_id}/timeline
|
|
1843
|
+
apiCall(`/v1/admin/reports/${args.report_id}`, { headers }),
|
|
1844
|
+
apiCall(`/v1/admin/reports/${args.report_id}/timeline`, { headers }),
|
|
1811
1845
|
apiCall(`/v1/admin/reports/similarity`, {
|
|
1812
1846
|
method: "POST",
|
|
1847
|
+
headers,
|
|
1813
1848
|
body: JSON.stringify({ report_id: args.report_id })
|
|
1814
1849
|
}).catch(() => null),
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
includeLogs
|
|
1850
|
+
apiCall(`/v1/admin/reports/${args.report_id}/fix-context`, { headers }).catch(() => null),
|
|
1851
|
+
apiCall(`/v1/admin/reports/${args.report_id}/blast-radius`, { headers }).catch(() => null),
|
|
1852
|
+
includeLogs ? apiCall(`/v1/admin/mcp/logs/${pid}?limit=20&level=warn`, { headers }).catch(() => null) : Promise.resolve(null)
|
|
1818
1853
|
]);
|
|
1819
1854
|
const report = reportRes.status === "fulfilled" ? reportRes.value : null;
|
|
1820
1855
|
const severity = report ? report.severity : "unknown";
|
|
@@ -1843,11 +1878,31 @@ function createMushiServer(config) {
|
|
|
1843
1878
|
args: { reportId: args.report_id }
|
|
1844
1879
|
});
|
|
1845
1880
|
}
|
|
1881
|
+
const partial_errors = [];
|
|
1882
|
+
if (reportRes.status === "rejected") {
|
|
1883
|
+
partial_errors.push(`report: ${String(reportRes.reason)}`);
|
|
1884
|
+
}
|
|
1885
|
+
if (evidenceRes.status === "rejected") {
|
|
1886
|
+
partial_errors.push(`timeline: ${String(evidenceRes.reason)}`);
|
|
1887
|
+
}
|
|
1888
|
+
if (similarRes.status === "rejected") {
|
|
1889
|
+
partial_errors.push(`similarity: ${String(similarRes.reason)}`);
|
|
1890
|
+
}
|
|
1891
|
+
if (fixCtxRes.status === "rejected") {
|
|
1892
|
+
partial_errors.push(`fix_context: ${String(fixCtxRes.reason)}`);
|
|
1893
|
+
}
|
|
1894
|
+
if (blastRes.status === "rejected") {
|
|
1895
|
+
partial_errors.push(`blast_radius: ${String(blastRes.reason)}`);
|
|
1896
|
+
}
|
|
1897
|
+
if (includeLogs && logsRes.status === "rejected") {
|
|
1898
|
+
partial_errors.push(`recent_logs: ${String(logsRes.reason)}`);
|
|
1899
|
+
}
|
|
1846
1900
|
const result = {
|
|
1847
1901
|
report_id: args.report_id,
|
|
1848
1902
|
severity,
|
|
1849
1903
|
category,
|
|
1850
1904
|
status,
|
|
1905
|
+
partial_errors,
|
|
1851
1906
|
report: reportRes.status === "fulfilled" ? reportRes.value : { error: String(reportRes.reason) },
|
|
1852
1907
|
reporter_thread: evidenceRes.status === "fulfilled" ? evidenceRes.value : null,
|
|
1853
1908
|
similar_bugs: similarRes.status === "fulfilled" ? similarRes.value : null,
|
|
@@ -1912,7 +1967,7 @@ function createMushiServer(config) {
|
|
|
1912
1967
|
description: descOf("dispatch_fix"),
|
|
1913
1968
|
annotations: annotationsFor("dispatch_fix"),
|
|
1914
1969
|
inputSchema: {
|
|
1915
|
-
reportId: z.string().describe("Report UUID to fix"),
|
|
1970
|
+
reportId: z.string().uuid().describe("Report UUID to fix"),
|
|
1916
1971
|
agent: z.enum(["claude_code", "codex", "rest_worker", "mcp"]).optional().describe("Override the agent adapter"),
|
|
1917
1972
|
idempotencyKey: z.string().uuid().optional().describe("Optional RFC 4122 UUID. Resend the same key to safely retry without dispatching a duplicate fix job (Idempotency-Key IETF draft)."),
|
|
1918
1973
|
inventoryActionNodeId: z.string().uuid().optional().describe("Optional inventory Action node UUID for spec-traceability (\xA72.10). When provided, the fix-worker embeds the expected_outcome contract in the LLM prompt and runs validateAgainstSpec before opening the PR.")
|
|
@@ -3079,6 +3134,7 @@ Be specific. No generic advice.`
|
|
|
3079
3134
|
}
|
|
3080
3135
|
|
|
3081
3136
|
// src/index.ts
|
|
3137
|
+
import * as Sentry from "@sentry/node";
|
|
3082
3138
|
var _writeStderr = (...args) => process.stderr.write(
|
|
3083
3139
|
args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ") + "\n"
|
|
3084
3140
|
);
|
|
@@ -3094,6 +3150,14 @@ var SCOPES_RAW = process.env.MUSHI_SCOPES ?? "";
|
|
|
3094
3150
|
var parsedScopes = SCOPES_RAW ? SCOPES_RAW.split(",").map((s) => s.trim()).filter((s) => s === "mcp:read" || s === "mcp:write") : ALL_SCOPES;
|
|
3095
3151
|
var SCOPES = SCOPES_RAW && parsedScopes.length === 0 ? ALL_SCOPES : parsedScopes;
|
|
3096
3152
|
var FEATURES = parseFeaturesCsv(process.env.MUSHI_FEATURES);
|
|
3153
|
+
var MCP_SENTRY_DSN = process.env.MUSHI_MCP_SENTRY_DSN?.trim();
|
|
3154
|
+
if (MCP_SENTRY_DSN) {
|
|
3155
|
+
Sentry.init({
|
|
3156
|
+
dsn: MCP_SENTRY_DSN,
|
|
3157
|
+
environment: process.env.MUSHI_SENTRY_ENVIRONMENT ?? process.env.NODE_ENV ?? "development",
|
|
3158
|
+
tracesSampleRate: 0
|
|
3159
|
+
});
|
|
3160
|
+
}
|
|
3097
3161
|
async function main() {
|
|
3098
3162
|
if (!API_KEY) {
|
|
3099
3163
|
log.fatal("MUSHI_API_KEY environment variable is required");
|
|
@@ -3149,7 +3213,8 @@ async function main() {
|
|
|
3149
3213
|
}
|
|
3150
3214
|
lastInventoryAt = updatedAt;
|
|
3151
3215
|
}
|
|
3152
|
-
} catch {
|
|
3216
|
+
} catch (pollErr) {
|
|
3217
|
+
log.debug("inventory poll failed", { err: String(pollErr) });
|
|
3153
3218
|
}
|
|
3154
3219
|
};
|
|
3155
3220
|
void pollInventory();
|
package/dist/server.js
CHANGED
|
@@ -2,13 +2,13 @@ import {
|
|
|
2
2
|
CODEBASE_TOOL_CATALOG,
|
|
3
3
|
TDD_TOOL_CATALOG,
|
|
4
4
|
TOOL_CATALOG
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-7UOTJ2LI.js";
|
|
6
6
|
import {
|
|
7
7
|
MUSHI_SERVER_METADATA
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-EANW5YTI.js";
|
|
9
9
|
import {
|
|
10
10
|
toolMatchesFeatures
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-PW4AK6SV.js";
|
|
12
12
|
|
|
13
13
|
// src/server.ts
|
|
14
14
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
@@ -123,6 +123,7 @@ function searchMushiDocs(query, limit = 8) {
|
|
|
123
123
|
}
|
|
124
124
|
|
|
125
125
|
// src/server.ts
|
|
126
|
+
var NO_ARG_INPUT = {};
|
|
126
127
|
var MushiApiError = class extends Error {
|
|
127
128
|
constructor(status, code, message) {
|
|
128
129
|
super(`[${code}] ${message}`);
|
|
@@ -199,7 +200,16 @@ function createMushiServer(config) {
|
|
|
199
200
|
return body;
|
|
200
201
|
}
|
|
201
202
|
async function resolveProjectId(explicitId) {
|
|
202
|
-
if (explicitId)
|
|
203
|
+
if (explicitId) {
|
|
204
|
+
if (projectId && explicitId !== projectId) {
|
|
205
|
+
throw new MushiApiError(
|
|
206
|
+
400,
|
|
207
|
+
"PROJECT_SCOPE_MISMATCH",
|
|
208
|
+
`This MCP entry is bound to project ${projectId}. Either pass projectId="${projectId}", add a second MCP server entry for ${explicitId} (Console \u2192 Connect \u2192 Add to Cursor while that project is selected), or run \`mushi setup --all-projects --ide cursor\` for one entry per repo.`
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
return explicitId;
|
|
212
|
+
}
|
|
203
213
|
if (projectId) return projectId;
|
|
204
214
|
try {
|
|
205
215
|
const data = await apiCall(
|
|
@@ -230,6 +240,11 @@ function createMushiServer(config) {
|
|
|
230
240
|
);
|
|
231
241
|
}
|
|
232
242
|
}
|
|
243
|
+
async function projectScopeHeaders(explicitId) {
|
|
244
|
+
const pid = await resolveProjectId(explicitId ?? void 0);
|
|
245
|
+
const headers = { "X-Mushi-Project-Id": pid };
|
|
246
|
+
return { projectId: pid, headers };
|
|
247
|
+
}
|
|
233
248
|
function jsonText(value) {
|
|
234
249
|
return {
|
|
235
250
|
content: [{ type: "text", text: JSON.stringify(value, null, 2) }]
|
|
@@ -311,11 +326,15 @@ function createMushiServer(config) {
|
|
|
311
326
|
title: titleOf("get_report_detail"),
|
|
312
327
|
description: descOf("get_report_detail"),
|
|
313
328
|
annotations: annotationsFor("get_report_detail"),
|
|
314
|
-
inputSchema: {
|
|
329
|
+
inputSchema: {
|
|
330
|
+
reportId: z.string().describe("The report UUID"),
|
|
331
|
+
project_id: z.string().optional().describe("Project UUID \u2014 required for org-scoped keys with multiple projects.")
|
|
332
|
+
},
|
|
315
333
|
outputSchema: { report: z.unknown() }
|
|
316
334
|
},
|
|
317
335
|
async (args) => {
|
|
318
|
-
const
|
|
336
|
+
const { headers } = await projectScopeHeaders(args.project_id);
|
|
337
|
+
const data = await apiCall(`/v1/admin/reports/${args.reportId}`, { headers });
|
|
319
338
|
const res = jsonResult({ report: data });
|
|
320
339
|
return {
|
|
321
340
|
...res,
|
|
@@ -341,7 +360,7 @@ function createMushiServer(config) {
|
|
|
341
360
|
title: titleOf("get_two_way_comms_health"),
|
|
342
361
|
description: descOf("get_two_way_comms_health"),
|
|
343
362
|
annotations: annotationsFor("get_two_way_comms_health"),
|
|
344
|
-
inputSchema:
|
|
363
|
+
inputSchema: NO_ARG_INPUT
|
|
345
364
|
},
|
|
346
365
|
async () => jsonText(await apiCall("/v1/sync/two-way-health"))
|
|
347
366
|
);
|
|
@@ -406,7 +425,10 @@ function createMushiServer(config) {
|
|
|
406
425
|
title: titleOf("get_fix_context"),
|
|
407
426
|
description: descOf("get_fix_context"),
|
|
408
427
|
annotations: annotationsFor("get_fix_context"),
|
|
409
|
-
inputSchema: {
|
|
428
|
+
inputSchema: {
|
|
429
|
+
reportId: z.string().describe("The report UUID to fix"),
|
|
430
|
+
project_id: z.string().optional().describe("Project UUID \u2014 required for org-scoped keys with multiple projects.")
|
|
431
|
+
},
|
|
410
432
|
outputSchema: {
|
|
411
433
|
report: z.unknown(),
|
|
412
434
|
fixPrompt: z.unknown(),
|
|
@@ -417,7 +439,10 @@ function createMushiServer(config) {
|
|
|
417
439
|
}
|
|
418
440
|
},
|
|
419
441
|
async (args) => {
|
|
420
|
-
const
|
|
442
|
+
const { headers } = await projectScopeHeaders(args.project_id);
|
|
443
|
+
const report = await apiCall(`/v1/admin/reports/${args.reportId}`, {
|
|
444
|
+
headers
|
|
445
|
+
});
|
|
421
446
|
return jsonResult({
|
|
422
447
|
report,
|
|
423
448
|
// Paste-ready fix prompt composed server-side by composeFixPacket()
|
|
@@ -437,9 +462,15 @@ function createMushiServer(config) {
|
|
|
437
462
|
title: titleOf("get_fix_timeline"),
|
|
438
463
|
description: descOf("get_fix_timeline"),
|
|
439
464
|
annotations: annotationsFor("get_fix_timeline"),
|
|
440
|
-
inputSchema: {
|
|
465
|
+
inputSchema: {
|
|
466
|
+
fixId: z.string().describe("fix_attempt UUID"),
|
|
467
|
+
project_id: z.string().optional().describe("Project UUID \u2014 required for org-scoped keys with multiple projects.")
|
|
468
|
+
}
|
|
441
469
|
},
|
|
442
|
-
async (args) =>
|
|
470
|
+
async (args) => {
|
|
471
|
+
const { headers } = await projectScopeHeaders(args.project_id);
|
|
472
|
+
return jsonText(await apiCall(`/v1/admin/fixes/${args.fixId}/timeline`, { headers }));
|
|
473
|
+
}
|
|
443
474
|
);
|
|
444
475
|
server.registerTool(
|
|
445
476
|
"get_blast_radius",
|
|
@@ -636,7 +667,7 @@ function createMushiServer(config) {
|
|
|
636
667
|
title: titleOf("ingest_setup_check"),
|
|
637
668
|
description: descOf("ingest_setup_check"),
|
|
638
669
|
annotations: annotationsFor("ingest_setup_check"),
|
|
639
|
-
inputSchema:
|
|
670
|
+
inputSchema: NO_ARG_INPUT
|
|
640
671
|
},
|
|
641
672
|
async () => {
|
|
642
673
|
const data = await apiCall("/v1/sync/ingest-setup");
|
|
@@ -730,9 +761,11 @@ function createMushiServer(config) {
|
|
|
730
761
|
const failed = preflight.checks.filter((c) => !c.ready);
|
|
731
762
|
dispatchBlock = failed[0]?.hint;
|
|
732
763
|
}
|
|
733
|
-
} catch {
|
|
764
|
+
} catch (err) {
|
|
734
765
|
dispatchReady = false;
|
|
735
|
-
|
|
766
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
767
|
+
dispatchBlock = `Could not run dispatch preflight \u2014 ${detail}`;
|
|
768
|
+
apiLog.warn("diagnose_setup dispatch preflight failed", { error: detail });
|
|
736
769
|
}
|
|
737
770
|
}
|
|
738
771
|
const ingestFailed = ingest.steps.filter((s) => s.required && !s.complete);
|
|
@@ -756,7 +789,7 @@ function createMushiServer(config) {
|
|
|
756
789
|
title: titleOf("diagnose_connection"),
|
|
757
790
|
description: descOf("diagnose_connection"),
|
|
758
791
|
annotations: annotationsFor("diagnose_connection"),
|
|
759
|
-
inputSchema:
|
|
792
|
+
inputSchema: NO_ARG_INPUT,
|
|
760
793
|
outputSchema: {
|
|
761
794
|
ready: z.boolean(),
|
|
762
795
|
healthOk: z.boolean(),
|
|
@@ -909,7 +942,7 @@ function createMushiServer(config) {
|
|
|
909
942
|
title: titleOf("list_projects"),
|
|
910
943
|
description: descOf("list_projects"),
|
|
911
944
|
annotations: annotationsFor("list_projects"),
|
|
912
|
-
inputSchema:
|
|
945
|
+
inputSchema: NO_ARG_INPUT,
|
|
913
946
|
outputSchema: {
|
|
914
947
|
projects: z.array(z.unknown()),
|
|
915
948
|
total: z.number().optional(),
|
|
@@ -930,7 +963,7 @@ function createMushiServer(config) {
|
|
|
930
963
|
title: titleOf("get_account_overview"),
|
|
931
964
|
description: descOf("get_account_overview"),
|
|
932
965
|
annotations: annotationsFor("get_account_overview"),
|
|
933
|
-
inputSchema:
|
|
966
|
+
inputSchema: NO_ARG_INPUT,
|
|
934
967
|
outputSchema: {
|
|
935
968
|
projects: z.array(z.unknown()),
|
|
936
969
|
total: z.number(),
|
|
@@ -1049,7 +1082,7 @@ function createMushiServer(config) {
|
|
|
1049
1082
|
description: descOf("triage_issue"),
|
|
1050
1083
|
annotations: annotationsFor("triage_issue"),
|
|
1051
1084
|
inputSchema: {
|
|
1052
|
-
report_id: z.string().describe("Report UUID to triage."),
|
|
1085
|
+
report_id: z.string().uuid().describe("Report UUID to triage."),
|
|
1053
1086
|
project_id: z.string().optional().describe("Project UUID. Defaults to configured project."),
|
|
1054
1087
|
include_logs: z.boolean().optional().describe("Include recent pipeline logs in triage packet (default: true).")
|
|
1055
1088
|
},
|
|
@@ -1069,18 +1102,19 @@ function createMushiServer(config) {
|
|
|
1069
1102
|
}
|
|
1070
1103
|
},
|
|
1071
1104
|
async (args) => {
|
|
1072
|
-
const pid = args.project_id
|
|
1105
|
+
const { projectId: pid, headers } = await projectScopeHeaders(args.project_id);
|
|
1073
1106
|
const includeLogs = args.include_logs !== false;
|
|
1074
1107
|
const [reportRes, evidenceRes, similarRes, fixCtxRes, blastRes, logsRes] = await Promise.allSettled([
|
|
1075
|
-
apiCall(`/v1/admin/reports/${args.report_id}
|
|
1076
|
-
apiCall(`/v1/admin/reports/${args.report_id}/timeline
|
|
1108
|
+
apiCall(`/v1/admin/reports/${args.report_id}`, { headers }),
|
|
1109
|
+
apiCall(`/v1/admin/reports/${args.report_id}/timeline`, { headers }),
|
|
1077
1110
|
apiCall(`/v1/admin/reports/similarity`, {
|
|
1078
1111
|
method: "POST",
|
|
1112
|
+
headers,
|
|
1079
1113
|
body: JSON.stringify({ report_id: args.report_id })
|
|
1080
1114
|
}).catch(() => null),
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
includeLogs
|
|
1115
|
+
apiCall(`/v1/admin/reports/${args.report_id}/fix-context`, { headers }).catch(() => null),
|
|
1116
|
+
apiCall(`/v1/admin/reports/${args.report_id}/blast-radius`, { headers }).catch(() => null),
|
|
1117
|
+
includeLogs ? apiCall(`/v1/admin/mcp/logs/${pid}?limit=20&level=warn`, { headers }).catch(() => null) : Promise.resolve(null)
|
|
1084
1118
|
]);
|
|
1085
1119
|
const report = reportRes.status === "fulfilled" ? reportRes.value : null;
|
|
1086
1120
|
const severity = report ? report.severity : "unknown";
|
|
@@ -1109,11 +1143,31 @@ function createMushiServer(config) {
|
|
|
1109
1143
|
args: { reportId: args.report_id }
|
|
1110
1144
|
});
|
|
1111
1145
|
}
|
|
1146
|
+
const partial_errors = [];
|
|
1147
|
+
if (reportRes.status === "rejected") {
|
|
1148
|
+
partial_errors.push(`report: ${String(reportRes.reason)}`);
|
|
1149
|
+
}
|
|
1150
|
+
if (evidenceRes.status === "rejected") {
|
|
1151
|
+
partial_errors.push(`timeline: ${String(evidenceRes.reason)}`);
|
|
1152
|
+
}
|
|
1153
|
+
if (similarRes.status === "rejected") {
|
|
1154
|
+
partial_errors.push(`similarity: ${String(similarRes.reason)}`);
|
|
1155
|
+
}
|
|
1156
|
+
if (fixCtxRes.status === "rejected") {
|
|
1157
|
+
partial_errors.push(`fix_context: ${String(fixCtxRes.reason)}`);
|
|
1158
|
+
}
|
|
1159
|
+
if (blastRes.status === "rejected") {
|
|
1160
|
+
partial_errors.push(`blast_radius: ${String(blastRes.reason)}`);
|
|
1161
|
+
}
|
|
1162
|
+
if (includeLogs && logsRes.status === "rejected") {
|
|
1163
|
+
partial_errors.push(`recent_logs: ${String(logsRes.reason)}`);
|
|
1164
|
+
}
|
|
1112
1165
|
const result = {
|
|
1113
1166
|
report_id: args.report_id,
|
|
1114
1167
|
severity,
|
|
1115
1168
|
category,
|
|
1116
1169
|
status,
|
|
1170
|
+
partial_errors,
|
|
1117
1171
|
report: reportRes.status === "fulfilled" ? reportRes.value : { error: String(reportRes.reason) },
|
|
1118
1172
|
reporter_thread: evidenceRes.status === "fulfilled" ? evidenceRes.value : null,
|
|
1119
1173
|
similar_bugs: similarRes.status === "fulfilled" ? similarRes.value : null,
|
|
@@ -1178,7 +1232,7 @@ function createMushiServer(config) {
|
|
|
1178
1232
|
description: descOf("dispatch_fix"),
|
|
1179
1233
|
annotations: annotationsFor("dispatch_fix"),
|
|
1180
1234
|
inputSchema: {
|
|
1181
|
-
reportId: z.string().describe("Report UUID to fix"),
|
|
1235
|
+
reportId: z.string().uuid().describe("Report UUID to fix"),
|
|
1182
1236
|
agent: z.enum(["claude_code", "codex", "rest_worker", "mcp"]).optional().describe("Override the agent adapter"),
|
|
1183
1237
|
idempotencyKey: z.string().uuid().optional().describe("Optional RFC 4122 UUID. Resend the same key to safely retry without dispatching a duplicate fix job (Idempotency-Key IETF draft)."),
|
|
1184
1238
|
inventoryActionNodeId: z.string().uuid().optional().describe("Optional inventory Action node UUID for spec-traceability (\xA72.10). When provided, the fix-worker embeds the expected_outcome contract in the LLM prompt and runs validateAgainstSpec before opening the PR.")
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mushi-mushi/mcp",
|
|
3
3
|
"mcpName": "io.github.kensaurus/mushi-mushi",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.17.1",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "MCP server for Mushi Mushi — triage user-felt bugs, pull evidence (screenshots, console logs, network requests), check blast radius, and dispatch fix PRs, all from your AI coding agent",
|
|
7
7
|
"type": "module",
|
|
@@ -25,6 +25,10 @@
|
|
|
25
25
|
"./feature-groups": {
|
|
26
26
|
"types": "./dist/feature-groups.d.ts",
|
|
27
27
|
"import": "./dist/feature-groups.js"
|
|
28
|
+
},
|
|
29
|
+
"./clients": {
|
|
30
|
+
"types": "./dist/clients.d.ts",
|
|
31
|
+
"import": "./dist/clients.js"
|
|
28
32
|
}
|
|
29
33
|
},
|
|
30
34
|
"main": "./dist/index.js",
|
|
@@ -42,8 +46,9 @@
|
|
|
42
46
|
],
|
|
43
47
|
"dependencies": {
|
|
44
48
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
49
|
+
"@sentry/node": "^10.57.0",
|
|
45
50
|
"zod": "^4.4.3",
|
|
46
|
-
"@mushi-mushi/core": "^1.
|
|
51
|
+
"@mushi-mushi/core": "^1.22.1"
|
|
47
52
|
},
|
|
48
53
|
"devDependencies": {
|
|
49
54
|
"@types/node": "^22.19.21",
|