@mushi-mushi/mcp 0.21.0 → 0.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +34 -42
- package/dist/branding.d.ts +8 -2
- package/dist/branding.js +1 -1
- package/dist/catalog.d.ts +41 -1
- package/dist/catalog.js +7 -3
- package/dist/{chunk-EANW5YTI.js → chunk-FJRPC4VR.js} +1 -1
- package/dist/{chunk-XSL2FPLV.js → chunk-LFSAKI6Z.js} +12 -15
- package/dist/{chunk-FNDGCFSJ.js → chunk-RPSPV4FC.js} +87 -21
- package/dist/clients.d.ts +1 -1
- package/dist/clients.js +3 -3
- package/dist/feature-groups.d.ts +14 -4
- package/dist/feature-groups.js +1 -1
- package/dist/index.js +3041 -512
- package/dist/server.d.ts +18 -5
- package/dist/server.js +2747 -392
- package/package.json +21 -19
- package/CODE_OF_CONDUCT.md +0 -51
- package/CONTRIBUTING.md +0 -226
- package/SECURITY.md +0 -359
package/dist/server.js
CHANGED
|
@@ -1,128 +1,1886 @@
|
|
|
1
1
|
import {
|
|
2
2
|
CODEBASE_TOOL_CATALOG,
|
|
3
|
+
MUSHI_SERVER_INSTRUCTIONS,
|
|
3
4
|
TDD_TOOL_CATALOG,
|
|
4
5
|
TOOL_CATALOG,
|
|
5
|
-
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
routeUseMushiIntent
|
|
7
|
+
} from "./chunk-RPSPV4FC.js";
|
|
7
8
|
import {
|
|
8
9
|
MUSHI_SERVER_METADATA
|
|
9
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-FJRPC4VR.js";
|
|
10
11
|
import {
|
|
11
12
|
DEPRECATED_TOOL_ALIASES,
|
|
13
|
+
TOOL_FEATURE_MAP,
|
|
12
14
|
toolMatchesFeatures
|
|
13
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-LFSAKI6Z.js";
|
|
14
16
|
|
|
15
17
|
// src/server.ts
|
|
18
|
+
import { AsyncLocalStorage } from "async_hooks";
|
|
16
19
|
import { McpServer } from "@modelcontextprotocol/server";
|
|
17
20
|
import { z } from "zod";
|
|
18
21
|
import { createLogger } from "@mushi-mushi/core";
|
|
19
22
|
|
|
20
23
|
// src/docs-index.ts
|
|
21
|
-
var
|
|
24
|
+
var MUSHI_DOCS_BASE = "https://kensaur.us/mushi-mushi/docs";
|
|
25
|
+
function docUrl(route) {
|
|
26
|
+
return route === "/" ? MUSHI_DOCS_BASE : MUSHI_DOCS_BASE + route;
|
|
27
|
+
}
|
|
22
28
|
var MUSHI_DOCS_INDEX = [
|
|
23
29
|
{
|
|
24
|
-
title: "
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
30
|
+
title: "Mushi Mushi \u2014 know why your AI-built app broke, with the fix ready",
|
|
31
|
+
url: docUrl("/"),
|
|
32
|
+
route: "/",
|
|
33
|
+
markdown: "index.md",
|
|
34
|
+
keywords: ["know", "ai-built", "app", "broke", "fix", "ready"],
|
|
35
|
+
headings: ["See it in action", "What this is (and is not)", "Get started with the SDK", "Install in your framework", "One queue. Sentry flows in, fixes flow out."],
|
|
36
|
+
excerpt: "Your AI shipped it. Mushi tells you why it broke: a plain-English diagnosis and a ready fix in your editor. Open source, Sentry optional."
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
title: "Admin console",
|
|
40
|
+
url: docUrl("/admin"),
|
|
41
|
+
route: "/admin",
|
|
42
|
+
markdown: "admin/index.md",
|
|
43
|
+
keywords: ["admin", "console"],
|
|
44
|
+
headings: ["Day-to-day", "Deeper surfaces"],
|
|
45
|
+
excerpt: "Guide to the Mushi admin console \u2014 the reports inbox, fix drafts and PRs, plain-English queries, and a link to every deeper page of the console."
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
title: "Activity",
|
|
49
|
+
url: docUrl("/admin/activity"),
|
|
50
|
+
route: "/admin/activity",
|
|
51
|
+
markdown: "admin/activity.md",
|
|
52
|
+
keywords: ["admin", "activity"],
|
|
53
|
+
headings: ["What you see", "Data source", "Related"],
|
|
54
|
+
excerpt: "The Activity page in the Mushi console shows end-user sessions, top routes, and identified versus anonymous users for a project over the last 30 days."
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
title: "Anomaly detection",
|
|
58
|
+
url: docUrl("/admin/anomalies"),
|
|
59
|
+
route: "/admin/anomalies",
|
|
60
|
+
markdown: "admin/anomalies.md",
|
|
61
|
+
keywords: ["admin", "anomalies", "anomaly", "detection"],
|
|
62
|
+
headings: ["Tabs", "Anomalies", "Metrics", "Detect", "Detection methods", "Auto-reporting", "API", "Related pages"],
|
|
63
|
+
excerpt: "The Anomaly detection page runs statistical detectors over the metrics you send and can file a report in your queue when one behaves unexpectedly."
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
title: "Anti-gaming",
|
|
67
|
+
url: docUrl("/admin/anti-gaming"),
|
|
68
|
+
route: "/admin/anti-gaming",
|
|
69
|
+
markdown: "admin/anti-gaming.md",
|
|
70
|
+
keywords: ["admin", "anti-gaming", "anti", "gaming"],
|
|
71
|
+
headings: ["How detection works", "Actions", "Velocity rules", "False positive rate", "Related pages"],
|
|
72
|
+
excerpt: "The Anti-gaming page flags reporters whose patterns look abusive \u2014 near-duplicate spam, reward farming or synthetic sessions \u2014 for you to review."
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
title: "Audit log",
|
|
76
|
+
url: docUrl("/admin/audit"),
|
|
77
|
+
route: "/admin/audit",
|
|
78
|
+
markdown: "admin/audit.md",
|
|
79
|
+
keywords: ["admin", "audit", "log"],
|
|
80
|
+
headings: ["What's logged", "Columns", "Hero metrics", "Filtering", "Expanding a row", "Export", "Common tasks", "Tracing an unexpected fix-worker action"],
|
|
81
|
+
excerpt: "The Audit log records every admin action and agent run in Mushi \u2014 who minted API keys, who dispatched a fix, and what triggered a pull request."
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
title: "Billing",
|
|
85
|
+
url: docUrl("/admin/billing"),
|
|
86
|
+
route: "/admin/billing",
|
|
87
|
+
markdown: "admin/billing.md",
|
|
88
|
+
keywords: ["admin", "billing"],
|
|
89
|
+
headings: ["Reading your usage card", "Usage breakdown", "Predictability controls", "Upgrading your plan", "Managing payment and invoices", "CLI parity", "Related docs"],
|
|
90
|
+
excerpt: "The Billing page shows your diagnosis quota, usage alerts at 50% and 80%, your projected bill, and spend caps for your Mushi plan."
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
title: "CLI device auth",
|
|
94
|
+
url: docUrl("/admin/cli-auth"),
|
|
95
|
+
route: "/admin/cli-auth",
|
|
96
|
+
markdown: "admin/cli-auth.md",
|
|
97
|
+
keywords: ["admin", "cli-auth", "cli", "auth", "device"],
|
|
98
|
+
headings: ["Related pages"],
|
|
99
|
+
excerpt: "The page that mushi login opens so you can approve a CLI device code while signed in to the console, and how the device authorization flow works."
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
title: "Code health",
|
|
103
|
+
url: docUrl("/admin/code-health"),
|
|
104
|
+
route: "/admin/code-health",
|
|
105
|
+
markdown: "admin/code-health.md",
|
|
106
|
+
keywords: ["admin", "code-health", "code", "health"],
|
|
107
|
+
headings: ["What you see", "Wiring CI ingest", "API", "Related pages"],
|
|
108
|
+
excerpt: "The Code health page charts bundle size and oversized files from your CI over time, so you notice an app getting heavier before your users do."
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
title: "Compliance",
|
|
112
|
+
url: docUrl("/admin/compliance"),
|
|
113
|
+
route: "/admin/compliance",
|
|
114
|
+
markdown: "admin/compliance.md",
|
|
115
|
+
keywords: ["admin", "compliance"],
|
|
116
|
+
headings: ["Navigating the page", "SOC 2 control evidence", "Data residency", "Retention policies", "Data Subject Access Requests (DSAR)", "Filing a DSAR", "DSAR workflow", "Common tasks"],
|
|
117
|
+
excerpt: "The Compliance page shows SOC 2 control status, retention policies and DSAR requests, and exports an evidence PDF for procurement reviews."
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
title: "Connect & Update",
|
|
121
|
+
url: docUrl("/admin/connect"),
|
|
122
|
+
route: "/admin/connect",
|
|
123
|
+
markdown: "admin/connect.md",
|
|
124
|
+
keywords: ["admin", "connect", "update"],
|
|
125
|
+
headings: ["ConnectStudio hero (3 lanes)", "Sections", "Native app CI secrets", "SDK upgrade PR", "Quick path (5 minutes)", "Connect GitHub", "Install SDK", "Wire MCP"],
|
|
126
|
+
excerpt: "Connect & Update wires GitHub, the SDK, MCP, the CLI and your editor to a Mushi project, and shows when your installed SDK is out of date."
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
title: "Content quality",
|
|
130
|
+
url: docUrl("/admin/content"),
|
|
131
|
+
route: "/admin/content",
|
|
132
|
+
markdown: "admin/content.md",
|
|
133
|
+
keywords: ["admin", "content", "quality"],
|
|
134
|
+
headings: ["Related pages"],
|
|
135
|
+
excerpt: "The Content quality page queues AI-generated copy, release notes and knowledge snippets that scored poorly or were flagged, so you can fix them."
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
title: "Cost & usage",
|
|
139
|
+
url: docUrl("/admin/cost"),
|
|
140
|
+
route: "/admin/cost",
|
|
141
|
+
markdown: "admin/cost.md",
|
|
142
|
+
keywords: ["admin", "cost", "usage"],
|
|
143
|
+
headings: ["What's tracked", "Charts", "Controlling costs", "Export", "Related pages"],
|
|
144
|
+
excerpt: "The Cost page shows LLM token use and estimated spend per model and pipeline for a Mushi project, so you can see what each diagnosis costs."
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
title: "Dashboard",
|
|
148
|
+
url: docUrl("/admin/dashboard"),
|
|
149
|
+
route: "/admin/dashboard",
|
|
150
|
+
markdown: "admin/dashboard.md",
|
|
151
|
+
keywords: ["admin", "dashboard"],
|
|
152
|
+
headings: ["What to look for first", "Flow canvas", "KPI tiles (14-day window)", "Charts row (14 days)", "Triage queue", "Insights row", "Common tasks", "Morning check (2 minutes)"],
|
|
153
|
+
excerpt: "The Mushi dashboard answers whether anything broke since your last release \u2014 new reports, overnight fix runs and pipeline health on one screen."
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
title: "Docs bridge",
|
|
157
|
+
url: docUrl("/admin/docs-bridge"),
|
|
158
|
+
route: "/admin/docs-bridge",
|
|
159
|
+
markdown: "admin/docs-bridge.md",
|
|
160
|
+
keywords: ["admin", "docs-bridge", "bridge"],
|
|
161
|
+
headings: ["How it works", "Status messages", "Security", "Related pages"],
|
|
162
|
+
excerpt: "The docs bridge is the console's silent sign-in relay that lets the Mushi docs site fill live snippets with your project details when you are signed in."
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
title: "Drift scanner",
|
|
166
|
+
url: docUrl("/admin/drift"),
|
|
167
|
+
route: "/admin/drift",
|
|
168
|
+
markdown: "admin/drift.md",
|
|
169
|
+
keywords: ["admin", "drift", "scanner"],
|
|
170
|
+
headings: ["When to run it", "Tab: Scanner", "If the scan fails", "Tab: Findings", "What to do with a critical finding", "Tab: Snapshots", "Common tasks", "Post-deploy check (the fast path)"],
|
|
171
|
+
excerpt: "The Drift scanner compares your live routes and components against the last snapshot to find the pages that a refactor silently broke."
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
title: "Experiments",
|
|
175
|
+
url: docUrl("/admin/experiments"),
|
|
176
|
+
route: "/admin/experiments",
|
|
177
|
+
markdown: "admin/experiments.md",
|
|
178
|
+
keywords: ["admin", "experiments"],
|
|
179
|
+
headings: ["Tabs", "Experiments", "New", "Experiment detail drawer", "Analysis results", "Bandit mode (Thompson Sampling)", "API", "Related pages"],
|
|
180
|
+
excerpt: "The Experiments page defines and runs A/B tests in Mushi and analyses them with CUPED variance reduction and sequential testing."
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
title: "Codebase Atlas",
|
|
184
|
+
url: docUrl("/admin/explore"),
|
|
185
|
+
route: "/admin/explore",
|
|
186
|
+
markdown: "admin/explore.md",
|
|
187
|
+
keywords: ["admin", "explore", "codebase", "atlas"],
|
|
188
|
+
headings: ["Tab groups", "Map views", "Graph", "Layers", "Search", "The detail panel", "Layer filter chips", "Stats bar"],
|
|
189
|
+
excerpt: "Visual, searchable map of every indexed source file \u2014 explore architectural layers, import dependencies, and find code by meaning."
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
title: "Feature board",
|
|
193
|
+
url: docUrl("/admin/feature-board"),
|
|
194
|
+
route: "/admin/feature-board",
|
|
195
|
+
markdown: "admin/feature-board.md",
|
|
196
|
+
keywords: ["admin", "feature-board", "feature", "board"],
|
|
197
|
+
headings: ["Related pages"],
|
|
198
|
+
excerpt: "The Feature board collects feature requests from support tickets so members can upvote them and you can mark them as shipped."
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
title: "Feedback hub",
|
|
202
|
+
url: docUrl("/admin/feedback"),
|
|
203
|
+
route: "/admin/feedback",
|
|
204
|
+
markdown: "admin/feedback.md",
|
|
205
|
+
keywords: ["admin", "feedback", "hub"],
|
|
206
|
+
headings: ["Related pages"],
|
|
207
|
+
excerpt: "The Feedback hub lists the support tickets filed from the console \u2014 active, shipped and all \u2014 with unread replies in one place."
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
title: "Fine-tuning",
|
|
211
|
+
url: docUrl("/admin/fine-tuning"),
|
|
212
|
+
route: "/admin/fine-tuning",
|
|
213
|
+
markdown: "admin/fine-tuning.md",
|
|
214
|
+
keywords: ["admin", "fine-tuning", "fine", "tuning"],
|
|
215
|
+
headings: ["Pipeline", "Stage details", "exporting \u2192 exported", "training \u2192 trained", "validating \u2192 validated / rejected", "promoted", "History tab", "Cost projection"],
|
|
216
|
+
excerpt: "The Fine-tuning page exports your best-scored classifications, trains a fine-tuned model, benchmarks it offline and promotes it to production."
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
title: "Fix drafts & PRs",
|
|
220
|
+
url: docUrl("/admin/fixes"),
|
|
221
|
+
route: "/admin/fixes",
|
|
222
|
+
markdown: "admin/fixes.md",
|
|
223
|
+
keywords: ["admin", "fixes", "fix", "drafts", "prs"],
|
|
224
|
+
headings: ["The fixes list", "Streaming view", "Spec-validation warnings", "Cursor Cloud Agent runs", "Dispatching a fix", "Webhooks", "CI feedback on open PRs", "Merging from the console"],
|
|
225
|
+
excerpt: "The Fixes page tracks every draft pull request Mushi opened from a bug report, from the moment a fix run starts until the PR is ready or the run fails."
|
|
226
|
+
},
|
|
227
|
+
{
|
|
228
|
+
title: "Full-stack audit",
|
|
229
|
+
url: docUrl("/admin/fullstack-audit"),
|
|
230
|
+
route: "/admin/fullstack-audit",
|
|
231
|
+
markdown: "admin/fullstack-audit.md",
|
|
232
|
+
keywords: ["admin", "fullstack-audit", "fullstack", "audit", "full-stack"],
|
|
233
|
+
headings: ["Scorecard sections", "Prerequisites", "CLI", "Related pages"],
|
|
234
|
+
excerpt: "The Full-stack audit runs one health check across RLS gaps, recent backend errors and API contract drift, and returns a readable scorecard."
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
title: "Knowledge graph",
|
|
238
|
+
url: docUrl("/admin/graph"),
|
|
239
|
+
route: "/admin/graph",
|
|
240
|
+
markdown: "admin/graph.md",
|
|
241
|
+
keywords: ["admin", "graph", "knowledge"],
|
|
242
|
+
headings: ["Node types", "Blast radius", "Surface mode", "Cypher console", "Keyboard shortcuts", "See also"],
|
|
243
|
+
excerpt: "The Knowledge graph page draws your project's bugs, components and fixes as an interactive graph, so you can see which areas cluster problems."
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
title: "Integration health",
|
|
247
|
+
url: docUrl("/admin/health"),
|
|
248
|
+
route: "/admin/health",
|
|
249
|
+
markdown: "admin/health.md",
|
|
250
|
+
keywords: ["admin", "health", "integration"],
|
|
251
|
+
headings: ["Time window", "LLM health summary", "Provider probes", "Cron job health", "Recent LLM calls log", "Recommended actions", "Related pages"],
|
|
252
|
+
excerpt: "The Integration health page monitors Mushi's LLM pipelines, cron jobs and provider connections so you catch a degraded integration early."
|
|
253
|
+
},
|
|
254
|
+
{
|
|
255
|
+
title: "Action inbox",
|
|
256
|
+
url: docUrl("/admin/inbox"),
|
|
257
|
+
route: "/admin/inbox",
|
|
258
|
+
markdown: "admin/inbox.md",
|
|
259
|
+
keywords: ["admin", "inbox", "action"],
|
|
260
|
+
headings: ["How it works", "Reading an action card", "Cleared stages strip", "Filtering by PDCA phase", "Common tasks", "Morning triage (the fast path)", "Understanding a stage chip colour", "When the Inbox seems wrong"],
|
|
261
|
+
excerpt: "The Action inbox gathers everything in Mushi that needs a human decision \u2014 reports, queue failures, health and drift alerts \u2014 in one list."
|
|
262
|
+
},
|
|
263
|
+
{
|
|
264
|
+
title: "Integrations",
|
|
265
|
+
url: docUrl("/admin/integrations"),
|
|
266
|
+
route: "/admin/integrations",
|
|
267
|
+
markdown: "admin/integrations.md",
|
|
268
|
+
keywords: ["admin", "integrations"],
|
|
269
|
+
headings: ["Hero", "Core platform integrations", "Sentry", "Langfuse", "GitHub", "Cursor Cloud", "Routing destinations", "Common tasks"],
|
|
270
|
+
excerpt: "Set up Mushi integrations from one page \u2014 send new bugs to Linear, bring in Sentry feedback, and open fix pull requests on your GitHub repo."
|
|
271
|
+
},
|
|
272
|
+
{
|
|
273
|
+
title: "Intelligence reports",
|
|
274
|
+
url: docUrl("/admin/intelligence"),
|
|
275
|
+
route: "/admin/intelligence",
|
|
276
|
+
markdown: "admin/intelligence.md",
|
|
277
|
+
keywords: ["admin", "intelligence", "reports"],
|
|
278
|
+
headings: ["What's in a report", "Schedule and delivery", "Regenerating on demand", "Cross-customer benchmarks", "PDF format", "See also"],
|
|
279
|
+
excerpt: "Intelligence reports are Mushi's weekly plain-English bug digest for each project \u2014 what broke, what got fixed and what to watch, sent to your inbox."
|
|
280
|
+
},
|
|
281
|
+
{
|
|
282
|
+
title: "User stories \xB7 Inventory",
|
|
283
|
+
url: docUrl("/admin/inventory"),
|
|
284
|
+
route: "/admin/inventory",
|
|
285
|
+
markdown: "admin/inventory.md",
|
|
286
|
+
keywords: ["admin", "inventory", "user", "stories"],
|
|
287
|
+
headings: ["What you see", "Run gates / Run crawler from a row", "SDK-driven discovery", "Surface mode in the graph", "Five gates", "Synthetic monitor", "Spec traceability \u2014 every fix carries the originating Action", "See also"],
|
|
288
|
+
excerpt: "The User stories page manages your inventory.yaml \u2014 every page, action and story your app should have \u2014 and shows which of them the gates cover."
|
|
289
|
+
},
|
|
290
|
+
{
|
|
291
|
+
title: "Iterate",
|
|
292
|
+
url: docUrl("/admin/iterate"),
|
|
293
|
+
route: "/admin/iterate",
|
|
294
|
+
markdown: "admin/iterate.md",
|
|
295
|
+
keywords: ["admin", "iterate"],
|
|
296
|
+
headings: ["Understanding the score", "Runs table", "Run detail drawer", "Score timeline", "Iterations list", "Succeeded run", "Creating a run", "Common tasks"],
|
|
297
|
+
excerpt: "The Iterate page queues improvement runs on a user flow \u2014 an agent tests the screens, finds friction and proposes changes for you to review."
|
|
298
|
+
},
|
|
299
|
+
{
|
|
300
|
+
title: "Judge dashboard",
|
|
301
|
+
url: docUrl("/admin/judge"),
|
|
302
|
+
route: "/admin/judge",
|
|
303
|
+
markdown: "admin/judge.md",
|
|
304
|
+
keywords: ["admin", "judge", "dashboard"],
|
|
305
|
+
headings: ["Trailing score panel", "Per-component breakdown", "Prompt A/B status", "Drift alerts", "See also"],
|
|
306
|
+
excerpt: "The Judge page scores how well Mushi's plain-English reads match reality over time, so you see quality slipping or a better prompt ready to promote."
|
|
307
|
+
},
|
|
308
|
+
{
|
|
309
|
+
title: "Lessons",
|
|
310
|
+
url: docUrl("/admin/lessons"),
|
|
311
|
+
route: "/admin/lessons",
|
|
312
|
+
markdown: "admin/lessons.md",
|
|
313
|
+
keywords: ["admin", "lessons"],
|
|
314
|
+
headings: ["The lifecycle: how a report becomes a rule", "Tab: Lessons", "When to retire a lesson", "Tab: Clusters", "Tab: Query Sim", "Common tasks", "Reviewing new candidates (weekly)", "After fixing a recurring bug"],
|
|
315
|
+
excerpt: "The Lessons page turns repeat fixes into rules \u2014 when the same root cause keeps returning, Mushi records it so your editor sees it on the next PR."
|
|
316
|
+
},
|
|
317
|
+
{
|
|
318
|
+
title: "Plugin marketplace",
|
|
319
|
+
url: docUrl("/admin/marketplace"),
|
|
320
|
+
route: "/admin/marketplace",
|
|
321
|
+
markdown: "admin/marketplace.md",
|
|
322
|
+
keywords: ["admin", "marketplace", "plugin"],
|
|
323
|
+
headings: ["Sections", "Available plugins", "Installed", "Recent deliveries", "Installing a plugin", "Managing installed plugins", "Signing verification", "First-party plugins"],
|
|
324
|
+
excerpt: "The Plugin marketplace installs outbound plugins such as PagerDuty, Linear and Zapier that receive Mushi webhook events for your project."
|
|
325
|
+
},
|
|
326
|
+
{
|
|
327
|
+
title: "MCP",
|
|
328
|
+
url: docUrl("/admin/mcp"),
|
|
329
|
+
route: "/admin/mcp",
|
|
330
|
+
markdown: "admin/mcp.md",
|
|
331
|
+
keywords: ["admin", "mcp"],
|
|
332
|
+
headings: ["Quickstart", "1. Generate an MCP-scoped API key", "2. Install the MCP server", "3. Use it", "Install snippets", ".cursor/mcp.json (stdio \u2014 recommended)", "Hosted HTTP (no local subprocess)", "Key status"],
|
|
333
|
+
excerpt: "The MCP page connects Cursor, Claude Code, Windsurf and other MCP clients to Mushi and lists the tools your agent can call from the editor."
|
|
334
|
+
},
|
|
335
|
+
{
|
|
336
|
+
title: "MCP OAuth consent",
|
|
337
|
+
url: docUrl("/admin/mcp-auth"),
|
|
338
|
+
route: "/admin/mcp-auth",
|
|
339
|
+
markdown: "admin/mcp-auth.md",
|
|
340
|
+
keywords: ["admin", "mcp-auth", "mcp", "auth", "oauth", "consent"],
|
|
341
|
+
headings: ["Related pages"],
|
|
342
|
+
excerpt: "The consent page the hosted Mushi MCP server opens when your editor signs in over OAuth \u2014 what you approve and how the authorization flow works."
|
|
343
|
+
},
|
|
344
|
+
{
|
|
345
|
+
title: "Notifications",
|
|
346
|
+
url: docUrl("/admin/notifications"),
|
|
347
|
+
route: "/admin/notifications",
|
|
348
|
+
markdown: "admin/notifications.md",
|
|
349
|
+
keywords: ["admin", "notifications"],
|
|
350
|
+
headings: ["What appears here", "Columns", "Marking as read", "Debugging delivery failures", "Configuring notification rules", "Related pages"],
|
|
351
|
+
excerpt: "The Notifications page lists every alert Mushi sent to reporters and team members for a project, so you can debug delivery or audit messages."
|
|
352
|
+
},
|
|
353
|
+
{
|
|
354
|
+
title: "Onboarding",
|
|
355
|
+
url: docUrl("/admin/onboarding"),
|
|
356
|
+
route: "/admin/onboarding",
|
|
357
|
+
markdown: "admin/onboarding.md",
|
|
358
|
+
keywords: ["admin", "onboarding"],
|
|
359
|
+
headings: ["Setup checklist", "Create a project", "Generate an API key", "Test the connection", "Install the SDK", "Go to dashboard", "CLI setup mode (?setup=cli)", "CliSetupGuide strip"],
|
|
360
|
+
excerpt: "Get your first real bug report into Mushi in about 10 minutes with the console onboarding checklist, from new project to classified report."
|
|
361
|
+
},
|
|
362
|
+
{
|
|
363
|
+
title: "Overview (portfolio)",
|
|
364
|
+
url: docUrl("/admin/overview"),
|
|
365
|
+
route: "/admin/overview",
|
|
366
|
+
markdown: "admin/overview.md",
|
|
367
|
+
keywords: ["admin", "overview", "portfolio"],
|
|
368
|
+
headings: ["What you see", "Data source", "Related"],
|
|
369
|
+
excerpt: "The Overview page is the organization-wide portfolio view \u2014 seven-day activity and open tickets for every connected project at a glance."
|
|
370
|
+
},
|
|
371
|
+
{
|
|
372
|
+
title: "Projects",
|
|
373
|
+
url: docUrl("/admin/projects"),
|
|
374
|
+
route: "/admin/projects",
|
|
375
|
+
markdown: "admin/projects.md",
|
|
376
|
+
keywords: ["admin", "projects"],
|
|
377
|
+
headings: ["Project card", "Creating a project", "API keys", "SDK install configurator", "Send test report", "Deleting a project", "Common tasks", "Connecting a new app (5 minutes)"],
|
|
378
|
+
excerpt: "Create a Mushi project, mint an API key, copy the install snippet for your framework and send a test report, all from the Projects page."
|
|
379
|
+
},
|
|
380
|
+
{
|
|
381
|
+
title: "Prompt lab",
|
|
382
|
+
url: docUrl("/admin/prompt-lab"),
|
|
383
|
+
route: "/admin/prompt-lab",
|
|
384
|
+
markdown: "admin/prompt-lab.md",
|
|
385
|
+
keywords: ["admin", "prompt-lab", "prompt", "lab"],
|
|
386
|
+
headings: ["The 4-step workflow", "KPI tiles", "Stage tabs", "Prompt table columns explained", "Actions per row", "Common tasks", "Fixing wrong component classification", "Investigating a bad judge score"],
|
|
387
|
+
excerpt: "The Prompt lab lets you edit, test and A/B compare the classification prompts behind Mushi's diagnoses before you promote a new version."
|
|
388
|
+
},
|
|
389
|
+
{
|
|
390
|
+
title: "QA Coverage",
|
|
391
|
+
url: docUrl("/admin/qa-coverage"),
|
|
392
|
+
route: "/admin/qa-coverage",
|
|
393
|
+
markdown: "admin/qa-coverage.md",
|
|
394
|
+
keywords: ["admin", "qa-coverage", "coverage", "qa"],
|
|
395
|
+
headings: ["Story lifecycle", "Execution providers", "Creating a story", "Story cards", "Story drawer", "Manual run", "Coverage report", "Related pages"],
|
|
396
|
+
excerpt: "QA Coverage turns user flows written in plain English into scheduled browser tests, so you find out when login or checkout breaks."
|
|
397
|
+
},
|
|
398
|
+
{
|
|
399
|
+
title: "Natural-language query",
|
|
400
|
+
url: docUrl("/admin/query"),
|
|
401
|
+
route: "/admin/query",
|
|
402
|
+
markdown: "admin/query.md",
|
|
403
|
+
keywords: ["admin", "query", "natural-language"],
|
|
404
|
+
headings: ["How it works", "Allow-listed tables", "Example questions", "Audit log", "Chart types", "Saved queries", "See also"],
|
|
405
|
+
excerpt: "Ask questions about your Mushi project's bug data in plain English \u2014 the Query page writes the SQL and draws the chart for you."
|
|
406
|
+
},
|
|
407
|
+
{
|
|
408
|
+
title: "Processing queue",
|
|
409
|
+
url: docUrl("/admin/queue"),
|
|
410
|
+
route: "/admin/queue",
|
|
411
|
+
markdown: "admin/queue.md",
|
|
412
|
+
keywords: ["admin", "queue", "processing"],
|
|
413
|
+
headings: ['What "dead-letter" means', "KPI tiles (14-day sparklines)", "Throughput chart", "Stage breakdown", "Item cards", "Bulk actions", "Common tasks", "Monday morning: 14 dead-letter items"],
|
|
414
|
+
excerpt: "The Processing queue shows reports waiting for classification, retries and dead-letter items, and lets you requeue the ones that failed."
|
|
415
|
+
},
|
|
416
|
+
{
|
|
417
|
+
title: "Real-time collaboration",
|
|
418
|
+
url: docUrl("/admin/realtime"),
|
|
419
|
+
route: "/admin/realtime",
|
|
420
|
+
markdown: "admin/realtime.md",
|
|
421
|
+
keywords: ["admin", "realtime", "real-time", "collaboration"],
|
|
422
|
+
headings: ["Presence", "Comments", "Writing a comment", "Editing and deleting", "Timestamps", "Notifications", "Audit log", "See also"],
|
|
423
|
+
excerpt: "How Mushi report pages support real-time collaboration \u2014 see who else is viewing a report and watch new comments arrive without a refresh."
|
|
424
|
+
},
|
|
425
|
+
{
|
|
426
|
+
title: "Releases",
|
|
427
|
+
url: docUrl("/admin/releases"),
|
|
428
|
+
route: "/admin/releases",
|
|
429
|
+
markdown: "admin/releases.md",
|
|
430
|
+
keywords: ["admin", "releases"],
|
|
431
|
+
headings: ["What it generates", "Creating a release", "Release entry fields", "SDK surface", "Related pages"],
|
|
432
|
+
excerpt: "The Releases page drafts a changelog for each version from the fixes that shipped, crediting the users whose reports led to each fix."
|
|
433
|
+
},
|
|
434
|
+
{
|
|
435
|
+
title: "Repo graph",
|
|
436
|
+
url: docUrl("/admin/repo"),
|
|
437
|
+
route: "/admin/repo",
|
|
438
|
+
markdown: "admin/repo.md",
|
|
439
|
+
keywords: ["admin", "repo", "graph"],
|
|
440
|
+
headings: ["Repo header", "Branch list", "Repo activity log", "Managing the GitHub connection", "API", "Related pages"],
|
|
441
|
+
excerpt: "The Repo page shows every fix branch in your connected GitHub repository \u2014 open PRs, CI status, and what the fix worker did on each branch."
|
|
442
|
+
},
|
|
443
|
+
{
|
|
444
|
+
title: "Reports & triage",
|
|
445
|
+
url: docUrl("/admin/reports"),
|
|
446
|
+
route: "/admin/reports",
|
|
447
|
+
markdown: "admin/reports.md",
|
|
448
|
+
keywords: ["admin", "reports", "triage"],
|
|
449
|
+
headings: ["Diagnosis + Fix hero", "Compact diagnosis preview", "Merging a fix from report detail"],
|
|
450
|
+
excerpt: "The Reports list is Mushi's bug inbox \u2014 filter by status, severity, category and component, and start with the recommended action at the top."
|
|
451
|
+
},
|
|
452
|
+
{
|
|
453
|
+
title: "Research",
|
|
454
|
+
url: docUrl("/admin/research"),
|
|
455
|
+
route: "/admin/research",
|
|
456
|
+
markdown: "admin/research.md",
|
|
457
|
+
keywords: ["admin", "research"],
|
|
458
|
+
headings: ["What it's for", "Running a search", "Saving results", "Prerequisites", "Related pages"],
|
|
459
|
+
excerpt: "The Research page runs a Firecrawl web search scoped to your tech stack and returns summarised answers with links to their sources."
|
|
460
|
+
},
|
|
461
|
+
{
|
|
462
|
+
title: "Rewards program",
|
|
463
|
+
url: docUrl("/admin/rewards"),
|
|
464
|
+
route: "/admin/rewards",
|
|
465
|
+
markdown: "admin/rewards.md",
|
|
466
|
+
keywords: ["admin", "rewards", "program"],
|
|
467
|
+
headings: ["How the pipeline works", "Overview tab", "One-click recommended defaults", "Activity Rules tab", "Custom rules", "Tier Ladder tab", "Contributors tab", "Quests tab"],
|
|
468
|
+
excerpt: "Set up the Mushi rewards program from the console \u2014 points for reporter activity, the tier ladder, and the perks your app grants at each tier."
|
|
469
|
+
},
|
|
470
|
+
{
|
|
471
|
+
title: "SDK health",
|
|
472
|
+
url: docUrl("/admin/sdk-health"),
|
|
473
|
+
route: "/admin/sdk-health",
|
|
474
|
+
markdown: "admin/sdk-health.md",
|
|
475
|
+
keywords: ["admin", "sdk-health", "sdk", "health"],
|
|
476
|
+
headings: ["What it shows", "Out-of-date detection", "Setup checklist integration", "Where it lives", "Related"],
|
|
477
|
+
excerpt: "The SDK health card shows the live heartbeat of every Mushi SDK that reported in the last 24 hours, so an SDK that never shipped is obvious."
|
|
478
|
+
},
|
|
479
|
+
{
|
|
480
|
+
title: "Settings",
|
|
481
|
+
url: docUrl("/admin/settings"),
|
|
482
|
+
route: "/admin/settings",
|
|
483
|
+
markdown: "admin/settings.md",
|
|
484
|
+
keywords: ["admin", "settings"],
|
|
485
|
+
headings: ["Tabs", "General", "BYOK (API keys)", "Firecrawl", "Browserbase", "Health", "Dev tools", "Changing a key"],
|
|
486
|
+
excerpt: "The Settings page holds every project option \u2014 notifications, your own LLM keys, widget copy, Slack alerts and developer settings."
|
|
487
|
+
},
|
|
488
|
+
{
|
|
489
|
+
title: "Setup Copilot",
|
|
490
|
+
url: docUrl("/admin/setup-copilot"),
|
|
491
|
+
route: "/admin/setup-copilot",
|
|
492
|
+
markdown: "admin/setup-copilot.md",
|
|
493
|
+
keywords: ["admin", "setup-copilot", "setup", "copilot"],
|
|
494
|
+
headings: ["Related pages"],
|
|
495
|
+
excerpt: "Setup Copilot is a guided checklist that verifies SDK ingest and fix dispatch for a project, with copy-paste CLI commands for each step."
|
|
496
|
+
},
|
|
497
|
+
{
|
|
498
|
+
title: "Skill Pipelines",
|
|
499
|
+
url: docUrl("/admin/skill-pipelines"),
|
|
500
|
+
route: "/admin/skill-pipelines",
|
|
501
|
+
markdown: "admin/skill-pipelines.md",
|
|
502
|
+
keywords: ["admin", "skill-pipelines", "skill", "pipelines"],
|
|
503
|
+
headings: ["How it works", "The three tabs", "Catalog", "Pipelines", "Sources", "Execution modes", "Triage links (?skill=)", "CLI quick reference"],
|
|
504
|
+
excerpt: "Skill Pipelines recommend agent skills from the cursor-kenji and skills.sh ecosystem for each classified bug and run them during review in Mushi."
|
|
505
|
+
},
|
|
506
|
+
{
|
|
507
|
+
title: "SSO",
|
|
508
|
+
url: docUrl("/admin/sso"),
|
|
509
|
+
route: "/admin/sso",
|
|
510
|
+
markdown: "admin/sso.md",
|
|
511
|
+
keywords: ["admin", "sso"],
|
|
512
|
+
headings: ["Supported protocols", "Registering an identity provider", "Providers table", "Disconnecting a provider", "End-user login flow", "API", "Related pages"],
|
|
513
|
+
excerpt: "Set up SAML 2.0 or OIDC single sign-on for your Mushi organization so teammates log in with your company's identity provider."
|
|
514
|
+
},
|
|
515
|
+
{
|
|
516
|
+
title: "Storage",
|
|
517
|
+
url: docUrl("/admin/storage"),
|
|
518
|
+
route: "/admin/storage",
|
|
519
|
+
markdown: "admin/storage.md",
|
|
520
|
+
keywords: ["admin", "storage"],
|
|
521
|
+
headings: ["Providers", "Configuring a custom bucket", "Required IAM permissions (S3)", "Required R2 permissions", "Required GCS permissions", "Data residency", "Related pages"],
|
|
522
|
+
excerpt: "The Storage page chooses where Mushi keeps report attachments and recordings \u2014 managed storage, or your own bucket for residency or cost."
|
|
523
|
+
},
|
|
524
|
+
{
|
|
525
|
+
title: "Teams and Members",
|
|
526
|
+
url: docUrl("/admin/teams"),
|
|
527
|
+
route: "/admin/teams",
|
|
528
|
+
markdown: "admin/teams.md",
|
|
529
|
+
keywords: ["admin", "teams", "members"],
|
|
530
|
+
headings: ["Plans", "Roles", "Invitation Flow"],
|
|
531
|
+
excerpt: "How teams work in Mushi \u2014 organizations own projects, billing, invitations and the member roster, and project access comes from membership."
|
|
532
|
+
},
|
|
533
|
+
{
|
|
534
|
+
title: "Users",
|
|
535
|
+
url: docUrl("/admin/users"),
|
|
536
|
+
route: "/admin/users",
|
|
537
|
+
markdown: "admin/users.md",
|
|
538
|
+
keywords: ["admin", "users"],
|
|
539
|
+
headings: ["Metrics row", "User directory", "User detail drawer", "API", "Related pages"],
|
|
540
|
+
excerpt: "The Users page is an operator-only directory of every Mushi account with plan, MRR and churn; other visitors see a not-found page instead."
|
|
541
|
+
},
|
|
542
|
+
{
|
|
543
|
+
title: "Voice intake",
|
|
544
|
+
url: docUrl("/admin/voice"),
|
|
545
|
+
route: "/admin/voice",
|
|
546
|
+
markdown: "admin/voice.md",
|
|
547
|
+
keywords: ["admin", "voice", "intake"],
|
|
548
|
+
headings: ["Turning it on", "The four ways in", "The confirmation gate", "Getting the answer back", "Telegram", "Recent voice requests", "Limits", "Self-hosting"],
|
|
549
|
+
excerpt: "Report a bug by voice from the Mushi console \u2014 say what is wrong, confirm the transcript, and a draft pull request is started for you."
|
|
550
|
+
},
|
|
551
|
+
{
|
|
552
|
+
title: "Blog",
|
|
553
|
+
url: docUrl("/blog"),
|
|
554
|
+
route: "/blog",
|
|
555
|
+
markdown: "blog/index.md",
|
|
556
|
+
keywords: ["blog"],
|
|
557
|
+
headings: [],
|
|
558
|
+
excerpt: "Notes from building Mushi Mushi \u2014 the real numbers behind the funnel, open-source licensing, launches, and how AI-built apps get debugged in practice."
|
|
559
|
+
},
|
|
560
|
+
{
|
|
561
|
+
title: "Mushi goes AGPLv3 \u2014 the honest open source story",
|
|
562
|
+
url: docUrl("/blog/agplv3-relicense"),
|
|
563
|
+
route: "/blog/agplv3-relicense",
|
|
564
|
+
markdown: "blog/agplv3-relicense.md",
|
|
565
|
+
keywords: ["blog", "agplv3-relicense", "agplv3", "relicense", "goes", "honest", "open", "source", "story"],
|
|
566
|
+
headings: ["What we actually want", "The decision", "What this changes", 'The honest "open source" claim'],
|
|
567
|
+
excerpt: "Why Mushi Mushi relicensed its server to AGPLv3 while keeping every SDK MIT \u2014 the open-core reasoning, what changes for self-hosters, and what stays free."
|
|
568
|
+
},
|
|
569
|
+
{
|
|
570
|
+
title: '60 seconds from "this is broken" to a draft PR \u2014 building Mushi Mushi',
|
|
571
|
+
url: docUrl("/blog/auto-fix-loop"),
|
|
572
|
+
route: "/blog/auto-fix-loop",
|
|
573
|
+
markdown: "blog/auto-fix-loop.md",
|
|
574
|
+
keywords: ["blog", "auto-fix-loop", "auto", "fix", "loop", "60", "seconds", "this", "broken", "draft", "pr", "building"],
|
|
575
|
+
headings: ["The 60-second loop", "Why we kept the human in the loop", "1. The judge is good. The judge is not infallible.", "2. The user's trust is the moat.", "What's actually open source", "Try it"],
|
|
576
|
+
excerpt: "How Mushi Mushi turns a one-sentence user complaint into a classified, deduped, AI-judged draft PR, and why the human stays in the loop the whole way."
|
|
577
|
+
},
|
|
578
|
+
{
|
|
579
|
+
title: "Launch Week 1 \u2014 five features, five days",
|
|
580
|
+
url: docUrl("/blog/launch-week-1"),
|
|
581
|
+
route: "/blog/launch-week-1",
|
|
582
|
+
markdown: "blog/launch-week-1.md",
|
|
583
|
+
keywords: ["blog", "launch-week-1", "launch", "week", "five", "features", "days"],
|
|
584
|
+
headings: ["The five", "Why now", "Numbers on Friday"],
|
|
585
|
+
excerpt: "Everything Mushi Mushi shipped in Launch Week 1 \u2014 one feature a day, from bug-report capture to editor-ready fixes, with demos and setup commands."
|
|
586
|
+
},
|
|
587
|
+
{
|
|
588
|
+
title: "I shipped a bug tool for 5 months and got 9 signups. Here's what the data said.",
|
|
589
|
+
url: docUrl("/blog/nine-signups-what-the-data-said"),
|
|
590
|
+
route: "/blog/nine-signups-what-the-data-said",
|
|
591
|
+
markdown: "blog/nine-signups-what-the-data-said.md",
|
|
592
|
+
keywords: ["blog", "nine-signups-what-the-data-said", "nine", "signups", "data", "said", "shipped", "bug", "tool", "months", "got", "here"],
|
|
593
|
+
headings: ["The numbers", "Three things I found when I looked", "What the data said", "What changed", "What happens next"],
|
|
594
|
+
excerpt: "Five months after launch, Mushi Mushi had 30 npm packages, an MCP server in every registry and nine signups. What the data said, and what changed."
|
|
595
|
+
},
|
|
596
|
+
{
|
|
597
|
+
title: "Changelog",
|
|
598
|
+
url: docUrl("/changelog"),
|
|
599
|
+
route: "/changelog",
|
|
600
|
+
markdown: "changelog.md",
|
|
601
|
+
keywords: ["changelog"],
|
|
602
|
+
headings: [],
|
|
603
|
+
excerpt: "What shipped in Mushi Mushi \u2014 new SDK releases, console features, MCP tools, and fixes, in plain English with dates."
|
|
604
|
+
},
|
|
605
|
+
{
|
|
606
|
+
title: "Mushi Cloud",
|
|
607
|
+
url: docUrl("/cloud"),
|
|
608
|
+
route: "/cloud",
|
|
609
|
+
markdown: "cloud.md",
|
|
610
|
+
keywords: ["cloud"],
|
|
611
|
+
headings: ["Plans", "Sign-up flow", "Why metered on diagnoses", "Spend controls", "Self-hosting"],
|
|
612
|
+
excerpt: "Hosted Mushi Mushi \u2014 bug reports, plain-English diagnoses, and fix dispatch without running servers. Free tier includes 50 diagnoses a month."
|
|
613
|
+
},
|
|
614
|
+
{
|
|
615
|
+
title: "Compare",
|
|
616
|
+
url: docUrl("/compare"),
|
|
617
|
+
route: "/compare",
|
|
618
|
+
markdown: "compare/index.md",
|
|
619
|
+
keywords: ["compare", "sentry", "jam", "posthog"],
|
|
620
|
+
headings: ["How these pages are written"],
|
|
621
|
+
excerpt: "Honest, dated comparisons of Mushi Mushi with Sentry, Jam and PostHog, plus Sentry alternatives for solo founders. Every number links to its source."
|
|
622
|
+
},
|
|
623
|
+
{
|
|
624
|
+
title: "Jam vs Mushi Mushi",
|
|
625
|
+
url: docUrl("/compare/jam-vs-mushi"),
|
|
626
|
+
route: "/compare/jam-vs-mushi",
|
|
627
|
+
markdown: "compare/jam-vs-mushi.md",
|
|
628
|
+
keywords: ["compare", "jam-vs-mushi", "jam"],
|
|
629
|
+
headings: ["What each one is", "Side by side", "Where they overlap", "Where they do not", "Using both", "Pick Jam if", "Pick Mushi if", "Frequently asked questions"],
|
|
630
|
+
excerpt: "Jam.dev vs Mushi Mushi in 2026 \u2014 a teammate recording a bug in the Jam extension versus a user reporting from your app, MCP handoff and free tiers."
|
|
631
|
+
},
|
|
632
|
+
{
|
|
633
|
+
title: "PostHog session replay vs Mushi Mushi",
|
|
634
|
+
url: docUrl("/compare/posthog-session-replay-vs-mushi"),
|
|
635
|
+
route: "/compare/posthog-session-replay-vs-mushi",
|
|
636
|
+
markdown: "compare/posthog-session-replay-vs-mushi.md",
|
|
637
|
+
keywords: ["compare", "posthog-session-replay-vs-mushi", "posthog", "session", "replay"],
|
|
638
|
+
headings: ["What each one is", "Side by side", "They work alongside each other", "Pick PostHog if", "Pick Mushi if", "Frequently asked questions"],
|
|
639
|
+
excerpt: "PostHog session replay vs Mushi Mushi in 2026 \u2014 every session recorded versus the one a user flagged as broken, free tiers, and why teams run both."
|
|
640
|
+
},
|
|
641
|
+
{
|
|
642
|
+
title: "Sentry alternatives for solo founders",
|
|
643
|
+
url: docUrl("/compare/sentry-alternatives-for-solo-founders"),
|
|
644
|
+
route: "/compare/sentry-alternatives-for-solo-founders",
|
|
645
|
+
markdown: "compare/sentry-alternatives-for-solo-founders.md",
|
|
646
|
+
keywords: ["compare", "sentry-alternatives-for-solo-founders", "sentry", "alternatives", "solo", "founders"],
|
|
647
|
+
headings: ["The table", "How to read it", "What this page does not claim", "Frequently asked questions"],
|
|
648
|
+
excerpt: "Sentry alternatives for solo founders in 2026 \u2014 Bugsnag, Rollbar, highlight.io, PostHog and Mushi compared on free tiers, self-hosting and AI fixes."
|
|
649
|
+
},
|
|
650
|
+
{
|
|
651
|
+
title: "Sentry vs Mushi Mushi",
|
|
652
|
+
url: docUrl("/compare/sentry-vs-mushi"),
|
|
653
|
+
route: "/compare/sentry-vs-mushi",
|
|
654
|
+
markdown: "compare/sentry-vs-mushi.md",
|
|
655
|
+
keywords: ["compare", "sentry-vs-mushi", "sentry"],
|
|
656
|
+
headings: ["What each one is", "Side by side", "Where they overlap, and where they do not", "Running Mushi alongside Sentry", "Pick Sentry if", "Pick Mushi if", "Frequently asked questions"],
|
|
657
|
+
excerpt: "Sentry vs Mushi Mushi in 2026 \u2014 what each captures, Sentry's free tier and Seer pricing next to Mushi's 50 free diagnoses a month, and running both."
|
|
658
|
+
},
|
|
659
|
+
{
|
|
660
|
+
title: "Concepts",
|
|
661
|
+
url: docUrl("/concepts"),
|
|
662
|
+
route: "/concepts",
|
|
663
|
+
markdown: "concepts/index.md",
|
|
664
|
+
keywords: ["concepts"],
|
|
665
|
+
headings: ["Primary paths", "Everything else"],
|
|
666
|
+
excerpt: "Mushi's core ideas in one place \u2014 capture, plain-English diagnosis, dedupe into one row, optional agent fixes, verification, and lessons it remembers."
|
|
667
|
+
},
|
|
668
|
+
{
|
|
669
|
+
title: "Agent contract",
|
|
670
|
+
url: docUrl("/concepts/agent-contract"),
|
|
671
|
+
route: "/concepts/agent-contract",
|
|
672
|
+
markdown: "concepts/agent-contract.md",
|
|
673
|
+
keywords: ["concepts", "agent-contract", "agent", "contract"],
|
|
674
|
+
headings: ["The evolution loop (what you're plugging into)", "What the agent reads before opening a PR", "What the agent does after merging the PR", "What Mushi commits to in return", "Self-bootstrap (Claude Code and similar)", "Privacy contract", "Scopes"],
|
|
675
|
+
excerpt: "What Mushi expects an agent to read before opening a PR, and what it promises in return."
|
|
676
|
+
},
|
|
677
|
+
{
|
|
678
|
+
title: "Anti-gaming & reputation",
|
|
679
|
+
url: docUrl("/concepts/anti-gaming"),
|
|
680
|
+
route: "/concepts/anti-gaming",
|
|
681
|
+
markdown: "concepts/anti-gaming.md",
|
|
682
|
+
keywords: ["concepts", "anti-gaming", "anti", "gaming", "reputation"],
|
|
683
|
+
headings: ["Reporter reputation", "Anti-gaming detection", "Report fatigue prevention"],
|
|
684
|
+
excerpt: "How Mushi keeps bug reports honest \u2014 reporter reputation against spam and point farming, and prompt-fatigue limits so real users are not nagged."
|
|
685
|
+
},
|
|
686
|
+
{
|
|
687
|
+
title: "Architecture",
|
|
688
|
+
url: docUrl("/concepts/architecture"),
|
|
689
|
+
route: "/concepts/architecture",
|
|
690
|
+
markdown: "concepts/architecture.md",
|
|
691
|
+
keywords: ["concepts", "architecture"],
|
|
692
|
+
headings: ["Wire-level flow", "Component summary", "MCP surface (2026)", "Connect & Update (console)", "Knowledge graph", "A2A Agent Card", "Data residency", "Storage"],
|
|
693
|
+
excerpt: "How Mushi is built \u2014 a Hono gateway in front of Supabase, the edge functions behind it, and the relational, vector and graph stores that hold each report."
|
|
694
|
+
},
|
|
695
|
+
{
|
|
696
|
+
title: "Mushi Bounties \u2014 crowd-testing marketplace",
|
|
697
|
+
url: docUrl("/concepts/bounty-marketplace"),
|
|
698
|
+
route: "/concepts/bounty-marketplace",
|
|
699
|
+
markdown: "concepts/bounty-marketplace.md",
|
|
700
|
+
keywords: ["concepts", "bounty-marketplace", "bounty", "marketplace", "bounties", "crowd-testing"],
|
|
701
|
+
headings: ["How it works", "Publish your app", "Testers find your app", "Testers submit bugs", "You review and grade", "Testers redeem", "Reward model and legal posture", "Anti-gaming and reputation"],
|
|
702
|
+
excerpt: "Mushi Bounties is a crowd-testing marketplace \u2014 publish your app, and real testers file bug reports and earn points they can redeem for rewards."
|
|
703
|
+
},
|
|
704
|
+
{
|
|
705
|
+
title: "How Mushi reads a bug report",
|
|
706
|
+
url: docUrl("/concepts/classification"),
|
|
707
|
+
route: "/concepts/classification",
|
|
708
|
+
markdown: "concepts/classification.md",
|
|
709
|
+
keywords: ["concepts", "classification", "reads", "bug", "report"],
|
|
710
|
+
headings: ["Outputs", "Vision air-gap", "BYOK"],
|
|
711
|
+
excerpt: "How Mushi turns a raw bug report and screenshot into a plain-English diagnosis \u2014 the fast filter, the text and vision stages, and what gets stored."
|
|
712
|
+
},
|
|
713
|
+
{
|
|
714
|
+
title: "Closed-loop evolution \u2014 the thesis",
|
|
715
|
+
url: docUrl("/concepts/closed-loop"),
|
|
716
|
+
route: "/concepts/closed-loop",
|
|
717
|
+
markdown: "concepts/closed-loop.md",
|
|
718
|
+
keywords: ["concepts", "closed-loop", "closed", "loop", "evolution", "thesis"],
|
|
719
|
+
headings: ["The NTSB moment software is missing", "Why the linear SDLC is the wrong unit", "Cumulative selection: why it works", "What the loop looks like in practice", "What this means for the roadmap"],
|
|
720
|
+
excerpt: "The thesis behind Mushi \u2014 why AI-built apps need a closed loop from what users feel to a verified fix, and how capture, diagnosis and fixes connect."
|
|
721
|
+
},
|
|
722
|
+
{
|
|
723
|
+
title: "Project ID & API keys",
|
|
724
|
+
url: docUrl("/concepts/credentials"),
|
|
725
|
+
route: "/concepts/credentials",
|
|
726
|
+
markdown: "concepts/credentials.md",
|
|
727
|
+
keywords: ["concepts", "credentials", "project", "id", "api", "keys"],
|
|
728
|
+
headings: ["API key scopes", "Project ID", "API key", "API endpoint", "Environment variable names"],
|
|
729
|
+
excerpt: "Where to find your Mushi project ID and API key, what the report:write and mcp scopes allow, the one mushi_ key format, and env var names per framework."
|
|
730
|
+
},
|
|
731
|
+
{
|
|
732
|
+
title: "API error catalog",
|
|
733
|
+
url: docUrl("/concepts/error-catalog"),
|
|
734
|
+
route: "/concepts/error-catalog",
|
|
735
|
+
markdown: "concepts/error-catalog.md",
|
|
736
|
+
keywords: ["concepts", "error-catalog", "error", "catalog", "api"],
|
|
737
|
+
headings: ["Why codes exist", "Codes you will see often", "Console feedback", "SDK reporters", "Drift gate"],
|
|
738
|
+
excerpt: "Stable error codes returned by the Mushi API envelope, how to read them in the console, and how they correlate with Sentry and Langfuse."
|
|
739
|
+
},
|
|
740
|
+
{
|
|
741
|
+
title: "The evolution loop \u2014 how Mushi closes the cycle",
|
|
742
|
+
url: docUrl("/concepts/evolution-loop"),
|
|
743
|
+
route: "/concepts/evolution-loop",
|
|
744
|
+
markdown: "concepts/evolution-loop.md",
|
|
745
|
+
keywords: ["concepts", "evolution-loop", "evolution", "loop", "closes", "cycle"],
|
|
746
|
+
headings: ["Why vibe coding needs a closing loop", "The five stages", "\u2460 Capture", "\u2461 Classify", "\u2462 Fix", "\u2463 Verify", "\u2464 Remember", "How to see the loop close"],
|
|
747
|
+
excerpt: "The five stages that turn a user-felt bug into a merged fix and a lesson rule \u2014 the loop that improves your app without Jira, QA teams or PM bottlenecks."
|
|
748
|
+
},
|
|
749
|
+
{
|
|
750
|
+
title: "Fix drafts & PRs",
|
|
751
|
+
url: docUrl("/concepts/fix-orchestrator"),
|
|
752
|
+
route: "/concepts/fix-orchestrator",
|
|
753
|
+
markdown: "concepts/fix-orchestrator.md",
|
|
754
|
+
keywords: ["concepts", "fix-orchestrator", "fix", "orchestrator", "drafts", "prs"],
|
|
755
|
+
headings: ["Flow", "Sandbox providers", "Agent contract", "Credential scoping", "Spec traceability \u2014 inventoryAction end-to-end", "What the worker now does", "expectedoutcome shape", "Carrying the anchor through external surfaces"],
|
|
756
|
+
excerpt: "What happens when you dispatch a fix \u2014 Mushi runs a coding agent in a sandbox and opens a draft pull request with a rationale and CI status to review."
|
|
757
|
+
},
|
|
758
|
+
{
|
|
759
|
+
title: "Inventory and gates (v2)",
|
|
760
|
+
url: docUrl("/concepts/inventory-and-gates"),
|
|
761
|
+
route: "/concepts/inventory-and-gates",
|
|
762
|
+
markdown: "concepts/inventory-and-gates.md",
|
|
763
|
+
keywords: ["concepts", "inventory-and-gates", "inventory", "gates", "v2"],
|
|
764
|
+
headings: ["The model", "Five gates", "Discovery \u2014 the SDK proposes the inventory", "Synthetic monitor", "Surface mode in the graph", "Spec traceability \u2014 read AND write side", "Where each link lives", "Related packages"],
|
|
765
|
+
excerpt: "Mushi v2's inventory.yaml lists every page, story and action in your app, and five composite gates fail the build when a change breaks that contract."
|
|
766
|
+
},
|
|
767
|
+
{
|
|
768
|
+
title: "Judge & self-improvement",
|
|
769
|
+
url: docUrl("/concepts/judge-loop"),
|
|
770
|
+
route: "/concepts/judge-loop",
|
|
771
|
+
markdown: "concepts/judge-loop.md",
|
|
772
|
+
keywords: ["concepts", "judge-loop", "judge", "loop", "self-improvement"],
|
|
773
|
+
headings: ["1. Judge", "2. Prompt A/B testing", "3. Fine-tune export", "4. Drift detection", "See also"],
|
|
774
|
+
excerpt: "How Mushi's classifier improves itself \u2014 a nightly judge scores yesterday's reads, prompts are A/B tested, and drift is caught before it reaches you."
|
|
775
|
+
},
|
|
776
|
+
{
|
|
777
|
+
title: "Knowledge graph",
|
|
778
|
+
url: docUrl("/concepts/knowledge-graph"),
|
|
779
|
+
route: "/concepts/knowledge-graph",
|
|
780
|
+
markdown: "concepts/knowledge-graph.md",
|
|
781
|
+
keywords: ["concepts", "knowledge-graph", "knowledge", "graph"],
|
|
782
|
+
headings: ["Storage", "Why both pgvector and AGE?", "Indexing", "Bug Ontology"],
|
|
783
|
+
excerpt: "How Mushi links reports, components, fixes and developers in a per-project knowledge graph, with pgvector for similarity and Apache AGE for traversal."
|
|
784
|
+
},
|
|
785
|
+
{
|
|
786
|
+
title: "Multi-repo coordinated fixes",
|
|
787
|
+
url: docUrl("/concepts/multi-repo-fixes"),
|
|
788
|
+
route: "/concepts/multi-repo-fixes",
|
|
789
|
+
markdown: "concepts/multi-repo-fixes.md",
|
|
790
|
+
keywords: ["concepts", "multi-repo-fixes", "multi", "repo", "fixes", "multi-repo", "coordinated"],
|
|
791
|
+
headings: ["Setup", "How a coordination flows", "Cross-linking PRs", "Inventory traceability", "See also"],
|
|
792
|
+
excerpt: "When one bug spans a frontend and a backend repo, Mushi plans the fix across both and opens one cross-linked pull request per repository."
|
|
793
|
+
},
|
|
794
|
+
{
|
|
795
|
+
title: "Open source & licensing",
|
|
796
|
+
url: docUrl("/concepts/open-source"),
|
|
797
|
+
route: "/concepts/open-source",
|
|
798
|
+
markdown: "concepts/open-source.md",
|
|
799
|
+
keywords: ["concepts", "open-source", "open", "source", "licensing"],
|
|
800
|
+
headings: ["Open-core, one repo", "Why MIT for the SDK?", "Why AGPLv3 for the server?", "No lock-in", "What's paid and what's free", "Run it yourself", "Trademarks"],
|
|
801
|
+
excerpt: "What is open source in Mushi \u2014 MIT SDKs you can embed anywhere, an AGPLv3 server you can self-host, and the small commercial enterprise edition."
|
|
802
|
+
},
|
|
803
|
+
{
|
|
804
|
+
title: "Connecting your orchestrator (MCP / A2A / REST / AG-UI)",
|
|
805
|
+
url: docUrl("/concepts/orchestrator-interop"),
|
|
806
|
+
route: "/concepts/orchestrator-interop",
|
|
807
|
+
markdown: "concepts/orchestrator-interop.md",
|
|
808
|
+
keywords: ["concepts", "orchestrator-interop", "orchestrator", "interop", "connecting", "mcp", "a2a", "rest", "ag-ui"],
|
|
809
|
+
headings: ["Discovery", "Inbound surfaces", "MCP \u2014 stdio (local) AND Streamable HTTP (hosted)", "A2A v1.0.0 \u2014 Tasks resource", "Push notifications (A2A v1.0.0 PushNotificationConfig)", "REST + OpenAPI 3.1", "AG-UI v0.4 \u2014 fix dispatch SSE", "JSON Schemas"],
|
|
810
|
+
excerpt: "Connect any agent orchestrator to Mushi \u2014 MCP, A2A, REST and AG-UI surfaces for Cursor, Claude, OpenAI Agents, LangGraph, CrewAI and your own agents."
|
|
811
|
+
},
|
|
812
|
+
{
|
|
813
|
+
title: "Rewards & contributor identity",
|
|
814
|
+
url: docUrl("/concepts/rewards"),
|
|
815
|
+
route: "/concepts/rewards",
|
|
816
|
+
markdown: "concepts/rewards.md",
|
|
817
|
+
keywords: ["concepts", "rewards", "contributor", "identity"],
|
|
818
|
+
headings: ["Design principles", "Data model", "Server-side award flow", "SDK-side flow", "Identity verification (P2 \u2014 monetary awards)", "Tier multipliers", "Anti-gaming integration", "Webhooks reference"],
|
|
819
|
+
excerpt: "How Mushi's rewards program works \u2014 reporters earn points for reports and triage, climb project-defined tiers, and get perks from your app via webhooks."
|
|
820
|
+
},
|
|
821
|
+
{
|
|
822
|
+
title: "Runtime config",
|
|
823
|
+
url: docUrl("/concepts/runtime-config"),
|
|
824
|
+
route: "/concepts/runtime-config",
|
|
825
|
+
markdown: "concepts/runtime-config.md",
|
|
826
|
+
keywords: ["concepts", "runtime-config", "runtime", "config"],
|
|
827
|
+
headings: ["Setup default", "When host init wins", "Console tuning without rebuild", "Widget draft persistence", "Troubleshooting", "Related"],
|
|
828
|
+
excerpt: "How the Mushi SDK fetches widget and capture settings from the console at startup, how they merge with your init options, and when to turn it off."
|
|
829
|
+
},
|
|
830
|
+
{
|
|
831
|
+
title: "Where the report button lives",
|
|
832
|
+
url: docUrl("/concepts/trigger-modes"),
|
|
833
|
+
route: "/concepts/trigger-modes",
|
|
834
|
+
markdown: "concepts/trigger-modes.md",
|
|
835
|
+
keywords: ["concepts", "trigger-modes", "trigger", "modes", "where", "report", "button", "lives"],
|
|
836
|
+
headings: ["Choosing a Mode", "Bring Your Own Button", "Smart Hide", "Native SDKs"],
|
|
837
|
+
excerpt: "Choose where Mushi's bug-report button lives \u2014 corner stamp, edge tab, banner, your own button via attach, or fully manual \u2014 on web and mobile."
|
|
838
|
+
},
|
|
839
|
+
{
|
|
840
|
+
title: "Cursor integration",
|
|
841
|
+
url: docUrl("/integrations/cursor"),
|
|
842
|
+
route: "/integrations/cursor",
|
|
843
|
+
markdown: "integrations/cursor.md",
|
|
844
|
+
keywords: ["integrations", "cursor", "integration"],
|
|
845
|
+
headings: ["Step-by-step", "Log in", "Wire Cursor", "(Optional) Add the evolution-loop coding rules", "Restart Cursor", "Verify", "Available tools in Cursor", "Keep lessons current"],
|
|
846
|
+
excerpt: "Wire Mushi's evolution loop into Cursor with one command \u2014 get fix context, lessons, and dispatch right from the editor."
|
|
847
|
+
},
|
|
848
|
+
{
|
|
849
|
+
title: "Voice intake",
|
|
850
|
+
url: docUrl("/integrations/voice-intake"),
|
|
851
|
+
route: "/integrations/voice-intake",
|
|
852
|
+
markdown: "integrations/voice-intake.md",
|
|
853
|
+
keywords: ["integrations", "voice-intake", "voice", "intake"],
|
|
854
|
+
headings: ["Enable it", "Turn it on", "Mint a voice:write key", "Make sure Autofix is on", "iOS Shortcut", "Telegram (the Android inbox)", "Create a bot", "Connect it"],
|
|
855
|
+
excerpt: "Say the bug on your phone, get a draft PR back. iOS Shortcut, Telegram, Slack, or the installed console \u2014 one pipeline, one confirmation gate, no audio kept."
|
|
856
|
+
},
|
|
857
|
+
{
|
|
858
|
+
title: "Launch Week",
|
|
859
|
+
url: docUrl("/launch-week"),
|
|
860
|
+
route: "/launch-week",
|
|
861
|
+
markdown: "launch-week.md",
|
|
862
|
+
keywords: ["launch-week", "launch", "week"],
|
|
863
|
+
headings: ["What shipped in June 2026", "How we launch now: one post per release", "The gate before R1", "What we will not do", "Want to be notified?", "Run your own"],
|
|
864
|
+
excerpt: "How Mushi Mushi launches now \u2014 one public post per release (Show HN, then Product Hunt), the gate each release must pass, and what we publish after."
|
|
865
|
+
},
|
|
866
|
+
{
|
|
867
|
+
title: "Legal",
|
|
868
|
+
url: docUrl("/legal"),
|
|
869
|
+
route: "/legal",
|
|
870
|
+
markdown: "legal/index.md",
|
|
871
|
+
keywords: ["legal"],
|
|
872
|
+
headings: [],
|
|
873
|
+
excerpt: "Privacy policy and terms of service for Mushi Mushi Cloud, the SDKs, and the tester program."
|
|
874
|
+
},
|
|
875
|
+
{
|
|
876
|
+
title: "Privacy Policy",
|
|
877
|
+
url: docUrl("/legal/privacy"),
|
|
878
|
+
route: "/legal/privacy",
|
|
879
|
+
markdown: "legal/privacy.md",
|
|
880
|
+
keywords: ["legal", "privacy", "policy"],
|
|
881
|
+
headings: ["1. Who is responsible", "2. What data we handle, in one table", "3. Data where Mushi is the controller", "4. Data where Mushi is the processor", "5. Sub-processors", "6. International transfers and residency", "7. Retention", "8. Your rights and how to use them"],
|
|
882
|
+
excerpt: "What Mushi Mushi collects, why, who processes it, how long it is kept, and how to exercise your rights under GDPR and Japan's APPI."
|
|
883
|
+
},
|
|
884
|
+
{
|
|
885
|
+
title: "Terms of Service",
|
|
886
|
+
url: docUrl("/legal/terms"),
|
|
887
|
+
route: "/legal/terms",
|
|
888
|
+
markdown: "legal/terms.md",
|
|
889
|
+
keywords: ["legal", "terms", "service"],
|
|
890
|
+
headings: ["1. Acceptance", "2. The Service", "3. Software licenses", "4. Accounts", "5. Acceptable use", "6. Your data and the license you give us", "7. Free tier", "8. Paid plans and billing"],
|
|
891
|
+
excerpt: "The terms for Mushi Cloud, the admin console, the SDKs and the Bounties tester marketplace \u2014 plans, limits, AI-output disclaimer, licenses and liability."
|
|
892
|
+
},
|
|
893
|
+
{
|
|
894
|
+
title: "Migration guides",
|
|
895
|
+
url: docUrl("/migrations"),
|
|
896
|
+
route: "/migrations",
|
|
897
|
+
markdown: "migrations/index.md",
|
|
898
|
+
keywords: ["migrations", "migration", "guides"],
|
|
899
|
+
headings: ["Don't see your migration?"],
|
|
900
|
+
excerpt: "Step-by-step guides for moving an app onto Mushi from Sentry, Instabug, Shake, LogRocket, BugHerd or Pendo, or between React, Vue and mobile runtimes."
|
|
901
|
+
},
|
|
902
|
+
{
|
|
903
|
+
title: "BugHerd \u2192 Mushi",
|
|
904
|
+
url: docUrl("/migrations/bugherd-to-mushi"),
|
|
905
|
+
route: "/migrations/bugherd-to-mushi",
|
|
906
|
+
markdown: "migrations/bugherd-to-mushi.md",
|
|
907
|
+
keywords: ["migrations", "bugherd-to-mushi", "bugherd"],
|
|
908
|
+
headings: ["Why switch", "API mapping", "Before / After", "Migration checklist", "Feature parity", "References"],
|
|
909
|
+
excerpt: "Move from BugHerd to Mushi \u2014 map pin-to-element feedback onto Mushi's element selector, screenshot and metadata capture, with an interactive checklist."
|
|
910
|
+
},
|
|
911
|
+
{
|
|
912
|
+
title: "Migration: Capacitor \u2192 React Native",
|
|
913
|
+
url: docUrl("/migrations/capacitor-to-react-native"),
|
|
914
|
+
route: "/migrations/capacitor-to-react-native",
|
|
915
|
+
markdown: "migrations/capacitor-to-react-native.md",
|
|
916
|
+
keywords: ["migrations", "capacitor-to-react-native", "capacitor", "react", "native", "migration"],
|
|
917
|
+
headings: ["TL;DR", "Web strategy", "Mushi Mushi during the migration", "API mapping at a glance", "Before / after", "Get the snippet from the admin console", "Or use the CLI", "What's covered in the full guide"],
|
|
918
|
+
excerpt: "Port an Ionic or Capacitor app to React Native \u2014 a full plan with a Mushi API map and CI/CD recipes for Expo EAS or React Native CLI with Fastlane."
|
|
919
|
+
},
|
|
920
|
+
{
|
|
921
|
+
title: "Cordova \u2192 Capacitor",
|
|
922
|
+
url: docUrl("/migrations/cordova-to-capacitor"),
|
|
923
|
+
route: "/migrations/cordova-to-capacitor",
|
|
924
|
+
markdown: "migrations/cordova-to-capacitor.md",
|
|
925
|
+
keywords: ["migrations", "cordova-to-capacitor", "cordova", "capacitor"],
|
|
926
|
+
headings: ["Why migrate", "Prerequisites", "API mapping (Cordova plugins \u2192 Capacitor)", "Migration checklist", "Common gotchas", "Rollback", "Related guides", "References"],
|
|
927
|
+
excerpt: "Migrate a Cordova app to Capacitor in place \u2014 same web code, a new native shell \u2014 and re-mount Mushi bug reporting with @mushi-mushi/capacitor."
|
|
928
|
+
},
|
|
929
|
+
{
|
|
930
|
+
title: "Cordova \u2192 React Native",
|
|
931
|
+
url: docUrl("/migrations/cordova-to-react-native"),
|
|
932
|
+
route: "/migrations/cordova-to-react-native",
|
|
933
|
+
markdown: "migrations/cordova-to-react-native.md",
|
|
934
|
+
keywords: ["migrations", "cordova-to-react-native", "cordova", "react", "native"],
|
|
935
|
+
headings: ["Why migrate", "Two paths", "Path A: Cordova \u2192 Capacitor \u2192 React Native (recommended)", "Path B: Direct Cordova \u2192 React Native", "Mushi during the migration", "Migration checklist (Path A)", "Migration checklist (Path B \u2014 direct)", "Verification"],
|
|
936
|
+
excerpt: "Move a Cordova app to React Native in two hops \u2014 stabilise on Capacitor first, then port screen by screen \u2014 without losing Mushi bug reporting."
|
|
937
|
+
},
|
|
938
|
+
{
|
|
939
|
+
title: "Create React App \u2192 Vite",
|
|
940
|
+
url: docUrl("/migrations/cra-to-vite"),
|
|
941
|
+
route: "/migrations/cra-to-vite",
|
|
942
|
+
markdown: "migrations/cra-to-vite.md",
|
|
943
|
+
keywords: ["migrations", "cra-to-vite", "cra", "vite", "create", "react", "app"],
|
|
944
|
+
headings: ["Prerequisites", "Migration checklist", "Common gotchas", "Rollback", "Mushi compatibility", "References"],
|
|
945
|
+
excerpt: "Migrate a Create React App project to Vite \u2014 run the codemod, rename REACT_APP_ env vars to VITE_, and keep your Mushi React setup working."
|
|
946
|
+
},
|
|
947
|
+
{
|
|
948
|
+
title: "Instabug (Luciq) \u2192 Mushi",
|
|
949
|
+
url: docUrl("/migrations/instabug-to-mushi"),
|
|
950
|
+
route: "/migrations/instabug-to-mushi",
|
|
951
|
+
markdown: "migrations/instabug-to-mushi.md",
|
|
952
|
+
keywords: ["migrations", "instabug-to-mushi", "instabug", "luciq"],
|
|
953
|
+
headings: ["Why switch", "API mapping", "Before / After", "Web", "React Native", "Migration checklist", "Feature parity at a glance", "Rollback"],
|
|
954
|
+
excerpt: "Replace Instabug (Luciq) with Mushi for in-app bug reporting \u2014 config mapping, shake-to-report parity, a beta validation pass, and a checklist."
|
|
955
|
+
},
|
|
956
|
+
{
|
|
957
|
+
title: "LogRocket Feedback \u2192 Mushi",
|
|
958
|
+
url: docUrl("/migrations/logrocket-feedback-to-mushi"),
|
|
959
|
+
route: "/migrations/logrocket-feedback-to-mushi",
|
|
960
|
+
markdown: "migrations/logrocket-feedback-to-mushi.md",
|
|
961
|
+
keywords: ["migrations", "logrocket-feedback-to-mushi", "logrocket", "feedback"],
|
|
962
|
+
headings: ["Why switch (the feedback piece)", "API mapping", "Linking LogRocket session URLs into Mushi reports", "Migration checklist", "Feature parity (feedback only \u2014 replay is its own product)", "References"],
|
|
963
|
+
excerpt: "Move LogRocket's feedback widget to Mushi while keeping LogRocket for session replay \u2014 both SDKs run side by side, with a step-by-step checklist."
|
|
964
|
+
},
|
|
965
|
+
{
|
|
966
|
+
title: "@mushi-mushi/* upgrades",
|
|
967
|
+
url: docUrl("/migrations/mushi-sdk-upgrade"),
|
|
968
|
+
route: "/migrations/mushi-sdk-upgrade",
|
|
969
|
+
markdown: "migrations/mushi-sdk-upgrade.md",
|
|
970
|
+
keywords: ["migrations", "mushi-sdk-upgrade", "sdk", "upgrade", "@mushi-mushi", "upgrades"],
|
|
971
|
+
headings: ["What stays stable", "Migration checklist", "Current package tracks (workspace snapshot)"],
|
|
972
|
+
excerpt: "Upgrade @mushi-mushi packages to the current 1.x releases \u2014 breaking changes by package, the codemod, and how to check which versions you run."
|
|
973
|
+
},
|
|
974
|
+
{
|
|
975
|
+
title: "Native iOS / Android \u2192 Hybrid",
|
|
976
|
+
url: docUrl("/migrations/native-to-hybrid"),
|
|
977
|
+
route: "/migrations/native-to-hybrid",
|
|
978
|
+
markdown: "migrations/native-to-hybrid.md",
|
|
979
|
+
keywords: ["migrations", "native-to-hybrid", "native", "hybrid", "ios", "android"],
|
|
980
|
+
headings: ["Should you migrate?", "Pick a target shell", "Migration checklist", "Mushi during the migration", "Verification", "References"],
|
|
981
|
+
excerpt: "Should you wrap a native iOS or Android app in Capacitor or React Native? When it pays off, how to do it, and where Mushi's SDKs fit either way."
|
|
982
|
+
},
|
|
983
|
+
{
|
|
984
|
+
title: "Next.js Pages \u2192 App Router",
|
|
985
|
+
url: docUrl("/migrations/nextjs-pages-to-app-router"),
|
|
986
|
+
route: "/migrations/nextjs-pages-to-app-router",
|
|
987
|
+
markdown: "migrations/nextjs-pages-to-app-router.md",
|
|
988
|
+
keywords: ["migrations", "nextjs-pages-to-app-router", "nextjs", "pages", "app", "router", "next.js"],
|
|
989
|
+
headings: ["API mapping (Pages \u2192 App)", "Migration checklist", "Where to put (the most asked question)", "Server Components and useMushi()", "Common gotchas", "References"],
|
|
990
|
+
excerpt: "Move a Next.js app from the Pages Router to the App Router step by step \u2014 where to mount Mushi, CSP for static export, and client component boundaries."
|
|
991
|
+
},
|
|
992
|
+
{
|
|
993
|
+
title: "Pendo Feedback \u2192 Mushi",
|
|
994
|
+
url: docUrl("/migrations/pendo-feedback-to-mushi"),
|
|
995
|
+
route: "/migrations/pendo-feedback-to-mushi",
|
|
996
|
+
markdown: "migrations/pendo-feedback-to-mushi.md",
|
|
997
|
+
keywords: ["migrations", "pendo-feedback-to-mushi", "pendo", "feedback"],
|
|
998
|
+
headings: ["Why switch (the feedback piece)", "API mapping", "Recipe: keep Pendo for analytics + guides; use Mushi for feedback", "Migration checklist", "Feature parity", "References"],
|
|
999
|
+
excerpt: "Swap Pendo's feedback module for Mushi while keeping Pendo for analytics, guides and NPS \u2014 both share user identity through one identify call."
|
|
1000
|
+
},
|
|
1001
|
+
{
|
|
1002
|
+
title: "React Native CLI \u2194 Expo",
|
|
1003
|
+
url: docUrl("/migrations/react-native-cli-to-expo"),
|
|
1004
|
+
route: "/migrations/react-native-cli-to-expo",
|
|
1005
|
+
markdown: "migrations/react-native-cli-to-expo.md",
|
|
1006
|
+
keywords: ["migrations", "react-native-cli-to-expo", "react", "native", "cli", "expo"],
|
|
1007
|
+
headings: ["Direction A: React Native CLI \u2192 Expo (managed-with-dev-client)", "Why", "Migration checklist", "Direction B: Expo \u2192 bare React Native CLI (expo prebuild)", "Hybrid (the third option, often the right one)", "Verification", "References"],
|
|
1008
|
+
excerpt: "Move between React Native CLI and Expo in either direction \u2014 EAS Build and OTA updates, or expo prebuild for full native control \u2014 with Mushi intact."
|
|
1009
|
+
},
|
|
1010
|
+
{
|
|
1011
|
+
title: "Sentry + Mushi (enrich or standalone)",
|
|
1012
|
+
url: docUrl("/migrations/sentry-to-mushi"),
|
|
1013
|
+
route: "/migrations/sentry-to-mushi",
|
|
1014
|
+
markdown: "migrations/sentry-to-mushi.md",
|
|
1015
|
+
keywords: ["migrations", "sentry-to-mushi", "sentry", "enrich", "standalone"],
|
|
1016
|
+
headings: ["What each tool is for", "Option 1 \u2014 Enrich: run both, link events", "Option 2 \u2014 Standalone", "API mapping (for code you already have)", "Migration checklist", "When would you actually drop Sentry?", "References"],
|
|
1017
|
+
excerpt: "Add Mushi alongside Sentry or run it on its own \u2014 three setups, the Sentry webhook for errors, and user bug reports with plain-English diagnoses."
|
|
1018
|
+
},
|
|
1019
|
+
{
|
|
1020
|
+
title: "Shake \u2192 Mushi",
|
|
1021
|
+
url: docUrl("/migrations/shake-to-mushi"),
|
|
1022
|
+
route: "/migrations/shake-to-mushi",
|
|
1023
|
+
markdown: "migrations/shake-to-mushi.md",
|
|
1024
|
+
keywords: ["migrations", "shake-to-mushi", "shake"],
|
|
1025
|
+
headings: ["Why switch", "API mapping", "Before / After", "React Native", "Web", "Migration checklist", "Feature parity", "References"],
|
|
1026
|
+
excerpt: "Replace the Shake bug-reporting SDK with Mushi and keep the same shake-to-report experience \u2014 config mapping, identity, and a cut-over checklist."
|
|
1027
|
+
},
|
|
1028
|
+
{
|
|
1029
|
+
title: "SPA \u2192 SSR (Next.js / Nuxt / SvelteKit)",
|
|
1030
|
+
url: docUrl("/migrations/spa-to-ssr"),
|
|
1031
|
+
route: "/migrations/spa-to-ssr",
|
|
1032
|
+
markdown: "migrations/spa-to-ssr.md",
|
|
1033
|
+
keywords: ["migrations", "spa-to-ssr", "spa", "ssr", "next.js", "nuxt", "sveltekit"],
|
|
1034
|
+
headings: ["Env-var prefix mapping", "Migration checklist (generic, picks the right framework as you go)", "Where the Mushi widget lives across SSR vs CSR", "Common gotchas", "References"],
|
|
1035
|
+
excerpt: "Move a Vite SPA to Next.js, Nuxt or SvelteKit \u2014 env var prefix changes, where to mount Mushi so it survives navigation, and hydration gotchas."
|
|
1036
|
+
},
|
|
1037
|
+
{
|
|
1038
|
+
title: "Vue 2 \u2192 Vue 3",
|
|
1039
|
+
url: docUrl("/migrations/vue-2-to-vue-3"),
|
|
1040
|
+
route: "/migrations/vue-2-to-vue-3",
|
|
1041
|
+
markdown: "migrations/vue-2-to-vue-3.md",
|
|
1042
|
+
keywords: ["migrations", "vue-2-to-vue-3", "vue"],
|
|
1043
|
+
headings: ["What changes (high level)", "API mapping (Mushi-specific)", "Migration checklist", "Common gotchas", "Mushi behaviour during the migration", "References"],
|
|
1044
|
+
excerpt: "Upgrade a Vue 2 app to Vue 3 and move from the vanilla Mushi web SDK to the @mushi-mushi/vue plugin, with an interactive migration checklist."
|
|
1045
|
+
},
|
|
1046
|
+
{
|
|
1047
|
+
title: "Operating (maintainers)",
|
|
1048
|
+
url: docUrl("/operating"),
|
|
1049
|
+
route: "/operating",
|
|
1050
|
+
markdown: "operating/index.md",
|
|
1051
|
+
keywords: ["operating", "maintainers"],
|
|
1052
|
+
headings: ["What lives here", "The four independent deploy paths"],
|
|
1053
|
+
excerpt: "Runbooks for maintainers running Mushi Cloud or publishing the open-source packages \u2014 deployment, releases and status. Not needed to use the SDK."
|
|
1054
|
+
},
|
|
1055
|
+
{
|
|
1056
|
+
title: "Deployment & releases",
|
|
1057
|
+
url: docUrl("/operating/deployment"),
|
|
1058
|
+
route: "/operating/deployment",
|
|
1059
|
+
markdown: "operating/deployment.md",
|
|
1060
|
+
keywords: ["operating", "deployment", "releases"],
|
|
1061
|
+
headings: ["Four independent pipelines", "npm SDK release (Changesets + OIDC)", "Add a changeset on your PR", "Merge to master", "Merge the version PR", "Dispatch the release if it does not auto-start", "Edge Functions, admin & docs", "Database migrations"],
|
|
1062
|
+
excerpt: "How Mushi's maintainers ship the SDKs, edge functions and docs \u2014 a public summary of the release process. App developers never need to run any of it."
|
|
1063
|
+
},
|
|
1064
|
+
{
|
|
1065
|
+
title: "Status & Uptime",
|
|
1066
|
+
url: docUrl("/operating/status"),
|
|
1067
|
+
route: "/operating/status",
|
|
1068
|
+
markdown: "operating/status.md",
|
|
1069
|
+
keywords: ["operating", "status", "uptime"],
|
|
1070
|
+
headings: ["What we monitor", "Uptime commitment", "Incident communication", "Reporting availability issues"],
|
|
1071
|
+
excerpt: "Mushi Cloud status and uptime \u2014 the live status page for the API, hosted MCP, console and docs, what is monitored, and the uptime commitment per plan."
|
|
1072
|
+
},
|
|
1073
|
+
{
|
|
1074
|
+
title: "Plugin marketplace",
|
|
1075
|
+
url: docUrl("/plugins"),
|
|
1076
|
+
route: "/plugins",
|
|
1077
|
+
markdown: "plugins/index.md",
|
|
1078
|
+
keywords: ["plugins", "plugin", "marketplace"],
|
|
1079
|
+
headings: ["Available plugins", "Webhook delivery contract", "Build your own"],
|
|
1080
|
+
excerpt: "Send Mushi events to the tools you already use through HMAC-signed Standard Webhooks \u2014 12 first-party plugins and an SDK to build your own."
|
|
1081
|
+
},
|
|
1082
|
+
{
|
|
1083
|
+
title: "Bugsnag",
|
|
1084
|
+
url: docUrl("/plugins/bugsnag"),
|
|
1085
|
+
route: "/plugins/bugsnag",
|
|
1086
|
+
markdown: "plugins/bugsnag.md",
|
|
1087
|
+
keywords: ["plugins", "bugsnag"],
|
|
1088
|
+
headings: ["Setup", "Package", "Events"],
|
|
1089
|
+
excerpt: "Mirror Mushi bug reports into Bugsnag and close the Bugsnag error when the fix PR merges \u2014 install from the marketplace and map release stages."
|
|
1090
|
+
},
|
|
1091
|
+
{
|
|
1092
|
+
title: "Building a plugin",
|
|
1093
|
+
url: docUrl("/plugins/building"),
|
|
1094
|
+
route: "/plugins/building",
|
|
1095
|
+
markdown: "plugins/building.md",
|
|
1096
|
+
keywords: ["plugins", "building", "plugin"],
|
|
1097
|
+
headings: ["Minimal receiver", "Typed event shapes", "Signature verification", "Publishing to the marketplace", "Local testing"],
|
|
1098
|
+
excerpt: "Build your own Mushi plugin \u2014 an HTTP endpoint that receives HMAC-signed webhook events, verified with @mushi-mushi/plugin-sdk in a few lines."
|
|
1099
|
+
},
|
|
1100
|
+
{
|
|
1101
|
+
title: "Crashlytics",
|
|
1102
|
+
url: docUrl("/plugins/crashlytics"),
|
|
1103
|
+
route: "/plugins/crashlytics",
|
|
1104
|
+
markdown: "plugins/crashlytics.md",
|
|
1105
|
+
keywords: ["plugins", "crashlytics"],
|
|
1106
|
+
headings: ["Setup", "Package", "Events"],
|
|
1107
|
+
excerpt: "Push Mushi mobile bug reports into Firebase Crashlytics issues and close them when fixes merge \u2014 service account setup and app ID mapping."
|
|
1108
|
+
},
|
|
1109
|
+
{
|
|
1110
|
+
title: "Cursor Cloud Agent",
|
|
1111
|
+
url: docUrl("/plugins/cursor-cloud"),
|
|
1112
|
+
route: "/plugins/cursor-cloud",
|
|
1113
|
+
markdown: "plugins/cursor-cloud.md",
|
|
1114
|
+
keywords: ["plugins", "cursor-cloud", "cursor", "cloud", "agent"],
|
|
1115
|
+
headings: ["Prerequisites", "Setup", "Behaviour", "Fixes view integration", "Common tasks", "Limiting dispatch to critical reports only", "Triggering manually", "Reviewing what the agent did"],
|
|
1116
|
+
excerpt: "Dispatch a Cursor Cloud Agent automatically when a Mushi report qualifies \u2014 it investigates, drafts a fix and opens a PR that links back to Mushi."
|
|
1117
|
+
},
|
|
1118
|
+
{
|
|
1119
|
+
title: "Discord",
|
|
1120
|
+
url: docUrl("/plugins/discord"),
|
|
1121
|
+
route: "/plugins/discord",
|
|
1122
|
+
markdown: "plugins/discord.md",
|
|
1123
|
+
keywords: ["plugins", "discord"],
|
|
1124
|
+
headings: ["Setup", "Package", "Events"],
|
|
1125
|
+
excerpt: "Post Mushi report and fix events to a Discord channel through a webhook \u2014 create the webhook, install the plugin, and choose which events to send."
|
|
1126
|
+
},
|
|
1127
|
+
{
|
|
1128
|
+
title: "Webhook events",
|
|
1129
|
+
url: docUrl("/plugins/events"),
|
|
1130
|
+
route: "/plugins/events",
|
|
1131
|
+
markdown: "plugins/events.md",
|
|
1132
|
+
keywords: ["plugins", "events", "webhook"],
|
|
1133
|
+
headings: ["Event reference", "Subscribing to a subset", "Cursor Cloud Agent events"],
|
|
1134
|
+
excerpt: "Reference for every webhook event Mushi plugins receive \u2014 the shared envelope, each event's payload, and how to subscribe to only the events you need."
|
|
1135
|
+
},
|
|
1136
|
+
{
|
|
1137
|
+
title: "GitHub Issues",
|
|
1138
|
+
url: docUrl("/plugins/github-issues"),
|
|
1139
|
+
route: "/plugins/github-issues",
|
|
1140
|
+
markdown: "plugins/github-issues.md",
|
|
1141
|
+
keywords: ["plugins", "github-issues", "github", "issues"],
|
|
1142
|
+
headings: ["Setup", "Package", "Events"],
|
|
1143
|
+
excerpt: "Open a labelled GitHub issue for every classified Mushi report, with a link back to the report \u2014 target repo, default labels and assignee settings."
|
|
1144
|
+
},
|
|
1145
|
+
{
|
|
1146
|
+
title: "Jira Cloud",
|
|
1147
|
+
url: docUrl("/plugins/jira"),
|
|
1148
|
+
route: "/plugins/jira",
|
|
1149
|
+
markdown: "plugins/jira.md",
|
|
1150
|
+
keywords: ["plugins", "jira", "cloud"],
|
|
1151
|
+
headings: ["Setup", "Package", "Events"],
|
|
1152
|
+
excerpt: "Two-way sync between Mushi reports and Jira Cloud issues \u2014 create issues on classification, sync status, and comment fix summaries when fixes land."
|
|
1153
|
+
},
|
|
1154
|
+
{
|
|
1155
|
+
title: "Linear",
|
|
1156
|
+
url: docUrl("/plugins/linear"),
|
|
1157
|
+
route: "/plugins/linear",
|
|
1158
|
+
markdown: "plugins/linear.md",
|
|
1159
|
+
keywords: ["plugins", "linear"],
|
|
1160
|
+
headings: ["Setup", "Behaviour", "Label mapping example", "Troubleshooting"],
|
|
1161
|
+
excerpt: "File a Linear issue automatically when Mushi classifies a bug report \u2014 create a Linear API key, install the plugin, and choose the team and filters."
|
|
1162
|
+
},
|
|
1163
|
+
{
|
|
1164
|
+
title: "Microsoft Teams",
|
|
1165
|
+
url: docUrl("/plugins/msteams"),
|
|
1166
|
+
route: "/plugins/msteams",
|
|
1167
|
+
markdown: "plugins/msteams.md",
|
|
1168
|
+
keywords: ["plugins", "msteams", "microsoft", "teams"],
|
|
1169
|
+
headings: ["Setup", "Package", "Events"],
|
|
1170
|
+
excerpt: "Send Adaptive Cards to a Microsoft Teams channel when Mushi classifies a report or a fix lands \u2014 incoming webhook setup and severity filters."
|
|
1171
|
+
},
|
|
1172
|
+
{
|
|
1173
|
+
title: "PagerDuty",
|
|
1174
|
+
url: docUrl("/plugins/pagerduty"),
|
|
1175
|
+
route: "/plugins/pagerduty",
|
|
1176
|
+
markdown: "plugins/pagerduty.md",
|
|
1177
|
+
keywords: ["plugins", "pagerduty"],
|
|
1178
|
+
headings: ["Setup", "Behaviour", "Troubleshooting"],
|
|
1179
|
+
excerpt: "Page your on-call rotation through PagerDuty when a P0 or P1 bug report lands in Mushi \u2014 Events API v2 integration key setup and filters."
|
|
1180
|
+
},
|
|
1181
|
+
{
|
|
1182
|
+
title: "Rollbar",
|
|
1183
|
+
url: docUrl("/plugins/rollbar"),
|
|
1184
|
+
route: "/plugins/rollbar",
|
|
1185
|
+
markdown: "plugins/rollbar.md",
|
|
1186
|
+
keywords: ["plugins", "rollbar"],
|
|
1187
|
+
headings: ["Setup", "Package", "Events"],
|
|
1188
|
+
excerpt: "Mirror Mushi bug reports into Rollbar items and resolve them when fixes ship \u2014 access token, environment name, and dedup key mapping."
|
|
1189
|
+
},
|
|
1190
|
+
{
|
|
1191
|
+
title: "Sentry",
|
|
1192
|
+
url: docUrl("/plugins/sentry"),
|
|
1193
|
+
route: "/plugins/sentry",
|
|
1194
|
+
markdown: "plugins/sentry.md",
|
|
1195
|
+
keywords: ["plugins", "sentry"],
|
|
1196
|
+
headings: ["Route Sentry errors into Mushi (inbound)", "Setup (outbound plugin)", "Package", "Events"],
|
|
1197
|
+
excerpt: "Two-way Sentry integration \u2014 Sentry errors flow into Mushi's queue, merging a Mushi fix resolves the Sentry issue, and critical reports mirror back."
|
|
1198
|
+
},
|
|
1199
|
+
{
|
|
1200
|
+
title: "Slack app",
|
|
1201
|
+
url: docUrl("/plugins/slack"),
|
|
1202
|
+
route: "/plugins/slack",
|
|
1203
|
+
markdown: "plugins/slack.md",
|
|
1204
|
+
keywords: ["plugins", "slack", "app"],
|
|
1205
|
+
headings: ["Setup", "What's on the card", "Acting from the channel", "Package", "Events", "Troubleshooting"],
|
|
1206
|
+
excerpt: "Triage Mushi bug reports from Slack \u2014 each classified report posts as a card with its root cause and buttons to dispatch a fix, resolve or dismiss."
|
|
1207
|
+
},
|
|
1208
|
+
{
|
|
1209
|
+
title: "Zapier",
|
|
1210
|
+
url: docUrl("/plugins/zapier"),
|
|
1211
|
+
route: "/plugins/zapier",
|
|
1212
|
+
markdown: "plugins/zapier.md",
|
|
1213
|
+
keywords: ["plugins", "zapier"],
|
|
1214
|
+
headings: ["Setup", "Signature verification in Zapier", "Common Zaps", "Troubleshooting"],
|
|
1215
|
+
excerpt: "Send every Mushi event to Zapier and on to thousands of apps such as Notion, Google Sheets and HubSpot \u2014 Catch Hook setup and event filters."
|
|
1216
|
+
},
|
|
1217
|
+
{
|
|
1218
|
+
title: "Pricing \u2014 free tier, Pro, and self-host",
|
|
1219
|
+
url: docUrl("/pricing"),
|
|
1220
|
+
route: "/pricing",
|
|
1221
|
+
markdown: "pricing.md",
|
|
1222
|
+
keywords: ["pricing", "free", "tier", "pro", "self-host"],
|
|
1223
|
+
headings: ["Plans", "What counts as a diagnosis", "Pricing estimator", "Example bills", "Why diagnoses, not events", "BYOK and the self-host path", "Compared to Sentry + Seer", "Frequently asked questions"],
|
|
1224
|
+
excerpt: "Mushi Mushi pricing \u2014 50 AI bug diagnoses a month free, no card. Open-source Sentry alternative you can self-host for free, or upgrade for teams."
|
|
1225
|
+
},
|
|
1226
|
+
{
|
|
1227
|
+
title: "Quickstart \u2014 fix your first bug in 60 seconds",
|
|
1228
|
+
url: docUrl("/quickstart"),
|
|
1229
|
+
route: "/quickstart",
|
|
1230
|
+
markdown: "quickstart/index.md",
|
|
1231
|
+
keywords: ["quickstart", "fix", "first", "bug", "60", "seconds", "choose", "stack"],
|
|
1232
|
+
headings: ["Start here (MCP)", "Pick your stack", "How do I install the SDK?", "Troubleshooting"],
|
|
1233
|
+
excerpt: "Install Mushi with npx mushi-mushi, connect Cursor or Claude Code over MCP, and turn user bug reports into plain-English diagnoses with ready fixes."
|
|
1234
|
+
},
|
|
1235
|
+
{
|
|
1236
|
+
title: "Android (Kotlin) quickstart",
|
|
1237
|
+
url: docUrl("/quickstart/android"),
|
|
1238
|
+
route: "/quickstart/android",
|
|
1239
|
+
markdown: "quickstart/android.md",
|
|
1240
|
+
keywords: ["quickstart", "android", "kotlin"],
|
|
1241
|
+
headings: ["Build from source", "Initialize", "Open the widget or submit a report", "Sentry bridge (optional)"],
|
|
1242
|
+
excerpt: "Try Mushi's native Kotlin SDK on Android \u2014 shake-to-report, a bottom-sheet widget and an offline queue. A preview built from source, not on Maven Central yet."
|
|
1243
|
+
},
|
|
1244
|
+
{
|
|
1245
|
+
title: "Angular quickstart",
|
|
1246
|
+
url: docUrl("/quickstart/angular"),
|
|
1247
|
+
route: "/quickstart/angular",
|
|
1248
|
+
markdown: "quickstart/angular.md",
|
|
1249
|
+
keywords: ["quickstart", "angular"],
|
|
1250
|
+
headings: [],
|
|
1251
|
+
excerpt: "Add Mushi to an Angular 17+ app \u2014 register the Mushi service and error handler as factory providers so uncaught errors and user bug reports reach your queue."
|
|
1252
|
+
},
|
|
1253
|
+
{
|
|
1254
|
+
title: "Capacitor quickstart",
|
|
1255
|
+
url: docUrl("/quickstart/capacitor"),
|
|
1256
|
+
route: "/quickstart/capacitor",
|
|
1257
|
+
markdown: "quickstart/capacitor.md",
|
|
1258
|
+
keywords: ["quickstart", "capacitor"],
|
|
1259
|
+
headings: ["1. Install", "2. Configure", "3. Submit a report"],
|
|
1260
|
+
excerpt: "Add Mushi to an Ionic or Capacitor app \u2014 install @mushi-mushi/capacitor, configure shake-to-report and screenshots, and file your first bug report."
|
|
1261
|
+
},
|
|
1262
|
+
{
|
|
1263
|
+
title: "CLI \u2194 console setup loop",
|
|
1264
|
+
url: docUrl("/quickstart/cli-console-loop"),
|
|
1265
|
+
route: "/quickstart/cli-console-loop",
|
|
1266
|
+
markdown: "quickstart/cli-console-loop.md",
|
|
1267
|
+
keywords: ["quickstart", "cli-console-loop", "cli", "console", "loop", "setup"],
|
|
1268
|
+
headings: ["Which CLI command?", "Browser sign-in flow (recommended)", "What happens in the terminal", "What happens in the browser", "How sign-in reliability works", "1. Run the wizard", "2. Approve in the browser", "3. Pick or create a project"],
|
|
1269
|
+
excerpt: "How npx mushi-mushi creates a project, mints an SDK key and installs the SDK through browser sign-in, and how to recover when the setup loop fails."
|
|
1270
|
+
},
|
|
1271
|
+
{
|
|
1272
|
+
title: "Flutter quickstart",
|
|
1273
|
+
url: docUrl("/quickstart/flutter"),
|
|
1274
|
+
route: "/quickstart/flutter",
|
|
1275
|
+
markdown: "quickstart/flutter.md",
|
|
1276
|
+
keywords: ["quickstart", "flutter"],
|
|
1277
|
+
headings: ["Install", "Initialize", "Open the widget or submit a report"],
|
|
1278
|
+
excerpt: "Add Mushi's Flutter SDK as a git dependency \u2014 shake-to-report, a Material bottom sheet and an offline queue for iOS and Android. Preview, not on pub.dev yet."
|
|
1279
|
+
},
|
|
1280
|
+
{
|
|
1281
|
+
title: "Incident loop \u2014 bug to fix prompt",
|
|
1282
|
+
url: docUrl("/quickstart/incident-loop"),
|
|
1283
|
+
route: "/quickstart/incident-loop",
|
|
1284
|
+
markdown: "quickstart/incident-loop.md",
|
|
1285
|
+
keywords: ["quickstart", "incident-loop", "incident", "loop", "bug", "fix", "prompt"],
|
|
1286
|
+
headings: ["Prerequisites", "Step 1 \u2014 Capture or pick a report", "Step 2 \u2014 Pull fix context", "Step 3 \u2014 Generate the fix prompt", "Step 4 \u2014 Paste and ship", "Troubleshooting", "See also"],
|
|
1287
|
+
excerpt: "Run npx mushi-mushi, ship, and turn your first user-reported bug into a plain-English diagnosis and a paste-ready fix for Cursor or Claude Code."
|
|
1288
|
+
},
|
|
1289
|
+
{
|
|
1290
|
+
title: "iOS (Swift) quickstart",
|
|
1291
|
+
url: docUrl("/quickstart/ios"),
|
|
1292
|
+
route: "/quickstart/ios",
|
|
1293
|
+
markdown: "quickstart/ios.md",
|
|
1294
|
+
keywords: ["quickstart", "ios", "swift"],
|
|
1295
|
+
headings: ["Install (Swift Package Manager)", "Initialize", "Open the widget or submit a report", "Sentry"],
|
|
1296
|
+
excerpt: "Add Mushi's native Swift SDK to an iOS app with Swift Package Manager \u2014 shake-to-report, offline queue and device context. Preview build, not on CocoaPods yet."
|
|
1297
|
+
},
|
|
1298
|
+
{
|
|
1299
|
+
title: "MCP server for bug fixing in Cursor & Claude Code",
|
|
1300
|
+
url: docUrl("/quickstart/mcp"),
|
|
1301
|
+
route: "/quickstart/mcp",
|
|
1302
|
+
markdown: "quickstart/mcp.md",
|
|
1303
|
+
keywords: ["quickstart", "mcp", "server", "bug", "fixing", "cursor", "claude", "code"],
|
|
1304
|
+
headings: ["OAuth login (recommended \u2014 no keys to copy)", "One-liner setup (recommended)", "Claude Code plugin", "Smithery (hosted MCP)", "Manual setup: Cursor", "Manual setup: Claude Desktop", "Hosted Streamable HTTP transport (no subprocess)", "Available tools"],
|
|
1305
|
+
excerpt: "Set up the Mushi MCP server with npx mushi-mushi setup \u2014 read bug reports, get fix context, and dispatch fixes from Cursor, Claude Code, or Codex."
|
|
1306
|
+
},
|
|
1307
|
+
{
|
|
1308
|
+
title: "Mobile (overview)",
|
|
1309
|
+
url: docUrl("/quickstart/mobile"),
|
|
1310
|
+
route: "/quickstart/mobile",
|
|
1311
|
+
markdown: "quickstart/mobile.md",
|
|
1312
|
+
keywords: ["quickstart", "mobile", "overview"],
|
|
1313
|
+
headings: [],
|
|
1314
|
+
excerpt: "Shake-to-report bug capture for mobile apps \u2014 React Native and Capacitor from npm with npx mushi-mushi, plus preview native SDKs for iOS, Android and Flutter."
|
|
1315
|
+
},
|
|
1316
|
+
{
|
|
1317
|
+
title: "React quickstart",
|
|
1318
|
+
url: docUrl("/quickstart/react"),
|
|
1319
|
+
route: "/quickstart/react",
|
|
1320
|
+
markdown: "quickstart/react.md",
|
|
1321
|
+
keywords: ["quickstart", "react"],
|
|
1322
|
+
headings: ["Try it live", "1. Install", "2. Wrap your app", "Next.js App Router", "3. Trigger reports", "4. Verify", "Next steps"],
|
|
1323
|
+
excerpt: "Add the Mushi React SDK in one command \u2014 npx mushi-mushi installs the bug-reporting widget, writes env vars, and files your first test report."
|
|
1324
|
+
},
|
|
1325
|
+
{
|
|
1326
|
+
title: "React Native quickstart",
|
|
1327
|
+
url: docUrl("/quickstart/react-native"),
|
|
1328
|
+
route: "/quickstart/react-native",
|
|
1329
|
+
markdown: "quickstart/react-native.md",
|
|
1330
|
+
keywords: ["quickstart", "react-native", "react", "native"],
|
|
1331
|
+
headings: ["1. Install", "2. Mount the provider", "3. Verify"],
|
|
1332
|
+
excerpt: "Add shake-to-report bug capture to a React Native or Expo app with @mushi-mushi/react-native \u2014 install, mount the provider, and verify the first report."
|
|
1333
|
+
},
|
|
1334
|
+
{
|
|
1335
|
+
title: "Svelte quickstart",
|
|
1336
|
+
url: docUrl("/quickstart/svelte"),
|
|
1337
|
+
route: "/quickstart/svelte",
|
|
1338
|
+
markdown: "quickstart/svelte.md",
|
|
1339
|
+
keywords: ["quickstart", "svelte"],
|
|
1340
|
+
headings: [],
|
|
1341
|
+
excerpt: "Add Mushi bug reporting to a Svelte or SvelteKit app \u2014 install @mushi-mushi/svelte and call initMushi from hooks.client.ts or a root layout's onMount."
|
|
1342
|
+
},
|
|
1343
|
+
{
|
|
1344
|
+
title: "Vue 3 quickstart",
|
|
1345
|
+
url: docUrl("/quickstart/vue"),
|
|
1346
|
+
route: "/quickstart/vue",
|
|
1347
|
+
markdown: "quickstart/vue.md",
|
|
1348
|
+
keywords: ["quickstart", "vue"],
|
|
1349
|
+
headings: [],
|
|
1350
|
+
excerpt: "Add Mushi bug reporting to a Vue 3 app \u2014 install @mushi-mushi/vue and register MushiPlugin with your project ID and public API key in main.ts."
|
|
1351
|
+
},
|
|
1352
|
+
{
|
|
1353
|
+
title: "Vanilla JS quickstart",
|
|
1354
|
+
url: docUrl("/quickstart/web"),
|
|
1355
|
+
route: "/quickstart/web",
|
|
1356
|
+
markdown: "quickstart/web.md",
|
|
1357
|
+
keywords: ["quickstart", "web", "vanilla", "js"],
|
|
1358
|
+
headings: ["Try it live", "1. Install", "2. Initialize", "3. Submit", "No bundler: script tag"],
|
|
1359
|
+
excerpt: "Add Mushi's browser SDK to any site without a framework \u2014 install @mushi-mushi/web, call Mushi.init, and file bug reports from your buttons or the widget."
|
|
1360
|
+
},
|
|
1361
|
+
{
|
|
1362
|
+
title: "Roadmap",
|
|
1363
|
+
url: docUrl("/roadmap"),
|
|
1364
|
+
route: "/roadmap",
|
|
1365
|
+
markdown: "roadmap.md",
|
|
1366
|
+
keywords: ["roadmap"],
|
|
1367
|
+
headings: ["Live boards", "Release waves", "Wave E details (in-flight)", "Wave D highlights (shipped)", "Completed highlights (Wave C, v0.8.0)"],
|
|
1368
|
+
excerpt: "Where Mushi Mushi is headed \u2014 upcoming SDK platforms, diagnosis speed targets, and console features, updated as work lands."
|
|
1369
|
+
},
|
|
1370
|
+
{
|
|
1371
|
+
title: "SDK reference",
|
|
1372
|
+
url: docUrl("/sdks"),
|
|
1373
|
+
route: "/sdks",
|
|
1374
|
+
markdown: "sdks/index.md",
|
|
1375
|
+
keywords: ["sdks", "sdk", "reference", "index"],
|
|
1376
|
+
headings: ["Maturity, honestly", "Web & frameworks", "Server", "Mobile & native", "Tooling & automation", "Infrastructure & advanced", "Recipes & presets", "Installation"],
|
|
1377
|
+
excerpt: "Bug-reporting SDKs for React, Vue, Svelte, Angular, React Native, Capacitor and Node, installed by npx mushi-mushi, plus preview iOS, Android and Flutter SDKs."
|
|
1378
|
+
},
|
|
1379
|
+
{
|
|
1380
|
+
title: "@mushi-mushi/adapters",
|
|
1381
|
+
url: docUrl("/sdks/adapters"),
|
|
1382
|
+
route: "/sdks/adapters",
|
|
1383
|
+
markdown: "sdks/adapters.md",
|
|
1384
|
+
keywords: ["sdks", "adapters", "@mushi-mushi/adapters"],
|
|
1385
|
+
headings: ["Install", "Eleven supported sources", "Quick start \u2014 Datadog \u2192 Hono", "Severity mapping", "Authentication"],
|
|
1386
|
+
excerpt: "Turn Sentry, Datadog, Bugsnag and other monitoring webhooks into Mushi reports with @mushi-mushi/adapters, deduped against the same queue as user reports."
|
|
1387
|
+
},
|
|
1388
|
+
{
|
|
1389
|
+
title: "Product analytics (track)",
|
|
1390
|
+
url: docUrl("/sdks/analytics"),
|
|
1391
|
+
route: "/sdks/analytics",
|
|
1392
|
+
markdown: "sdks/analytics.md",
|
|
1393
|
+
keywords: ["sdks", "analytics", "product", "track"],
|
|
1394
|
+
headings: ["Quick start", "API", "Configuration: analytics", "Event names and properties", "Consent and privacy", "Batching and delivery", "The console: Users & Funnels", "MCP tools"],
|
|
1395
|
+
excerpt: "Mushi.track() reference \u2014 named events, consent, sampling and batching, useMushiTrack() for React, Users & Funnels, and the /v1/sdk/events wire format."
|
|
1396
|
+
},
|
|
1397
|
+
{
|
|
1398
|
+
title: "dev.mushimushi:mushi-android (Android)",
|
|
1399
|
+
url: docUrl("/sdks/android"),
|
|
1400
|
+
route: "/sdks/android",
|
|
1401
|
+
markdown: "sdks/android.md",
|
|
1402
|
+
keywords: ["sdks", "android", "dev.mushimushi", "mushi-android"],
|
|
1403
|
+
headings: ["API surface", "Setup", "Identifying users", "Offline queue", "Sentry bridge"],
|
|
1404
|
+
excerpt: "API reference for Mushi's native Kotlin SDK \u2014 init, report, captureError, showWidget, breadcrumbs and the Sentry bridge. A preview built from source."
|
|
1405
|
+
},
|
|
1406
|
+
{
|
|
1407
|
+
title: "@mushi-mushi/angular",
|
|
1408
|
+
url: docUrl("/sdks/angular"),
|
|
1409
|
+
route: "/sdks/angular",
|
|
1410
|
+
markdown: "sdks/angular.md",
|
|
1411
|
+
keywords: ["sdks", "angular", "@mushi-mushi/angular"],
|
|
1412
|
+
headings: ["API surface", "Setup (standalone bootstrap)", "Submitting a report", "Identifying users"],
|
|
1413
|
+
excerpt: "API reference for @mushi-mushi/angular \u2014 MushiService, MushiErrorHandler and the MUSHI_CONFIG token, wired with factory providers in an Angular 17+ app."
|
|
1414
|
+
},
|
|
1415
|
+
{
|
|
1416
|
+
title: "In-SDK Ask assistant",
|
|
1417
|
+
url: docUrl("/sdks/assistant"),
|
|
1418
|
+
route: "/sdks/assistant",
|
|
1419
|
+
markdown: "sdks/assistant.md",
|
|
1420
|
+
keywords: ["sdks", "assistant", "in-sdk", "ask"],
|
|
1421
|
+
headings: ["What it does", "Enable it", "SDK surface (host app)", "Security (short)"],
|
|
1422
|
+
excerpt: "The Mushi widget's Ask tab answers end users' questions from the current page and your knowledge corpus, using your own LLM key, with every turn logged."
|
|
1423
|
+
},
|
|
1424
|
+
{
|
|
1425
|
+
title: "@mushi-mushi/capacitor",
|
|
1426
|
+
url: docUrl("/sdks/capacitor"),
|
|
1427
|
+
route: "/sdks/capacitor",
|
|
1428
|
+
markdown: "sdks/capacitor.md",
|
|
1429
|
+
keywords: ["sdks", "capacitor", "@mushi-mushi/capacitor"],
|
|
1430
|
+
headings: ["Install", "Configure", "Submit a report", "Programmatic widget + listeners", "Position the trigger above your tab bar / dock"],
|
|
1431
|
+
excerpt: "API reference for @mushi-mushi/capacitor \u2014 configure, report, listeners, setUser and widget options for Ionic and Capacitor apps on iOS, Android and web."
|
|
1432
|
+
},
|
|
1433
|
+
{
|
|
1434
|
+
title: "Capacitor bottom dock",
|
|
1435
|
+
url: docUrl("/sdks/capacitor-bottom-dock"),
|
|
1436
|
+
route: "/sdks/capacitor-bottom-dock",
|
|
1437
|
+
markdown: "sdks/capacitor-bottom-dock.md",
|
|
1438
|
+
keywords: ["sdks", "capacitor-bottom-dock", "capacitor", "bottom", "dock"],
|
|
1439
|
+
headings: [],
|
|
1440
|
+
excerpt: "Keep the Mushi bug-report button clear of tab bars and bottom docks in Capacitor apps with triggerInsetPreset, explicit insets, or widget.anchor."
|
|
1441
|
+
},
|
|
1442
|
+
{
|
|
1443
|
+
title: "@mushi-mushi/cli",
|
|
1444
|
+
url: docUrl("/sdks/cli"),
|
|
1445
|
+
route: "/sdks/cli",
|
|
1446
|
+
markdown: "sdks/cli.md",
|
|
1447
|
+
keywords: ["sdks", "cli", "@mushi-mushi/cli"],
|
|
1448
|
+
headings: ["Console \u2194 CLI setup loop", "Setup & health", "mushi doctor", "mushi nudge", "Reports & lessons", "Agentic fixes", "mushi fix", "mushi fixes"],
|
|
1449
|
+
excerpt: "Reference for @mushi-mushi/cli \u2014 log in, connect a project, run doctor, list reports, dispatch agent fixes and merge their pull requests from the terminal."
|
|
1450
|
+
},
|
|
1451
|
+
{
|
|
1452
|
+
title: "@mushi-mushi/core",
|
|
1453
|
+
url: docUrl("/sdks/core"),
|
|
1454
|
+
route: "/sdks/core",
|
|
1455
|
+
markdown: "sdks/core.md",
|
|
1456
|
+
keywords: ["sdks", "core", "@mushi-mushi/core"],
|
|
1457
|
+
headings: ["Install", "API", "Mushi.init(config) (web) / MushiProvider (React Native)", "MushiWidgetConfig (selected keys)", "submitReport(input)", "flushOfflineQueueNow()", "PII scrubbing utilities (1.27+)", "Region resolution"],
|
|
1458
|
+
excerpt: "Reference for @mushi-mushi/core \u2014 the MushiConfig shape, widget options, PII scrubbing helpers and region resolution shared by every Mushi JavaScript SDK."
|
|
1459
|
+
},
|
|
1460
|
+
{
|
|
1461
|
+
title: "eslint-plugin-mushi-mushi",
|
|
1462
|
+
url: docUrl("/sdks/eslint-plugin"),
|
|
1463
|
+
route: "/sdks/eslint-plugin",
|
|
1464
|
+
markdown: "sdks/eslint-plugin.md",
|
|
1465
|
+
keywords: ["sdks", "eslint-plugin", "eslint", "plugin", "eslint-plugin-mushi-mushi"],
|
|
1466
|
+
headings: ["Install", "Recommended preset", "Rules", "mushi-mushi/no-dead-handler", "mushi-mushi/no-mock-leak", "Combining with the gates action"],
|
|
1467
|
+
excerpt: "eslint-plugin-mushi-mushi flags empty event handlers and leftover mock data in production code \u2014 the no-dead-handler and no-mock-leak rules and preset."
|
|
1468
|
+
},
|
|
1469
|
+
{
|
|
1470
|
+
title: "mushi_mushi (Flutter)",
|
|
1471
|
+
url: docUrl("/sdks/flutter"),
|
|
1472
|
+
route: "/sdks/flutter",
|
|
1473
|
+
markdown: "sdks/flutter.md",
|
|
1474
|
+
keywords: ["sdks", "flutter"],
|
|
1475
|
+
headings: ["API surface", "Configuration", "Submitting a report", "Screenshot capture", "Offline queue"],
|
|
1476
|
+
excerpt: "API reference for mushi_mushi, Mushi's Flutter SDK \u2014 configure, report, captureError, showWidget, screenshots and the offline queue. Preview, not on pub.dev."
|
|
1477
|
+
},
|
|
1478
|
+
{
|
|
1479
|
+
title: "Framework wrappers (Vue / Svelte / Angular)",
|
|
1480
|
+
url: docUrl("/sdks/framework-wrappers"),
|
|
1481
|
+
route: "/sdks/framework-wrappers",
|
|
1482
|
+
markdown: "sdks/framework-wrappers.md",
|
|
1483
|
+
keywords: ["sdks", "framework-wrappers", "framework", "wrappers", "vue", "svelte", "angular"],
|
|
1484
|
+
headings: [],
|
|
1485
|
+
excerpt: "How the Vue, Svelte and Angular Mushi packages wrap @mushi-mushi/web \u2014 how each one boots the SDK once, and where to find capture and widget options."
|
|
1486
|
+
},
|
|
1487
|
+
{
|
|
1488
|
+
title: "@mushi-mushi/inventory-auth-runner",
|
|
1489
|
+
url: docUrl("/sdks/inventory-auth-runner"),
|
|
1490
|
+
route: "/sdks/inventory-auth-runner",
|
|
1491
|
+
markdown: "sdks/inventory-auth-runner.md",
|
|
1492
|
+
keywords: ["sdks", "inventory-auth-runner", "inventory", "auth", "runner", "@mushi-mushi/inventory-auth-runner"],
|
|
1493
|
+
headings: ["When you need it", "Install", "Usage", "In CI"],
|
|
1494
|
+
excerpt: "@mushi-mushi/inventory-auth-runner logs in with Playwright from your inventory.yaml auth script so Mushi's crawler can reach pages behind a login."
|
|
1495
|
+
},
|
|
1496
|
+
{
|
|
1497
|
+
title: "@mushi-mushi/inventory-schema",
|
|
1498
|
+
url: docUrl("/sdks/inventory-schema"),
|
|
1499
|
+
route: "/sdks/inventory-schema",
|
|
1500
|
+
markdown: "sdks/inventory-schema.md",
|
|
1501
|
+
keywords: ["sdks", "inventory-schema", "inventory", "schema", "@mushi-mushi/inventory-schema"],
|
|
1502
|
+
headings: ["Install", "What inventory.yaml looks like", "Programmatic use", "JSON Schema"],
|
|
1503
|
+
excerpt: "The Zod schema, JSON Schema and TypeScript types for inventory.yaml \u2014 the contract that Mushi's gates, crawler, ESLint plugin and CLI all read."
|
|
1504
|
+
},
|
|
1505
|
+
{
|
|
1506
|
+
title: "MushiMushi (iOS)",
|
|
1507
|
+
url: docUrl("/sdks/ios"),
|
|
1508
|
+
route: "/sdks/ios",
|
|
1509
|
+
markdown: "sdks/ios.md",
|
|
1510
|
+
keywords: ["sdks", "ios", "mushimushi"],
|
|
1511
|
+
headings: ["API surface", "Setup", "Identifying users", "Offline queue", "Screenshot capture", "Sentry"],
|
|
1512
|
+
excerpt: "API reference for MushiMushi, Mushi's native Swift SDK \u2014 configure, report, captureError, breadcrumbs and PII scrubbing. Preview, via SwiftPM from master."
|
|
1513
|
+
},
|
|
1514
|
+
{
|
|
1515
|
+
title: "mushi-mushi (launcher)",
|
|
1516
|
+
url: docUrl("/sdks/launcher"),
|
|
1517
|
+
route: "/sdks/launcher",
|
|
1518
|
+
markdown: "sdks/launcher.md",
|
|
1519
|
+
keywords: ["sdks", "launcher", "mushi-mushi"],
|
|
1520
|
+
headings: ["Try it", "What it detects", "Idempotent", "When to use the launcher vs the CLI directly"],
|
|
1521
|
+
excerpt: "npx mushi-mushi detects your framework, installs the matching @mushi-mushi SDK, writes your project ID and API key to .env.local, and prints the snippet."
|
|
1522
|
+
},
|
|
1523
|
+
{
|
|
1524
|
+
title: "Bug report MCP server for Cursor & Claude Code",
|
|
1525
|
+
url: docUrl("/sdks/mcp"),
|
|
1526
|
+
route: "/sdks/mcp",
|
|
1527
|
+
markdown: "sdks/mcp.md",
|
|
1528
|
+
keywords: ["sdks", "mcp", "bug", "report", "server", "cursor", "claude", "code", "@mushi-mushi/mcp"],
|
|
1529
|
+
headings: ["Two transports", "Spec traceability for dispatchfix"],
|
|
1530
|
+
excerpt: "Reference for @mushi-mushi/mcp, the Mushi MCP server for Cursor, Claude Code, VS Code and Windsurf \u2014 stdio and hosted HTTP transports, scopes and setup."
|
|
1531
|
+
},
|
|
1532
|
+
{
|
|
1533
|
+
title: "@mushi-mushi/mcp-ci",
|
|
1534
|
+
url: docUrl("/sdks/mcp-ci"),
|
|
1535
|
+
route: "/sdks/mcp-ci",
|
|
1536
|
+
markdown: "sdks/mcp-ci.md",
|
|
1537
|
+
keywords: ["sdks", "mcp-ci", "mcp", "@mushi-mushi/mcp-ci"],
|
|
1538
|
+
headings: ["Quick start", "Commands", "In your IDE", "Inputs"],
|
|
1539
|
+
excerpt: "@mushi-mushi/mcp-ci is Mushi's GitHub Action \u2014 run the five-gate composite check on pull requests, draft inventory entries, and bootstrap crawler auth."
|
|
1540
|
+
},
|
|
1541
|
+
{
|
|
1542
|
+
title: "MCP tools reference",
|
|
1543
|
+
url: docUrl("/sdks/mcp-tools"),
|
|
1544
|
+
route: "/sdks/mcp-tools",
|
|
1545
|
+
markdown: "sdks/mcp-tools.md",
|
|
1546
|
+
keywords: ["sdks", "mcp-tools", "mcp", "tools", "reference"],
|
|
1547
|
+
headings: ["Read tools (mcp:read)", "Write tools (mcp:write)", "Resources", "Prompts"],
|
|
1548
|
+
excerpt: "Every tool, resource and prompt the Mushi MCP server exposes to Cursor, Claude Code and other editors, with its read or write scope, from the catalog."
|
|
1549
|
+
},
|
|
1550
|
+
{
|
|
1551
|
+
title: "Next.js App Router + CSP",
|
|
1552
|
+
url: docUrl("/sdks/nextjs-app-router-csp"),
|
|
1553
|
+
route: "/sdks/nextjs-app-router-csp",
|
|
1554
|
+
markdown: "sdks/nextjs-app-router-csp.md",
|
|
1555
|
+
keywords: ["sdks", "nextjs-app-router-csp", "nextjs", "app", "router", "csp", "next.js"],
|
|
1556
|
+
headings: [],
|
|
1557
|
+
excerpt: "Mount Mushi in a Next.js App Router client component and allow its endpoints in your Content-Security-Policy, with a copy-paste CSP allowlist."
|
|
1558
|
+
},
|
|
1559
|
+
{
|
|
1560
|
+
title: "Next.js static export",
|
|
1561
|
+
url: docUrl("/sdks/nextjs-static-export"),
|
|
1562
|
+
route: "/sdks/nextjs-static-export",
|
|
1563
|
+
markdown: "sdks/nextjs-static-export.md",
|
|
1564
|
+
keywords: ["sdks", "nextjs-static-export", "nextjs", "static", "export", "next.js"],
|
|
1565
|
+
headings: [],
|
|
1566
|
+
excerpt: "Use Mushi in a Next.js static export \u2014 keep the public env vars in NEXT_PUBLIC_* or set runtimeConfig to false, and initialise on the client only."
|
|
1567
|
+
},
|
|
1568
|
+
{
|
|
1569
|
+
title: "Node.js server error reporting SDK",
|
|
1570
|
+
url: docUrl("/sdks/node"),
|
|
1571
|
+
route: "/sdks/node",
|
|
1572
|
+
markdown: "sdks/node.md",
|
|
1573
|
+
keywords: ["sdks", "node", "node.js", "server", "error", "reporting", "sdk", "@mushi-mushi/node"],
|
|
1574
|
+
headings: ["Install", "Express", "Fastify", "Hono", "Process-level fallbacks", "Programmatic submit", "PII and credential scrubbing"],
|
|
1575
|
+
excerpt: "Report server errors from Node with @mushi-mushi/node \u2014 Express, Fastify and Hono error handlers that tag each report with route, request ID and user."
|
|
1576
|
+
},
|
|
1577
|
+
{
|
|
1578
|
+
title: "@mushi-mushi/plugin-sdk",
|
|
1579
|
+
url: docUrl("/sdks/plugin-sdk"),
|
|
1580
|
+
route: "/sdks/plugin-sdk",
|
|
1581
|
+
markdown: "sdks/plugin-sdk.md",
|
|
1582
|
+
keywords: ["sdks", "plugin-sdk", "plugin", "sdk", "@mushi-mushi/plugin-sdk"],
|
|
1583
|
+
headings: ["Install", "Express adapter", "Hono adapter", "Verify a signature manually", "Wire format", "See also"],
|
|
1584
|
+
excerpt: "Build a Mushi plugin in TypeScript with @mushi-mushi/plugin-sdk \u2014 verify webhook signatures, dedupe deliveries and handle lifecycle events in Express."
|
|
1585
|
+
},
|
|
1586
|
+
{
|
|
1587
|
+
title: "SDK presets",
|
|
1588
|
+
url: docUrl("/sdks/presets"),
|
|
1589
|
+
route: "/sdks/presets",
|
|
1590
|
+
markdown: "sdks/presets.md",
|
|
1591
|
+
keywords: ["sdks", "presets", "sdk"],
|
|
1592
|
+
headings: [],
|
|
1593
|
+
excerpt: "Mushi SDK presets such as production-calm set sensible widget and capture defaults in one line, and any explicit Mushi.init option still wins."
|
|
1594
|
+
},
|
|
1595
|
+
{
|
|
1596
|
+
title: "React & Next.js bug reporting SDK",
|
|
1597
|
+
url: docUrl("/sdks/react"),
|
|
1598
|
+
route: "/sdks/react",
|
|
1599
|
+
markdown: "sdks/react.md",
|
|
1600
|
+
keywords: ["sdks", "react", "next.js", "bug", "reporting", "sdk", "@mushi-mushi/react"],
|
|
1601
|
+
headings: ["Identifying users", "Enabling the Rewards program", "useReputation()", "useTier()", "Submitting custom activity"],
|
|
1602
|
+
excerpt: "Reference for @mushi-mushi/react \u2014 MushiProvider, the useMushi, useMushiSdk and useMushiReport hooks, identifying users, and the rewards hooks and badge."
|
|
1603
|
+
},
|
|
1604
|
+
{
|
|
1605
|
+
title: "@mushi-mushi/react-native",
|
|
1606
|
+
url: docUrl("/sdks/react-native"),
|
|
1607
|
+
route: "/sdks/react-native",
|
|
1608
|
+
markdown: "sdks/react-native.md",
|
|
1609
|
+
keywords: ["sdks", "react-native", "react", "native", "@mushi-mushi/react-native"],
|
|
1610
|
+
headings: ["Install", "Mount the provider", "Submit a report from a screen", "Offline queue", "What gets captured (v0.19+)", "Screenshots (react-native-view-shot)", "Manual breadcrumbs", "Reporter identity contract"],
|
|
1611
|
+
excerpt: "Reference for @mushi-mushi/react-native \u2014 MushiProvider, hooks, shake-to-report, screenshots, identity and the offline queue for React Native and Expo."
|
|
1612
|
+
},
|
|
1613
|
+
{
|
|
1614
|
+
title: "Sentry Replay coexistence",
|
|
1615
|
+
url: docUrl("/sdks/sentry-replay-coexistence"),
|
|
1616
|
+
route: "/sdks/sentry-replay-coexistence",
|
|
1617
|
+
markdown: "sdks/sentry-replay-coexistence.md",
|
|
1618
|
+
keywords: ["sdks", "sentry-replay-coexistence", "sentry", "replay", "coexistence"],
|
|
1619
|
+
headings: [],
|
|
1620
|
+
excerpt: "Run Mushi next to Sentry Session Replay \u2014 initialise Sentry first so Mushi attaches the Sentry event and replay IDs to every bug report it sends."
|
|
1621
|
+
},
|
|
1622
|
+
{
|
|
1623
|
+
title: "Agent skills",
|
|
1624
|
+
url: docUrl("/sdks/skills"),
|
|
1625
|
+
route: "/sdks/skills",
|
|
1626
|
+
markdown: "sdks/skills.md",
|
|
1627
|
+
keywords: ["sdks", "skills", "agent"],
|
|
1628
|
+
headings: ["Install skills into your editor", "How skills connect to Mushi", "Console & MCP", "Related"],
|
|
1629
|
+
excerpt: "Install Mushi playbooks into Cursor and Claude Code, sync them into the console catalog, and attach skill chains to bug reports."
|
|
1630
|
+
},
|
|
1631
|
+
{
|
|
1632
|
+
title: "@mushi-mushi/svelte",
|
|
1633
|
+
url: docUrl("/sdks/svelte"),
|
|
1634
|
+
route: "/sdks/svelte",
|
|
1635
|
+
markdown: "sdks/svelte.md",
|
|
1636
|
+
keywords: ["sdks", "svelte", "@mushi-mushi/svelte"],
|
|
1637
|
+
headings: ["API surface", "Setup", "Submitting a report"],
|
|
1638
|
+
excerpt: "Reference for @mushi-mushi/svelte \u2014 initMushi, getMushi and the SvelteKit error hooks that forward client and server errors to your Mushi queue."
|
|
1639
|
+
},
|
|
1640
|
+
{
|
|
1641
|
+
title: "@mushi-mushi/vue",
|
|
1642
|
+
url: docUrl("/sdks/vue"),
|
|
1643
|
+
route: "/sdks/vue",
|
|
1644
|
+
markdown: "sdks/vue.md",
|
|
1645
|
+
keywords: ["sdks", "vue", "@mushi-mushi/vue"],
|
|
1646
|
+
headings: ["API surface", "Setup (Vite / Vue 3)", "Identifying users", "Submitting a report"],
|
|
1647
|
+
excerpt: "Reference for @mushi-mushi/vue \u2014 install MushiPlugin, use the useMushi and useMushiReport composables, identify users and submit reports in Vue 3."
|
|
1648
|
+
},
|
|
1649
|
+
{
|
|
1650
|
+
title: "@mushi-mushi/wasm-classifier",
|
|
1651
|
+
url: docUrl("/sdks/wasm-classifier"),
|
|
1652
|
+
route: "/sdks/wasm-classifier",
|
|
1653
|
+
markdown: "sdks/wasm-classifier.md",
|
|
1654
|
+
keywords: ["sdks", "wasm-classifier", "wasm", "classifier", "@mushi-mushi/wasm-classifier"],
|
|
1655
|
+
headings: [],
|
|
1656
|
+
excerpt: "@mushi-mushi/wasm-classifier runs a small on-device model in the browser to drop junk bug reports before they reach your LLM, cutting classification cost."
|
|
1657
|
+
},
|
|
1658
|
+
{
|
|
1659
|
+
title: "JavaScript bug reporting SDK for the browser",
|
|
1660
|
+
url: docUrl("/sdks/web"),
|
|
1661
|
+
route: "/sdks/web",
|
|
1662
|
+
markdown: "sdks/web.md",
|
|
1663
|
+
keywords: ["sdks", "web", "javascript", "bug", "reporting", "sdk", "browser", "@mushi-mushi/web"],
|
|
1664
|
+
headings: ["Sampling & beforeSend", "Screenshot preview & consent (1.19+)", "Runtime config merge (Jul 2026+)", "Capture availability & errors", "Description draft persistence", "Identifying users & the Rewards program", "Enabling the Rewards program", "Querying points & tier"],
|
|
1665
|
+
excerpt: "Reference for @mushi-mushi/web, the browser SDK \u2014 screenshots, console and network capture, the Shadow DOM widget, sampling, beforeSend and privacy."
|
|
1666
|
+
},
|
|
1667
|
+
{
|
|
1668
|
+
title: "Security & compliance",
|
|
1669
|
+
url: docUrl("/security"),
|
|
1670
|
+
route: "/security",
|
|
1671
|
+
markdown: "security/index.md",
|
|
1672
|
+
keywords: ["security", "compliance"],
|
|
1673
|
+
headings: ["Posture at a glance", "Operational transparency", "Report a vulnerability", "What we do not claim"],
|
|
1674
|
+
excerpt: "How Mushi Mushi protects bug-report data \u2014 residency, your own keys and storage, retention sweeps, RLS coverage, prompt-injection defence and SOC 2."
|
|
1675
|
+
},
|
|
1676
|
+
{
|
|
1677
|
+
title: "BYO storage",
|
|
1678
|
+
url: docUrl("/security/byo-storage"),
|
|
1679
|
+
route: "/security/byo-storage",
|
|
1680
|
+
markdown: "security/byo-storage.md",
|
|
1681
|
+
keywords: ["security", "byo-storage", "byo", "storage"],
|
|
1682
|
+
headings: ["Supported backends", "Setup", "What moves (and what doesn't)", "Health check", "Permissions required"],
|
|
1683
|
+
excerpt: "Keep Mushi screenshots and crash dumps in your own storage bucket instead of Mushi's \u2014 credentials in Vault, signed URLs, and the same console view."
|
|
1684
|
+
},
|
|
1685
|
+
{
|
|
1686
|
+
title: "Bring-your-own-key",
|
|
1687
|
+
url: docUrl("/security/byok"),
|
|
1688
|
+
route: "/security/byok",
|
|
1689
|
+
markdown: "security/byok.md",
|
|
1690
|
+
keywords: ["security", "byok", "bring-your-own-key"],
|
|
1691
|
+
headings: ["Setup", "What uses the key", "Audit trail", "Rotation", "Langfuse trace attribution"],
|
|
1692
|
+
excerpt: "Use your own Anthropic or OpenAI API key for a Mushi project, so every classification, judge and fix run bills to your provider account."
|
|
1693
|
+
},
|
|
1694
|
+
{
|
|
1695
|
+
title: "Data residency",
|
|
1696
|
+
url: docUrl("/security/data-residency"),
|
|
1697
|
+
route: "/security/data-residency",
|
|
1698
|
+
markdown: "security/data-residency.md",
|
|
1699
|
+
keywords: ["security", "data-residency", "data", "residency"],
|
|
1700
|
+
headings: ["Regions", "How the SDK routes", "Attestation", "Self-hosting", "GDPR notes"],
|
|
1701
|
+
excerpt: "Where Mushi Cloud stores data today (Tokyo, ap-northeast-1), how the region picked at project creation works, and what the SDK does about regions."
|
|
1702
|
+
},
|
|
1703
|
+
{
|
|
1704
|
+
title: "No client data leakage \u2014 what we promise and how we enforce it",
|
|
1705
|
+
url: docUrl("/security/no-leakage-claim"),
|
|
1706
|
+
route: "/security/no-leakage-claim",
|
|
1707
|
+
markdown: "security/no-leakage-claim.md",
|
|
1708
|
+
keywords: ["security", "no-leakage-claim", "leakage", "claim", "no", "client", "data", "we", "promise", "enforce"],
|
|
1709
|
+
headings: ["The three promises", "1. Your data doesn't cross project boundaries", "2. PII is scrubbed before it reaches Postgres or an LLM", "3. Screenshots are behind signed, time-limited URLs", "BYOK \u2014 your API keys run against your account", "Your code stays in your repo", "Screenshot URL host validation", "Vault secret scoping"],
|
|
1710
|
+
excerpt: "Explicit enumeration of Mushi Mushi's privacy boundaries, the gaps we've closed, and the controls that keep client data isolated."
|
|
1711
|
+
},
|
|
1712
|
+
{
|
|
1713
|
+
title: "Prompt-injection defence",
|
|
1714
|
+
url: docUrl("/security/prompt-injection"),
|
|
1715
|
+
route: "/security/prompt-injection",
|
|
1716
|
+
markdown: "security/prompt-injection.md",
|
|
1717
|
+
keywords: ["security", "prompt-injection", "prompt", "injection", "defence"],
|
|
1718
|
+
headings: ["Defence layers", "1. Text sanitizer", "2. Structural XML wrapping", "3. Zod-validated structured output", "4. Vision-channel sanitization", "5. Regression suite"],
|
|
1719
|
+
excerpt: "How Mushi defends its LLM pipeline against prompt injection hidden in user bug reports, and the OWASP LLM01 regression suite that runs on every CI push."
|
|
1720
|
+
},
|
|
1721
|
+
{
|
|
1722
|
+
title: "SOC 2 readiness",
|
|
1723
|
+
url: docUrl("/security/soc2"),
|
|
1724
|
+
route: "/security/soc2",
|
|
1725
|
+
markdown: "security/soc2.md",
|
|
1726
|
+
keywords: ["security", "soc2", "soc", "readiness"],
|
|
1727
|
+
headings: ["Automated controls", "Audit dashboard", "Generating an evidence pack manually", "Data retention policies", "DSAR (Data Subject Access Request)"],
|
|
1728
|
+
excerpt: "Mushi's SOC 2 Type 1 readiness module \u2014 automated evidence for access, retention and encryption controls, DSAR exports, and a quarterly evidence pack."
|
|
28
1729
|
},
|
|
29
1730
|
{
|
|
30
|
-
title: "
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
1731
|
+
title: "Self-hosting",
|
|
1732
|
+
url: docUrl("/self-hosting"),
|
|
1733
|
+
route: "/self-hosting",
|
|
1734
|
+
markdown: "self-hosting/index.md",
|
|
1735
|
+
keywords: ["self-hosting", "self", "hosting"],
|
|
1736
|
+
headings: ["What you'll deploy", "Fastest path \u2014 Docker Compose", "Managed-Supabase path"],
|
|
1737
|
+
excerpt: "Run the whole Mushi Mushi stack yourself with one command \u2014 Docker Compose, your own keys, AGPLv3 server, MIT SDKs, no usage limits."
|
|
34
1738
|
},
|
|
35
1739
|
{
|
|
36
|
-
title: "
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
1740
|
+
title: "Admin SPA deploy",
|
|
1741
|
+
url: docUrl("/self-hosting/admin-spa"),
|
|
1742
|
+
route: "/self-hosting/admin-spa",
|
|
1743
|
+
markdown: "self-hosting/admin-spa.md",
|
|
1744
|
+
keywords: ["self-hosting", "self", "hosting", "admin-spa", "admin", "spa", "deploy"],
|
|
1745
|
+
headings: ["Build", "Deploy options", "SPA routing", "First-run setup"],
|
|
1746
|
+
excerpt: "Build and deploy the Mushi admin console yourself \u2014 a static Vite and React app that talks to your Supabase project, and the env vars it needs."
|
|
40
1747
|
},
|
|
41
1748
|
{
|
|
42
|
-
title: "
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
1749
|
+
title: "Self-host in minutes (Docker Compose)",
|
|
1750
|
+
url: docUrl("/self-hosting/docker-compose"),
|
|
1751
|
+
route: "/self-hosting/docker-compose",
|
|
1752
|
+
markdown: "self-hosting/docker-compose.md",
|
|
1753
|
+
keywords: ["self-hosting", "self", "hosting", "docker-compose", "docker", "compose", "self-host", "minutes"],
|
|
1754
|
+
headings: ["One command", "Clone and configure", "Bring the stack up", "Open the console", "What's running", "Production notes", "Managed Supabase instead"],
|
|
1755
|
+
excerpt: "Self-host the whole Mushi stack with one Docker Compose file \u2014 Postgres, auth, storage, edge functions, the admin console and a Caddy reverse proxy."
|
|
46
1756
|
},
|
|
47
1757
|
{
|
|
48
|
-
title: "
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
1758
|
+
title: "Edge Functions deploy",
|
|
1759
|
+
url: docUrl("/self-hosting/edge-functions"),
|
|
1760
|
+
route: "/self-hosting/edge-functions",
|
|
1761
|
+
markdown: "self-hosting/edge-functions.md",
|
|
1762
|
+
keywords: ["self-hosting", "self", "hosting", "edge-functions", "edge", "functions", "deploy"],
|
|
1763
|
+
headings: ["Minimal required (ingest + classify)", "Checking what is deployed", "Common optional functions", "Function inventory (core pipeline)", "Required secrets", "JWT verification"],
|
|
1764
|
+
excerpt: "Deploy Mushi's Supabase Edge Functions for a self-hosted install \u2014 the minimal ingest and classification set, the full list, and the secrets they need."
|
|
52
1765
|
},
|
|
53
1766
|
{
|
|
54
|
-
title: "
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
1767
|
+
title: "Langfuse + Sentry",
|
|
1768
|
+
url: docUrl("/self-hosting/observability"),
|
|
1769
|
+
route: "/self-hosting/observability",
|
|
1770
|
+
markdown: "self-hosting/observability.md",
|
|
1771
|
+
keywords: ["self-hosting", "self", "hosting", "observability", "langfuse", "sentry"],
|
|
1772
|
+
headings: ["Langfuse (LLM tracing)", "What gets traced", "Viewing traces", "Sentry (error tracking)", "Admin SPA Sentry", "OpenTelemetry", "Health dashboard"],
|
|
1773
|
+
excerpt: "Wire Langfuse LLM tracing and Sentry error tracking into a self-hosted Mushi server \u2014 the Edge Function secrets and what each trace records."
|
|
58
1774
|
},
|
|
59
1775
|
{
|
|
60
|
-
title: "
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
1776
|
+
title: "Supabase setup",
|
|
1777
|
+
url: docUrl("/self-hosting/supabase"),
|
|
1778
|
+
route: "/self-hosting/supabase",
|
|
1779
|
+
markdown: "self-hosting/supabase.md",
|
|
1780
|
+
keywords: ["self-hosting", "self", "hosting", "supabase", "setup"],
|
|
1781
|
+
headings: ["Required extensions", "Required custom functions", "RLS pattern", "Required seed data", "Verifying the setup"],
|
|
1782
|
+
excerpt: "Set up a Supabase project for self-hosted Mushi \u2014 link the CLI, push every migration, and enable the Postgres extensions the server needs."
|
|
64
1783
|
},
|
|
65
1784
|
{
|
|
66
|
-
title: "
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
1785
|
+
title: "Use cases & comparisons",
|
|
1786
|
+
url: docUrl("/use-cases"),
|
|
1787
|
+
route: "/use-cases",
|
|
1788
|
+
markdown: "use-cases/index.md",
|
|
1789
|
+
keywords: ["use-cases", "use", "cases", "comparisons"],
|
|
1790
|
+
headings: [],
|
|
1791
|
+
excerpt: "Where Mushi Mushi fits \u2014 as an open-source Sentry alternative, a debugger for Cursor and Claude Code apps, and an MCP bug-fixing server for your editor."
|
|
70
1792
|
},
|
|
71
1793
|
{
|
|
72
|
-
title: "
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
1794
|
+
title: "AI code bug fixing \u2014 report to merged fix",
|
|
1795
|
+
url: docUrl("/use-cases/ai-code-bug-fixing"),
|
|
1796
|
+
route: "/use-cases/ai-code-bug-fixing",
|
|
1797
|
+
markdown: "use-cases/ai-code-bug-fixing.md",
|
|
1798
|
+
keywords: ["use-cases", "use", "cases", "ai-code-bug-fixing", "code", "bug", "fixing", "ai", "report", "merged", "fix"],
|
|
1799
|
+
headings: ["The pipeline", "Where this fits with your existing tools", "Start with one command"],
|
|
1800
|
+
excerpt: "AI code bug fixing end to end \u2014 user reports become plain-English diagnoses, fix prompts, and optional draft PRs, with lessons that prevent repeats."
|
|
76
1801
|
},
|
|
77
1802
|
{
|
|
78
|
-
title: "
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
1803
|
+
title: "Debug apps built with Claude Code",
|
|
1804
|
+
url: docUrl("/use-cases/debug-claude-code-apps"),
|
|
1805
|
+
route: "/use-cases/debug-claude-code-apps",
|
|
1806
|
+
markdown: "use-cases/debug-claude-code-apps.md",
|
|
1807
|
+
keywords: ["use-cases", "use", "cases", "debug-claude-code-apps", "debug", "claude", "code", "apps", "built"],
|
|
1808
|
+
headings: ["The loop, end to end", "What makes this different from error monitoring", "Set it up"],
|
|
1809
|
+
excerpt: "How to debug an app Claude Code built \u2014 bug reports arrive as plain-English diagnoses your Claude Code agent reads and fixes over MCP, fix prompt included."
|
|
82
1810
|
},
|
|
83
1811
|
{
|
|
84
|
-
title: "
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
1812
|
+
title: "Debug apps built with Cursor",
|
|
1813
|
+
url: docUrl("/use-cases/debug-cursor-apps"),
|
|
1814
|
+
route: "/use-cases/debug-cursor-apps",
|
|
1815
|
+
markdown: "use-cases/debug-cursor-apps.md",
|
|
1816
|
+
keywords: ["use-cases", "use", "cases", "debug-cursor-apps", "debug", "cursor", "apps", "built"],
|
|
1817
|
+
headings: ["The loop, end to end", "Why not just paste the bug into chat?", "Set it up"],
|
|
1818
|
+
excerpt: "How to debug an app Cursor wrote for you \u2014 user bug reports become plain-English diagnoses and fix prompts your Cursor agent applies over MCP."
|
|
88
1819
|
},
|
|
89
1820
|
{
|
|
90
|
-
title: "
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
1821
|
+
title: "Your Lovable app broke in production",
|
|
1822
|
+
url: docUrl("/use-cases/lovable-app-broke-in-production"),
|
|
1823
|
+
route: "/use-cases/lovable-app-broke-in-production",
|
|
1824
|
+
markdown: "use-cases/lovable-app-broke-in-production.md",
|
|
1825
|
+
keywords: ["use-cases", "use", "cases", "lovable-app-broke-in-production", "lovable", "app", "broke", "production"],
|
|
1826
|
+
headings: ["1. Get the user's exact words and their screen", "2. Reproduce in a private window, logged out", "3. Read the first red line in the console, not the last", "4. Find the file and ask what it assumes", "5. Ask for the smallest fix, and a test", "6. Ship, then tell the user", "7. Write the cause down where the agent will read it", "8. Make the next report arrive with all of this attached"],
|
|
1827
|
+
excerpt: "What to do when a Lovable, Bolt, Cursor or Claude Code app breaks for a real user \u2014 reproduce it, read the first error, and ask the agent for the smallest fix."
|
|
94
1828
|
},
|
|
95
1829
|
{
|
|
96
|
-
title: "
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
1830
|
+
title: "MCP server for bug fixing",
|
|
1831
|
+
url: docUrl("/use-cases/mcp-bug-fixing-server"),
|
|
1832
|
+
route: "/use-cases/mcp-bug-fixing-server",
|
|
1833
|
+
markdown: "use-cases/mcp-bug-fixing-server.md",
|
|
1834
|
+
keywords: ["use-cases", "use", "cases", "mcp-bug-fixing-server", "mcp", "bug", "fixing", "server"],
|
|
1835
|
+
headings: ["What the agent can do with it", "Setup", "Hosted or self-hosted"],
|
|
1836
|
+
excerpt: "What an MCP bug-fixing server is and how Mushi's works \u2014 Cursor, Claude Code, or Codex read live bug reports, pull fix context, and dispatch fixes."
|
|
100
1837
|
},
|
|
101
1838
|
{
|
|
102
|
-
title: "
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
1839
|
+
title: "Sentry alternative for AI-built apps",
|
|
1840
|
+
url: docUrl("/use-cases/sentry-alternative"),
|
|
1841
|
+
route: "/use-cases/sentry-alternative",
|
|
1842
|
+
markdown: "use-cases/sentry-alternative.md",
|
|
1843
|
+
keywords: ["use-cases", "use", "cases", "sentry-alternative", "sentry", "alternative", "ai-built", "apps"],
|
|
1844
|
+
headings: ["Instead of Sentry, or alongside it \u2014 your call", "Side by side", 'What "diagnosis" means here', "Try it on your app"],
|
|
1845
|
+
excerpt: "An open-source Sentry alternative for AI-built apps \u2014 plain-English bug diagnoses and ready fixes instead of raw stack traces. Works with or without Sentry."
|
|
106
1846
|
}
|
|
107
1847
|
];
|
|
1848
|
+
var QUERY_STOPWORDS = /* @__PURE__ */ new Set(["a", "an", "and", "are", "can", "do", "does", "for", "from", "how", "i", "in", "is", "it", "me", "my", "of", "on", "or", "the", "to", "use", "using", "what", "when", "where", "which", "why", "with", "you", "your", "mushi"]);
|
|
108
1849
|
function searchMushiDocs(query, limit = 8) {
|
|
109
1850
|
const q = query.trim().toLowerCase();
|
|
110
1851
|
if (!q) {
|
|
111
1852
|
return MUSHI_DOCS_INDEX.slice(0, limit).map((e) => ({ ...e, score: 0 }));
|
|
112
1853
|
}
|
|
113
|
-
const
|
|
1854
|
+
const allTerms = q.split(/[\s?!,;:()"']+/).filter(Boolean);
|
|
1855
|
+
const meaningful = allTerms.filter((t) => !QUERY_STOPWORDS.has(t));
|
|
1856
|
+
const terms = meaningful.length > 0 ? meaningful : allTerms;
|
|
114
1857
|
const scored = MUSHI_DOCS_INDEX.map((entry) => {
|
|
115
|
-
const
|
|
1858
|
+
const title = entry.title.toLowerCase();
|
|
1859
|
+
const headings = entry.headings.join(" ").toLowerCase();
|
|
1860
|
+
const hay = (entry.title + " " + entry.keywords.join(" ") + " " + headings + " " + entry.excerpt).toLowerCase();
|
|
116
1861
|
let score = 0;
|
|
117
1862
|
for (const term of terms) {
|
|
118
|
-
if (
|
|
119
|
-
if (entry.keywords.some((k) => k
|
|
1863
|
+
if (title.includes(term)) score += 4;
|
|
1864
|
+
if (entry.keywords.some((k) => k === term)) score += 4;
|
|
1865
|
+
else if (entry.keywords.some((k) => k.includes(term))) score += 2;
|
|
1866
|
+
if (headings.includes(term)) score += 2;
|
|
120
1867
|
if (hay.includes(term)) score += 1;
|
|
121
1868
|
}
|
|
1869
|
+
if (terms.length > 1 && terms.every((t) => hay.includes(t))) score += 3;
|
|
1870
|
+
if (score > 0 && entry.route.split("/").length <= 3) score += 1;
|
|
122
1871
|
return { ...entry, score };
|
|
123
|
-
}).filter((e) => e.score > 0).sort((a, b) => b.score - a.score);
|
|
1872
|
+
}).filter((e) => e.score > 0).sort((a, b) => b.score - a.score || a.title.localeCompare(b.title));
|
|
124
1873
|
return scored.slice(0, limit);
|
|
125
1874
|
}
|
|
1875
|
+
function findMushiDoc(page) {
|
|
1876
|
+
const raw = page.trim().split(/[?#]/)[0] ?? "";
|
|
1877
|
+
const route = raw.startsWith(MUSHI_DOCS_BASE) ? raw.slice(MUSHI_DOCS_BASE.length) || "/" : raw;
|
|
1878
|
+
const normalised = route === "/" ? "/" : "/" + route.replace(/^\/+|\/+$/g, "");
|
|
1879
|
+
return MUSHI_DOCS_INDEX.find((e) => e.route === normalised) ?? null;
|
|
1880
|
+
}
|
|
1881
|
+
function mushiDocMarkdownUrl(entry) {
|
|
1882
|
+
return MUSHI_DOCS_BASE + "/llm-md/" + entry.markdown;
|
|
1883
|
+
}
|
|
126
1884
|
|
|
127
1885
|
// src/wrap-untrusted.ts
|
|
128
1886
|
function wrapUntrusted(content, role) {
|
|
@@ -209,9 +1967,295 @@ function safeStringify(value) {
|
|
|
209
1967
|
}
|
|
210
1968
|
}
|
|
211
1969
|
|
|
1970
|
+
// src/arg-aliases.ts
|
|
1971
|
+
function snakeAliasOf(name) {
|
|
1972
|
+
return name.replace(/[A-Z]/g, (c) => `_${c.toLowerCase()}`);
|
|
1973
|
+
}
|
|
1974
|
+
function normalizeArgAliases(args, declared) {
|
|
1975
|
+
const out = { ...args };
|
|
1976
|
+
for (const name of declared) {
|
|
1977
|
+
const alias = snakeAliasOf(name);
|
|
1978
|
+
if (alias === name || !Object.prototype.hasOwnProperty.call(out, alias)) continue;
|
|
1979
|
+
if (out[name] === void 0) out[name] = out[alias];
|
|
1980
|
+
delete out[alias];
|
|
1981
|
+
}
|
|
1982
|
+
return out;
|
|
1983
|
+
}
|
|
1984
|
+
|
|
1985
|
+
// src/report-shapes.ts
|
|
1986
|
+
var REPORT_LIST_FIELDS = [
|
|
1987
|
+
"id",
|
|
1988
|
+
"status",
|
|
1989
|
+
"category",
|
|
1990
|
+
"severity",
|
|
1991
|
+
"summary",
|
|
1992
|
+
"component",
|
|
1993
|
+
"created_at",
|
|
1994
|
+
"processing_error"
|
|
1995
|
+
];
|
|
1996
|
+
var REPORTER_IDENTITY_FIELDS = [
|
|
1997
|
+
"end_user_id",
|
|
1998
|
+
"reporter_token_hash",
|
|
1999
|
+
"session_id",
|
|
2000
|
+
"reporter_display_name",
|
|
2001
|
+
"reporter_user_id",
|
|
2002
|
+
"reporter_identity",
|
|
2003
|
+
"tester_id"
|
|
2004
|
+
];
|
|
2005
|
+
var REPORT_DETAIL_FIELDS = [
|
|
2006
|
+
"id",
|
|
2007
|
+
"project_id",
|
|
2008
|
+
"title",
|
|
2009
|
+
"summary",
|
|
2010
|
+
"description",
|
|
2011
|
+
"status",
|
|
2012
|
+
"category",
|
|
2013
|
+
"severity",
|
|
2014
|
+
"component",
|
|
2015
|
+
"area_tag",
|
|
2016
|
+
"source",
|
|
2017
|
+
"confidence",
|
|
2018
|
+
"user_category",
|
|
2019
|
+
"user_intent",
|
|
2020
|
+
"voice_transcript",
|
|
2021
|
+
"created_at",
|
|
2022
|
+
"updated_at",
|
|
2023
|
+
"fixed_at",
|
|
2024
|
+
"verified_at",
|
|
2025
|
+
"reopened_at",
|
|
2026
|
+
"regressed_at",
|
|
2027
|
+
"regression_count",
|
|
2028
|
+
"screenshot_url",
|
|
2029
|
+
"environment",
|
|
2030
|
+
"console_logs",
|
|
2031
|
+
"network_logs",
|
|
2032
|
+
"performance_metrics",
|
|
2033
|
+
"breadcrumbs",
|
|
2034
|
+
"repro_timeline",
|
|
2035
|
+
"selected_element",
|
|
2036
|
+
"tags",
|
|
2037
|
+
"app_version",
|
|
2038
|
+
"sdk_package",
|
|
2039
|
+
"sdk_version",
|
|
2040
|
+
"stage1_classification",
|
|
2041
|
+
"stage2_analysis",
|
|
2042
|
+
"reproduction_steps",
|
|
2043
|
+
"bug_ontology_tags",
|
|
2044
|
+
"extracted_symptoms",
|
|
2045
|
+
"vision_analysis",
|
|
2046
|
+
"judge_score",
|
|
2047
|
+
"judge_eval",
|
|
2048
|
+
"processing_error",
|
|
2049
|
+
"fix_branch",
|
|
2050
|
+
"fix_pr_url",
|
|
2051
|
+
"fix_commit_sha",
|
|
2052
|
+
"fix_attempts",
|
|
2053
|
+
"fix_packet",
|
|
2054
|
+
"inventory_action",
|
|
2055
|
+
"sentry_event_id",
|
|
2056
|
+
"sentry_trace_id",
|
|
2057
|
+
"sentry_replay_id",
|
|
2058
|
+
"sentry_issue_url",
|
|
2059
|
+
"sentry_release",
|
|
2060
|
+
"report_group_id",
|
|
2061
|
+
"parent_report_id",
|
|
2062
|
+
"child_report_ids",
|
|
2063
|
+
"backend_spans",
|
|
2064
|
+
"anomalies"
|
|
2065
|
+
];
|
|
2066
|
+
var IDENTITY = new Set(REPORTER_IDENTITY_FIELDS);
|
|
2067
|
+
function spansWithoutSession(spans) {
|
|
2068
|
+
if (!Array.isArray(spans)) return spans;
|
|
2069
|
+
return spans.map((span) => {
|
|
2070
|
+
if (!span || typeof span !== "object" || Array.isArray(span)) return span;
|
|
2071
|
+
const { session_id: _session, ...rest } = span;
|
|
2072
|
+
return rest;
|
|
2073
|
+
});
|
|
2074
|
+
}
|
|
2075
|
+
function projectReportListRow(row, includeRaw) {
|
|
2076
|
+
if (includeRaw) return Object.fromEntries(Object.entries(row).filter(([k]) => !IDENTITY.has(k)));
|
|
2077
|
+
const out = {};
|
|
2078
|
+
for (const field of REPORT_LIST_FIELDS) {
|
|
2079
|
+
if (row[field] !== void 0) out[field] = row[field];
|
|
2080
|
+
}
|
|
2081
|
+
return out;
|
|
2082
|
+
}
|
|
2083
|
+
function projectReportDetail(row, includeRaw) {
|
|
2084
|
+
const out = {};
|
|
2085
|
+
if (includeRaw) {
|
|
2086
|
+
for (const [k, v] of Object.entries(row)) if (!IDENTITY.has(k)) out[k] = v;
|
|
2087
|
+
} else {
|
|
2088
|
+
for (const field of REPORT_DETAIL_FIELDS) if (row[field] !== void 0) out[field] = row[field];
|
|
2089
|
+
}
|
|
2090
|
+
if (out.backend_spans !== void 0) out.backend_spans = spansWithoutSession(out.backend_spans);
|
|
2091
|
+
return out;
|
|
2092
|
+
}
|
|
2093
|
+
function reportEvidenceOf(report, reportId) {
|
|
2094
|
+
return {
|
|
2095
|
+
report_id: reportId,
|
|
2096
|
+
title: report.title ?? null,
|
|
2097
|
+
summary: report.summary ?? null,
|
|
2098
|
+
description: report.description ?? null,
|
|
2099
|
+
status: report.status ?? null,
|
|
2100
|
+
severity: report.severity ?? null,
|
|
2101
|
+
category: report.category ?? null,
|
|
2102
|
+
screenshot_url: report.screenshot_url ?? null,
|
|
2103
|
+
environment: report.environment ?? null,
|
|
2104
|
+
// LOGS
|
|
2105
|
+
console_logs: report.console_logs ?? null,
|
|
2106
|
+
breadcrumbs: report.breadcrumbs ?? null,
|
|
2107
|
+
repro_timeline: report.repro_timeline ?? null,
|
|
2108
|
+
// TRACES
|
|
2109
|
+
network_requests: report.network_logs ?? null,
|
|
2110
|
+
backend_spans: spansWithoutSession(report.backend_spans ?? null),
|
|
2111
|
+
sentry_replay_id: report.sentry_replay_id ?? null,
|
|
2112
|
+
sentry_trace_id: report.sentry_trace_id ?? null,
|
|
2113
|
+
sentry_event_id: report.sentry_event_id ?? null,
|
|
2114
|
+
sentry_release: report.sentry_release ?? null,
|
|
2115
|
+
// METRICS
|
|
2116
|
+
performance_metrics: report.performance_metrics ?? null,
|
|
2117
|
+
anomalies: report.anomalies ?? null,
|
|
2118
|
+
// context
|
|
2119
|
+
tags: report.tags ?? null,
|
|
2120
|
+
created_at: report.created_at ?? null
|
|
2121
|
+
};
|
|
2122
|
+
}
|
|
2123
|
+
function fixContextOf(report) {
|
|
2124
|
+
return {
|
|
2125
|
+
// Paste-ready fix prompt composed server-side by composeFixPacket()
|
|
2126
|
+
// (diagnosis + repro + suggested fix + relevant code + blast radius).
|
|
2127
|
+
fixPrompt: report.fix_packet ?? null,
|
|
2128
|
+
reproductionSteps: report.reproduction_steps ?? [],
|
|
2129
|
+
component: report.component ?? null,
|
|
2130
|
+
rootCause: report.stage2_analysis?.rootCause ?? null,
|
|
2131
|
+
bugOntologyTags: report.bug_ontology_tags ?? null
|
|
2132
|
+
};
|
|
2133
|
+
}
|
|
2134
|
+
function similarityQueryOf(report) {
|
|
2135
|
+
for (const field of ["summary", "description"]) {
|
|
2136
|
+
const value = report[field];
|
|
2137
|
+
if (typeof value === "string" && value.trim()) return value.trim().slice(0, 2e3);
|
|
2138
|
+
}
|
|
2139
|
+
return null;
|
|
2140
|
+
}
|
|
2141
|
+
function inventoryActionNodeIdOf(report) {
|
|
2142
|
+
const anchor = report.inventory_action;
|
|
2143
|
+
return typeof anchor?.actionNodeId === "string" && anchor.actionNodeId ? anchor.actionNodeId : null;
|
|
2144
|
+
}
|
|
2145
|
+
function triageRecommendedActions(report, reportId) {
|
|
2146
|
+
if (!report) return [];
|
|
2147
|
+
const actions = [];
|
|
2148
|
+
const status = typeof report.status === "string" ? report.status : "";
|
|
2149
|
+
const processingError = typeof report.processing_error === "string" ? report.processing_error : null;
|
|
2150
|
+
const attempts = Array.isArray(report.fix_attempts) ? report.fix_attempts : [];
|
|
2151
|
+
const latest = attempts[0] ?? null;
|
|
2152
|
+
const latestStatus = latest ? String(latest.status ?? "") : "";
|
|
2153
|
+
const blocked = (processingError?.startsWith("autofix_blocked:") ?? false) || latestStatus.startsWith("skipped") || latestStatus === "failed";
|
|
2154
|
+
if (blocked && status !== "fixing" && status !== "fixed") {
|
|
2155
|
+
const why = processingError || String(latest?.error ?? "the previous fix attempt was skipped or failed");
|
|
2156
|
+
actions.push({
|
|
2157
|
+
action: "unblock_autofix",
|
|
2158
|
+
reason: `Auto-fix is blocked: ${why}. Resolve the blocker (settings/integration/budget), then re-dispatch.`,
|
|
2159
|
+
tool: "diagnose_setup",
|
|
2160
|
+
args: { mode: "dispatch" }
|
|
2161
|
+
});
|
|
2162
|
+
actions.push({
|
|
2163
|
+
action: "redispatch_after_unblock",
|
|
2164
|
+
reason: "Once the blocker above is resolved, dispatch a fresh fix attempt.",
|
|
2165
|
+
tool: "dispatch_fix",
|
|
2166
|
+
args: { reportId }
|
|
2167
|
+
});
|
|
2168
|
+
} else if (status === "new" || status === "classified" || status === "triaged" || status === "reopened") {
|
|
2169
|
+
actions.push({
|
|
2170
|
+
action: "dispatch_fix",
|
|
2171
|
+
reason: "The report is classified and has no fix in flight.",
|
|
2172
|
+
tool: "dispatch_fix",
|
|
2173
|
+
args: { reportId }
|
|
2174
|
+
});
|
|
2175
|
+
} else if (status === "fixing") {
|
|
2176
|
+
actions.push(
|
|
2177
|
+
typeof latest?.id === "string" ? {
|
|
2178
|
+
action: "check_fix_progress",
|
|
2179
|
+
reason: "A fix is in progress \u2014 check its timeline.",
|
|
2180
|
+
tool: "get_fix_timeline",
|
|
2181
|
+
args: { fixId: latest.id }
|
|
2182
|
+
} : {
|
|
2183
|
+
action: "check_fix_progress",
|
|
2184
|
+
reason: "A fix is in progress \u2014 check the report timeline.",
|
|
2185
|
+
tool: "get_report_timeline",
|
|
2186
|
+
args: { reportId }
|
|
2187
|
+
}
|
|
2188
|
+
);
|
|
2189
|
+
} else if (status === "fixed") {
|
|
2190
|
+
actions.push({
|
|
2191
|
+
action: "verify_fix",
|
|
2192
|
+
reason: "A fix was applied \u2014 verify it resolved the issue, then mark the report verified (or reopen it).",
|
|
2193
|
+
tool: "get_fix_context",
|
|
2194
|
+
args: { reportId }
|
|
2195
|
+
});
|
|
2196
|
+
}
|
|
2197
|
+
const actionNodeId = inventoryActionNodeIdOf(report);
|
|
2198
|
+
if ((report.severity === "critical" || report.severity === "high") && actionNodeId) {
|
|
2199
|
+
actions.push({
|
|
2200
|
+
action: "check_blast_radius",
|
|
2201
|
+
reason: "High severity \u2014 check what else the affected inventory action touches.",
|
|
2202
|
+
tool: "get_blast_radius",
|
|
2203
|
+
args: { nodeId: actionNodeId }
|
|
2204
|
+
});
|
|
2205
|
+
}
|
|
2206
|
+
return actions;
|
|
2207
|
+
}
|
|
2208
|
+
function triageSummaryOf(report, actions) {
|
|
2209
|
+
if (!report) return "Could not fetch the report \u2014 see partial_errors.";
|
|
2210
|
+
const severity = typeof report.severity === "string" ? report.severity : "unknown";
|
|
2211
|
+
const category = typeof report.category === "string" ? report.category : "unknown";
|
|
2212
|
+
const status = typeof report.status === "string" ? report.status : "unknown";
|
|
2213
|
+
const next = actions[0] ? `Recommended: ${actions[0].action}.` : "No action required.";
|
|
2214
|
+
return `[${severity}] ${category} \u2014 status: ${status}. ${next}`;
|
|
2215
|
+
}
|
|
2216
|
+
|
|
212
2217
|
// src/server.ts
|
|
213
2218
|
installStdoutGuard();
|
|
214
2219
|
var NO_ARG_INPUT = z.object({});
|
|
2220
|
+
var RENAMED_PARAMS = /* @__PURE__ */ new Set([
|
|
2221
|
+
"projectId",
|
|
2222
|
+
"reportId",
|
|
2223
|
+
"includeRaw",
|
|
2224
|
+
"includeLogs",
|
|
2225
|
+
"diffText",
|
|
2226
|
+
"maxTokens",
|
|
2227
|
+
"topK",
|
|
2228
|
+
"runId",
|
|
2229
|
+
"stepIndex",
|
|
2230
|
+
"prUrl",
|
|
2231
|
+
"agentRef",
|
|
2232
|
+
"threadId",
|
|
2233
|
+
"filePath",
|
|
2234
|
+
"symbolName",
|
|
2235
|
+
"scopePrefix",
|
|
2236
|
+
"fixId",
|
|
2237
|
+
"externalUserId",
|
|
2238
|
+
"tierSlug",
|
|
2239
|
+
"rootSkillSlug"
|
|
2240
|
+
]);
|
|
2241
|
+
function acceptSnakeCaseAliases(schema) {
|
|
2242
|
+
if (!(schema instanceof z.ZodObject)) return schema;
|
|
2243
|
+
const shape = schema.shape;
|
|
2244
|
+
const declared = Object.keys(shape).filter((key) => /[A-Z]/.test(key));
|
|
2245
|
+
if (declared.length === 0) return schema;
|
|
2246
|
+
const documented = {};
|
|
2247
|
+
for (const key of declared) {
|
|
2248
|
+
if (!RENAMED_PARAMS.has(key)) continue;
|
|
2249
|
+
const field = shape[key];
|
|
2250
|
+
const note = `(\`${snakeAliasOf(key)}\` is accepted too.)`;
|
|
2251
|
+
documented[key] = field.describe(field.description ? `${field.description} ${note}` : note);
|
|
2252
|
+
}
|
|
2253
|
+
const target = Object.keys(documented).length > 0 ? schema.extend(documented) : schema;
|
|
2254
|
+
return z.preprocess(
|
|
2255
|
+
(value) => value !== null && typeof value === "object" && !Array.isArray(value) ? normalizeArgAliases(value, declared) : value,
|
|
2256
|
+
target
|
|
2257
|
+
);
|
|
2258
|
+
}
|
|
215
2259
|
var DEFAULT_API_TIMEOUT_MS = 15e3;
|
|
216
2260
|
var MIN_API_TIMEOUT_MS = 250;
|
|
217
2261
|
var MAX_API_TIMEOUT_MS = 3e5;
|
|
@@ -254,6 +2298,104 @@ var MushiApiError = class extends Error {
|
|
|
254
2298
|
status;
|
|
255
2299
|
code;
|
|
256
2300
|
};
|
|
2301
|
+
var MUSHI_DOC_MAX_CHARS = 8e3;
|
|
2302
|
+
var REPORT_STATUSES = [
|
|
2303
|
+
"new",
|
|
2304
|
+
"pending",
|
|
2305
|
+
"submitted",
|
|
2306
|
+
"queued",
|
|
2307
|
+
"classified",
|
|
2308
|
+
"grouped",
|
|
2309
|
+
"fixing",
|
|
2310
|
+
"fixed",
|
|
2311
|
+
"dismissed",
|
|
2312
|
+
"triaged",
|
|
2313
|
+
"in_progress",
|
|
2314
|
+
"resolved",
|
|
2315
|
+
"verified",
|
|
2316
|
+
"reopened"
|
|
2317
|
+
];
|
|
2318
|
+
var REPORT_CATEGORIES = ["bug", "slow", "visual", "confusing", "other"];
|
|
2319
|
+
var REPORT_SEVERITIES = ["critical", "high", "medium", "low"];
|
|
2320
|
+
var GATE_IDS = [
|
|
2321
|
+
"dead_handler",
|
|
2322
|
+
"mock_leak",
|
|
2323
|
+
"api_contract",
|
|
2324
|
+
"crawl",
|
|
2325
|
+
"status_claim",
|
|
2326
|
+
"spec_drift",
|
|
2327
|
+
"orphan_endpoint",
|
|
2328
|
+
"unknown_call",
|
|
2329
|
+
"schema_drift",
|
|
2330
|
+
"code_health"
|
|
2331
|
+
];
|
|
2332
|
+
var GATE_FINDING_SEVERITIES = ["info", "warn", "error"];
|
|
2333
|
+
var SKILL_CATEGORIES = [
|
|
2334
|
+
"workflow",
|
|
2335
|
+
"debug",
|
|
2336
|
+
"test",
|
|
2337
|
+
"audit",
|
|
2338
|
+
"enhance",
|
|
2339
|
+
"backend",
|
|
2340
|
+
"design",
|
|
2341
|
+
"deploy",
|
|
2342
|
+
"data",
|
|
2343
|
+
"mobile",
|
|
2344
|
+
"docs",
|
|
2345
|
+
"meta",
|
|
2346
|
+
"mushi",
|
|
2347
|
+
"protocol",
|
|
2348
|
+
"iterate",
|
|
2349
|
+
"other"
|
|
2350
|
+
];
|
|
2351
|
+
var CODEBASE_SEARCH_MODES = ["semantic", "name"];
|
|
2352
|
+
var DISPATCH_AGENTS = [
|
|
2353
|
+
"auto",
|
|
2354
|
+
"claude_code",
|
|
2355
|
+
"codex",
|
|
2356
|
+
"cursor_cloud",
|
|
2357
|
+
"github_cloud_agent",
|
|
2358
|
+
"rest_worker",
|
|
2359
|
+
"rest_fix_worker",
|
|
2360
|
+
"llm",
|
|
2361
|
+
"mcp"
|
|
2362
|
+
];
|
|
2363
|
+
var nullableString = () => z.string().nullable().optional();
|
|
2364
|
+
var REPORT_DETAIL_OUTPUT = z.looseObject({
|
|
2365
|
+
id: z.string().describe("Report UUID"),
|
|
2366
|
+
project_id: nullableString(),
|
|
2367
|
+
title: nullableString(),
|
|
2368
|
+
summary: nullableString().describe("One-line summary written by the classifier"),
|
|
2369
|
+
description: nullableString().describe("What the reporter wrote (untrusted text)"),
|
|
2370
|
+
status: nullableString().describe("Workflow status, e.g. new, classified, fixing, fixed, verified, reopened"),
|
|
2371
|
+
category: nullableString().describe("bug | slow | visual | confusing | other"),
|
|
2372
|
+
severity: nullableString().describe("critical | high | medium | low"),
|
|
2373
|
+
component: nullableString().describe("Component or page the bug was attributed to"),
|
|
2374
|
+
created_at: nullableString(),
|
|
2375
|
+
screenshot_url: nullableString().describe("Signed screenshot URL"),
|
|
2376
|
+
environment: z.unknown().optional().describe("Browser/OS/viewport the SDK captured"),
|
|
2377
|
+
console_logs: z.unknown().optional().describe("Console entries captured with the report"),
|
|
2378
|
+
network_logs: z.unknown().optional().describe("Network requests captured with the report"),
|
|
2379
|
+
breadcrumbs: z.unknown().optional(),
|
|
2380
|
+
stage1_classification: z.unknown().optional().describe("Stage-1 (fast) classification"),
|
|
2381
|
+
stage2_analysis: z.unknown().optional().describe("Stage-2 analysis: rootCause, suggestedFix, \u2026"),
|
|
2382
|
+
reproduction_steps: z.unknown().optional(),
|
|
2383
|
+
fix_attempts: z.array(z.looseObject({ id: z.string(), status: nullableString() })).optional().describe("Latest fix attempts, newest first"),
|
|
2384
|
+
fix_packet: z.unknown().optional().describe("Paste-ready fix prompt composed server-side"),
|
|
2385
|
+
inventory_action: z.unknown().optional().describe("Inventory action the report is filed against, when resolved"),
|
|
2386
|
+
child_report_ids: z.array(z.string()).optional()
|
|
2387
|
+
});
|
|
2388
|
+
var FIX_CONTEXT_SHAPE = {
|
|
2389
|
+
fixPrompt: z.unknown().describe("Paste-ready fix prompt composed server-side, or null"),
|
|
2390
|
+
reproductionSteps: z.unknown().describe("Reproduction steps recorded on the report ([] if none)"),
|
|
2391
|
+
component: z.string().nullable().describe("Component or page the bug was attributed to"),
|
|
2392
|
+
rootCause: z.unknown().describe("Stage-2 root cause, or null before Stage 2 runs"),
|
|
2393
|
+
bugOntologyTags: z.unknown().describe("Bug ontology tags, or null")
|
|
2394
|
+
};
|
|
2395
|
+
function isWrappedUntrusted(text) {
|
|
2396
|
+
return text.startsWith('<mushi-data role="');
|
|
2397
|
+
}
|
|
2398
|
+
var toolCallContext = new AsyncLocalStorage();
|
|
257
2399
|
function createMushiServer(config) {
|
|
258
2400
|
const { version, apiEndpoint, apiKey, projectId } = config;
|
|
259
2401
|
const doFetch = config.fetch ?? globalThis.fetch;
|
|
@@ -276,6 +2418,7 @@ function createMushiServer(config) {
|
|
|
276
2418
|
const requestId = crypto.randomUUID().slice(0, 12);
|
|
277
2419
|
const started = Date.now();
|
|
278
2420
|
const timeoutSignal = AbortSignal.timeout(timeoutMs);
|
|
2421
|
+
const toolCall = toolCallContext.getStore();
|
|
279
2422
|
let res;
|
|
280
2423
|
try {
|
|
281
2424
|
res = await doFetch(`${apiEndpoint}${path}`, {
|
|
@@ -288,6 +2431,11 @@ function createMushiServer(config) {
|
|
|
288
2431
|
Authorization: `Bearer ${apiKey}`,
|
|
289
2432
|
"X-Mushi-Api-Key": apiKey,
|
|
290
2433
|
"X-Request-Id": requestId,
|
|
2434
|
+
// Lets the api count stdio tool use once per call and credit the
|
|
2435
|
+
// funnel steps (report opened, fix pulled, fix dispatched) that the
|
|
2436
|
+
// hosted transport already credits. Tool name and a random id only.
|
|
2437
|
+
"X-Mushi-Client": `mcp-stdio/${version}`,
|
|
2438
|
+
...toolCall ? { "X-Mushi-Mcp-Tool": toolCall.tool, "X-Mushi-Mcp-Invocation": toolCall.invocationId } : {},
|
|
291
2439
|
...projectId ? { "X-Mushi-Project-Id": projectId } : {},
|
|
292
2440
|
...options?.headers ?? {}
|
|
293
2441
|
},
|
|
@@ -449,13 +2597,26 @@ function createMushiServer(config) {
|
|
|
449
2597
|
structuredContent: value
|
|
450
2598
|
};
|
|
451
2599
|
}
|
|
452
|
-
const server = new McpServer(
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
2600
|
+
const server = new McpServer(
|
|
2601
|
+
{
|
|
2602
|
+
name: MUSHI_SERVER_METADATA.name,
|
|
2603
|
+
version,
|
|
2604
|
+
title: MUSHI_SERVER_METADATA.title,
|
|
2605
|
+
websiteUrl: MUSHI_SERVER_METADATA.websiteUrl,
|
|
2606
|
+
icons: [...MUSHI_SERVER_METADATA.icons]
|
|
2607
|
+
},
|
|
2608
|
+
// Returned in `initialize`; used to be null on stdio.
|
|
2609
|
+
{ instructions: config.instructions ?? MUSHI_SERVER_INSTRUCTIONS }
|
|
2610
|
+
);
|
|
2611
|
+
const registerTool = server.registerTool.bind(server);
|
|
2612
|
+
server.registerTool = ((name, toolConfig, handler) => registerTool(
|
|
2613
|
+
name,
|
|
2614
|
+
{ ...toolConfig, inputSchema: acceptSnakeCaseAliases(toolConfig.inputSchema) },
|
|
2615
|
+
((...args) => toolCallContext.run(
|
|
2616
|
+
{ tool: name, invocationId: crypto.randomUUID() },
|
|
2617
|
+
() => handler(...args)
|
|
2618
|
+
))
|
|
2619
|
+
));
|
|
459
2620
|
const ALL_TOOL_CATALOG = [...TOOL_CATALOG, ...TDD_TOOL_CATALOG, ...CODEBASE_TOOL_CATALOG];
|
|
460
2621
|
function annotationsFor(name, catalog = ALL_TOOL_CATALOG) {
|
|
461
2622
|
const spec = catalog.find((t) => t.name === name);
|
|
@@ -486,16 +2647,29 @@ function createMushiServer(config) {
|
|
|
486
2647
|
description: descOf("get_recent_reports"),
|
|
487
2648
|
annotations: annotationsFor("get_recent_reports"),
|
|
488
2649
|
inputSchema: z.object({
|
|
489
|
-
status: z.
|
|
490
|
-
category: z.
|
|
491
|
-
severity: z.
|
|
2650
|
+
status: z.enum(REPORT_STATUSES).optional().describe('Filter by status. "new" also matches queued rows; "classified" and "fixed" include legacy aliases.'),
|
|
2651
|
+
category: z.enum(REPORT_CATEGORIES).optional().describe("Filter by category."),
|
|
2652
|
+
severity: z.enum(REPORT_SEVERITIES).optional().describe("Filter by severity."),
|
|
492
2653
|
limit: z.number().optional().describe("Max reports to return (default 20, max 100)"),
|
|
493
|
-
|
|
494
|
-
"
|
|
2654
|
+
includeRaw: z.boolean().optional().describe(
|
|
2655
|
+
"Return every column the list route has (breadcrumbs, environment, tags, \u2026) instead of the documented fields. Reporter identifiers are removed either way."
|
|
2656
|
+
),
|
|
2657
|
+
projectId: z.string().optional().describe(
|
|
2658
|
+
"Project UUID \u2014 defaults to the server-configured project. Useful when your key spans several projects; the error you get without it lists their ids."
|
|
495
2659
|
)
|
|
496
2660
|
}),
|
|
497
2661
|
outputSchema: z.object({
|
|
498
|
-
reports: z.array(
|
|
2662
|
+
reports: z.array(
|
|
2663
|
+
z.looseObject({
|
|
2664
|
+
id: z.string(),
|
|
2665
|
+
status: z.string().nullable().optional(),
|
|
2666
|
+
category: z.string().nullable().optional(),
|
|
2667
|
+
severity: z.string().nullable().optional(),
|
|
2668
|
+
summary: z.string().nullable().optional(),
|
|
2669
|
+
component: z.string().nullable().optional(),
|
|
2670
|
+
created_at: z.string().nullable().optional()
|
|
2671
|
+
})
|
|
2672
|
+
),
|
|
499
2673
|
total: z.number()
|
|
500
2674
|
})
|
|
501
2675
|
},
|
|
@@ -505,7 +2679,7 @@ function createMushiServer(config) {
|
|
|
505
2679
|
if (args.category) params.set("category", args.category);
|
|
506
2680
|
if (args.severity) params.set("severity", args.severity);
|
|
507
2681
|
params.set("limit", String(Math.min(args.limit ?? 20, 100)));
|
|
508
|
-
const pid = await resolveProjectId(args.
|
|
2682
|
+
const pid = await resolveProjectId(args.projectId);
|
|
509
2683
|
const extraHeaders = pid !== projectId ? { "X-Mushi-Project-Id": pid } : {};
|
|
510
2684
|
const data = await apiCall(
|
|
511
2685
|
`/v1/admin/reports?${params}`,
|
|
@@ -513,7 +2687,10 @@ function createMushiServer(config) {
|
|
|
513
2687
|
headers: extraHeaders
|
|
514
2688
|
}
|
|
515
2689
|
);
|
|
516
|
-
return jsonResult(
|
|
2690
|
+
return jsonResult({
|
|
2691
|
+
reports: (data.reports ?? []).map((row) => projectReportListRow(row, args.includeRaw === true)),
|
|
2692
|
+
total: data.total ?? 0
|
|
2693
|
+
});
|
|
517
2694
|
}
|
|
518
2695
|
);
|
|
519
2696
|
server.registerTool(
|
|
@@ -524,23 +2701,17 @@ function createMushiServer(config) {
|
|
|
524
2701
|
annotations: annotationsFor("get_report_detail"),
|
|
525
2702
|
inputSchema: z.object({
|
|
526
2703
|
reportId: z.string().describe("The report UUID"),
|
|
527
|
-
|
|
2704
|
+
includeRaw: z.boolean().optional().describe(
|
|
2705
|
+
"Return every column the detail route has (LLM invocation log, storage paths, custom metadata, \u2026) instead of the documented fields. Reporter identifiers are removed either way."
|
|
2706
|
+
),
|
|
2707
|
+
projectId: z.string().optional().describe("Project UUID \u2014 required for org-scoped keys with multiple projects.")
|
|
528
2708
|
}),
|
|
529
|
-
outputSchema: z.object({ report:
|
|
2709
|
+
outputSchema: z.object({ report: REPORT_DETAIL_OUTPUT })
|
|
530
2710
|
},
|
|
531
2711
|
async (args) => {
|
|
532
|
-
const { headers } = await projectScopeHeaders(args.
|
|
2712
|
+
const { headers } = await projectScopeHeaders(args.projectId);
|
|
533
2713
|
const data = await apiCall(`/v1/admin/reports/${args.reportId}`, { headers });
|
|
534
|
-
|
|
535
|
-
return {
|
|
536
|
-
...res,
|
|
537
|
-
resource_links: [
|
|
538
|
-
{
|
|
539
|
-
uri: `project://reports/${args.reportId}`,
|
|
540
|
-
title: `Report ${args.reportId.slice(0, 8)}\u2026`
|
|
541
|
-
}
|
|
542
|
-
]
|
|
543
|
-
};
|
|
2714
|
+
return wrappedJsonResult({ report: projectReportDetail(data, args.includeRaw === true) }, "report body");
|
|
544
2715
|
}
|
|
545
2716
|
);
|
|
546
2717
|
server.registerTool(
|
|
@@ -626,33 +2797,24 @@ function createMushiServer(config) {
|
|
|
626
2797
|
annotations: annotationsFor("get_fix_context"),
|
|
627
2798
|
inputSchema: z.object({
|
|
628
2799
|
reportId: z.string().describe("The report UUID to fix"),
|
|
629
|
-
|
|
2800
|
+
projectId: z.string().optional().describe("Project UUID \u2014 required for org-scoped keys with multiple projects.")
|
|
630
2801
|
}),
|
|
631
2802
|
outputSchema: z.object({
|
|
632
|
-
report:
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
component: z.unknown(),
|
|
636
|
-
rootCause: z.unknown(),
|
|
637
|
-
bugOntologyTags: z.unknown()
|
|
2803
|
+
report: REPORT_DETAIL_OUTPUT,
|
|
2804
|
+
...FIX_CONTEXT_SHAPE,
|
|
2805
|
+
inventoryAction: z.unknown().describe("The inventory action (with its expected_outcome contract) the report is filed against, or null")
|
|
638
2806
|
})
|
|
639
2807
|
},
|
|
640
2808
|
async (args) => {
|
|
641
|
-
const { headers } = await projectScopeHeaders(args.
|
|
2809
|
+
const { headers } = await projectScopeHeaders(args.projectId);
|
|
642
2810
|
const report = await apiCall(`/v1/admin/reports/${args.reportId}`, {
|
|
643
2811
|
headers
|
|
644
2812
|
});
|
|
645
2813
|
return wrappedJsonResult(
|
|
646
2814
|
{
|
|
647
|
-
report,
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
// Hand this straight to the editing agent — no second LLM key needed.
|
|
651
|
-
fixPrompt: report.fix_packet ?? null,
|
|
652
|
-
reproductionSteps: report.reproduction_steps ?? [],
|
|
653
|
-
component: report.component,
|
|
654
|
-
rootCause: report.stage2_analysis?.rootCause,
|
|
655
|
-
bugOntologyTags: report.bug_ontology_tags
|
|
2815
|
+
report: projectReportDetail(report, false),
|
|
2816
|
+
...fixContextOf(report),
|
|
2817
|
+
inventoryAction: report.inventory_action ?? null
|
|
656
2818
|
},
|
|
657
2819
|
"fix context"
|
|
658
2820
|
);
|
|
@@ -665,12 +2827,12 @@ function createMushiServer(config) {
|
|
|
665
2827
|
description: descOf("get_fix_timeline"),
|
|
666
2828
|
annotations: annotationsFor("get_fix_timeline"),
|
|
667
2829
|
inputSchema: z.object({
|
|
668
|
-
fixId: z.string().describe("fix_attempt UUID"),
|
|
669
|
-
|
|
2830
|
+
fixId: z.string().describe("fix_attempt UUID (or the dispatch id dispatch_fix returned)"),
|
|
2831
|
+
projectId: z.string().optional().describe("Project UUID \u2014 required for org-scoped keys with multiple projects.")
|
|
670
2832
|
})
|
|
671
2833
|
},
|
|
672
2834
|
async (args) => {
|
|
673
|
-
const { headers } = await projectScopeHeaders(args.
|
|
2835
|
+
const { headers } = await projectScopeHeaders(args.projectId);
|
|
674
2836
|
return jsonText(await apiCall(`/v1/admin/fixes/${args.fixId}/timeline`, { headers }));
|
|
675
2837
|
}
|
|
676
2838
|
);
|
|
@@ -801,8 +2963,8 @@ function createMushiServer(config) {
|
|
|
801
2963
|
annotations: annotationsFor("list_gate_findings"),
|
|
802
2964
|
inputSchema: z.object({
|
|
803
2965
|
projectId: z.string().optional().describe("Project UUID \u2014 defaults to configured project"),
|
|
804
|
-
gate: z.
|
|
805
|
-
severity: z.
|
|
2966
|
+
gate: z.enum(GATE_IDS).optional().describe("Only runs of this gate (and their findings)."),
|
|
2967
|
+
severity: z.enum(GATE_FINDING_SEVERITIES).optional().describe("Only findings with exactly this severity.")
|
|
806
2968
|
}),
|
|
807
2969
|
outputSchema: z.object({
|
|
808
2970
|
runs: z.array(z.unknown()).describe("Recent gate_runs rows, newest first"),
|
|
@@ -902,11 +3064,10 @@ function createMushiServer(config) {
|
|
|
902
3064
|
mode: z.enum(["full", "ingest", "dispatch"]).optional().describe(
|
|
903
3065
|
"full (default) = ingest + dispatch; ingest = SDK pipeline only; dispatch = fix preflight only."
|
|
904
3066
|
),
|
|
905
|
-
|
|
906
|
-
projectId: z.string().optional().describe("Alias for project_id.")
|
|
3067
|
+
projectId: z.string().optional().describe("Project UUID for dispatch checks (defaults to configured project).")
|
|
907
3068
|
}),
|
|
908
3069
|
outputSchema: z.object({
|
|
909
|
-
mode: z.
|
|
3070
|
+
mode: z.enum(["full", "ingest", "dispatch"]),
|
|
910
3071
|
ready: z.boolean(),
|
|
911
3072
|
summary: z.string(),
|
|
912
3073
|
nextAction: z.string().optional(),
|
|
@@ -917,7 +3078,7 @@ function createMushiServer(config) {
|
|
|
917
3078
|
},
|
|
918
3079
|
async (args) => {
|
|
919
3080
|
const mode = args.mode ?? "full";
|
|
920
|
-
const resolvedId = args.
|
|
3081
|
+
const resolvedId = args.projectId ?? projectId;
|
|
921
3082
|
if (mode === "ingest") {
|
|
922
3083
|
const data = await apiCall("/v1/sync/ingest-setup");
|
|
923
3084
|
const failed = data.steps.filter((s) => s.required && !s.complete);
|
|
@@ -942,7 +3103,7 @@ function createMushiServer(config) {
|
|
|
942
3103
|
throw new MushiApiError(
|
|
943
3104
|
400,
|
|
944
3105
|
"MISSING_PROJECT",
|
|
945
|
-
"
|
|
3106
|
+
"projectId is required for dispatch mode"
|
|
946
3107
|
);
|
|
947
3108
|
}
|
|
948
3109
|
const data = await apiCall(`/v1/admin/projects/${resolvedId}/preflight`);
|
|
@@ -1121,7 +3282,7 @@ function createMushiServer(config) {
|
|
|
1121
3282
|
results: z.array(
|
|
1122
3283
|
z.object({
|
|
1123
3284
|
title: z.string(),
|
|
1124
|
-
|
|
3285
|
+
url: z.string(),
|
|
1125
3286
|
excerpt: z.string(),
|
|
1126
3287
|
score: z.number()
|
|
1127
3288
|
})
|
|
@@ -1131,15 +3292,58 @@ function createMushiServer(config) {
|
|
|
1131
3292
|
async (args) => {
|
|
1132
3293
|
const query = args.query ?? "";
|
|
1133
3294
|
const hits = searchMushiDocs(query, args.limit ?? 8);
|
|
1134
|
-
const results = hits.map(({ title,
|
|
1135
|
-
title,
|
|
1136
|
-
path,
|
|
1137
|
-
excerpt,
|
|
1138
|
-
score
|
|
1139
|
-
}));
|
|
3295
|
+
const results = hits.map(({ title, url, excerpt, score }) => ({ title, url, excerpt, score }));
|
|
1140
3296
|
return jsonResult({ query, results });
|
|
1141
3297
|
}
|
|
1142
3298
|
);
|
|
3299
|
+
server.registerTool(
|
|
3300
|
+
"get_mushi_doc",
|
|
3301
|
+
{
|
|
3302
|
+
title: titleOf("get_mushi_doc"),
|
|
3303
|
+
description: descOf("get_mushi_doc"),
|
|
3304
|
+
annotations: annotationsFor("get_mushi_doc"),
|
|
3305
|
+
inputSchema: z.object({
|
|
3306
|
+
page: z.string().min(1).describe('A url from search_mushi_docs, or a docs route such as "/quickstart/mcp".')
|
|
3307
|
+
}),
|
|
3308
|
+
outputSchema: z.object({
|
|
3309
|
+
title: z.string(),
|
|
3310
|
+
url: z.string(),
|
|
3311
|
+
markdown: z.string(),
|
|
3312
|
+
truncated: z.boolean()
|
|
3313
|
+
})
|
|
3314
|
+
},
|
|
3315
|
+
async (args) => {
|
|
3316
|
+
const entry = findMushiDoc(args.page);
|
|
3317
|
+
if (!entry) {
|
|
3318
|
+
throw new MushiApiError(
|
|
3319
|
+
404,
|
|
3320
|
+
"DOC_NOT_FOUND",
|
|
3321
|
+
`No docs page matches "${args.page}". Call search_mushi_docs and pass one of the urls it returns.`
|
|
3322
|
+
);
|
|
3323
|
+
}
|
|
3324
|
+
const res = await doFetch(mushiDocMarkdownUrl(entry), {
|
|
3325
|
+
headers: { Accept: "text/markdown, text/plain;q=0.9" },
|
|
3326
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
3327
|
+
});
|
|
3328
|
+
if (!res.ok) {
|
|
3329
|
+
throw new MushiApiError(
|
|
3330
|
+
res.status,
|
|
3331
|
+
"DOC_FETCH_FAILED",
|
|
3332
|
+
`The docs site answered ${res.status} for ${entry.url}. Open it in a browser instead.`
|
|
3333
|
+
);
|
|
3334
|
+
}
|
|
3335
|
+
const text = await res.text();
|
|
3336
|
+
const truncated = text.length > MUSHI_DOC_MAX_CHARS;
|
|
3337
|
+
return jsonResult({
|
|
3338
|
+
title: entry.title,
|
|
3339
|
+
url: entry.url,
|
|
3340
|
+
markdown: truncated ? `${text.slice(0, MUSHI_DOC_MAX_CHARS)}
|
|
3341
|
+
|
|
3342
|
+
\u2026 (truncated \u2014 read the rest at ${entry.url})` : text,
|
|
3343
|
+
truncated
|
|
3344
|
+
});
|
|
3345
|
+
}
|
|
3346
|
+
);
|
|
1143
3347
|
server.registerTool(
|
|
1144
3348
|
"list_projects",
|
|
1145
3349
|
{
|
|
@@ -1199,12 +3403,12 @@ function createMushiServer(config) {
|
|
|
1199
3403
|
description: descOf("get_project_context"),
|
|
1200
3404
|
annotations: annotationsFor("get_project_context"),
|
|
1201
3405
|
inputSchema: z.object({
|
|
1202
|
-
|
|
3406
|
+
projectId: z.string().optional().describe("Project UUID. Defaults to configured project.")
|
|
1203
3407
|
})
|
|
1204
3408
|
},
|
|
1205
3409
|
async (args) => {
|
|
1206
|
-
const pid = args.
|
|
1207
|
-
if (!pid) throw new MushiApiError(400, "MISSING_PROJECT_ID", "
|
|
3410
|
+
const pid = args.projectId ?? projectId;
|
|
3411
|
+
if (!pid) throw new MushiApiError(400, "MISSING_PROJECT_ID", "projectId is required");
|
|
1208
3412
|
const [preflightRes, activationRes] = await Promise.allSettled([
|
|
1209
3413
|
apiCall(`/v1/admin/projects/${pid}/preflight`),
|
|
1210
3414
|
apiCall(`/v1/admin/activation?project_id=${encodeURIComponent(pid)}`)
|
|
@@ -1223,7 +3427,7 @@ function createMushiServer(config) {
|
|
|
1223
3427
|
description: descOf("get_pipeline_logs"),
|
|
1224
3428
|
annotations: annotationsFor("get_pipeline_logs"),
|
|
1225
3429
|
inputSchema: z.object({
|
|
1226
|
-
|
|
3430
|
+
projectId: z.string().optional().describe("Project UUID. Defaults to configured project."),
|
|
1227
3431
|
service: z.enum(["fix-worker", "qa-story-runner", "pipeline", "all"]).optional().describe("Pipeline service to filter (default: all)."),
|
|
1228
3432
|
since: z.string().optional().describe("ISO-8601 timestamp \u2014 return only entries after this time."),
|
|
1229
3433
|
limit: z.number().optional().describe("Max entries to return (default 50, max 200)."),
|
|
@@ -1231,8 +3435,8 @@ function createMushiServer(config) {
|
|
|
1231
3435
|
})
|
|
1232
3436
|
},
|
|
1233
3437
|
async (args) => {
|
|
1234
|
-
const pid = args.
|
|
1235
|
-
if (!pid) throw new MushiApiError(400, "MISSING_PROJECT_ID", "
|
|
3438
|
+
const pid = args.projectId ?? projectId;
|
|
3439
|
+
if (!pid) throw new MushiApiError(400, "MISSING_PROJECT_ID", "projectId is required");
|
|
1236
3440
|
const qs = new URLSearchParams();
|
|
1237
3441
|
if (args.service && args.service !== "all") qs.set("service", args.service);
|
|
1238
3442
|
if (args.since) qs.set("since", args.since);
|
|
@@ -1249,41 +3453,17 @@ function createMushiServer(config) {
|
|
|
1249
3453
|
description: descOf("get_report_evidence"),
|
|
1250
3454
|
annotations: annotationsFor("get_report_evidence"),
|
|
1251
3455
|
inputSchema: z.object({
|
|
1252
|
-
|
|
3456
|
+
reportId: z.string().describe("Report UUID.")
|
|
1253
3457
|
})
|
|
1254
3458
|
},
|
|
1255
3459
|
async (args) => {
|
|
1256
3460
|
const [reportRes, timelineRes] = await Promise.allSettled([
|
|
1257
|
-
apiCall(`/v1/admin/reports/${args.
|
|
1258
|
-
apiCall(`/v1/admin/reports/${args.
|
|
3461
|
+
apiCall(`/v1/admin/reports/${args.reportId}`),
|
|
3462
|
+
apiCall(`/v1/admin/reports/${args.reportId}/timeline`)
|
|
1259
3463
|
]);
|
|
1260
3464
|
const report = reportRes.status === "fulfilled" ? reportRes.value : null;
|
|
1261
3465
|
const timeline = timelineRes.status === "fulfilled" ? timelineRes.value : null;
|
|
1262
|
-
const
|
|
1263
|
-
const evidence = r ? {
|
|
1264
|
-
report_id: args.report_id,
|
|
1265
|
-
description: r.description,
|
|
1266
|
-
summary: r.summary,
|
|
1267
|
-
screenshot_url: r.screenshot_url ?? null,
|
|
1268
|
-
environment: r.environment ?? null,
|
|
1269
|
-
// LOGS
|
|
1270
|
-
console_logs: r.console_logs ?? null,
|
|
1271
|
-
breadcrumbs: r.breadcrumbs ?? null,
|
|
1272
|
-
repro_timeline: r.repro_timeline ?? null,
|
|
1273
|
-
// TRACES
|
|
1274
|
-
network_requests: r.network_logs ?? null,
|
|
1275
|
-
backend_spans: r.backend_spans ?? null,
|
|
1276
|
-
sentry_replay_id: r.sentry_replay_id ?? null,
|
|
1277
|
-
sentry_trace_id: r.sentry_trace_id ?? null,
|
|
1278
|
-
sentry_event_id: r.sentry_event_id ?? null,
|
|
1279
|
-
// METRICS
|
|
1280
|
-
performance_metrics: r.performance_metrics ?? null,
|
|
1281
|
-
anomalies: r.anomalies ?? null,
|
|
1282
|
-
// context
|
|
1283
|
-
session_id: r.session_id ?? null,
|
|
1284
|
-
created_at: r.created_at,
|
|
1285
|
-
tags: r.tags ?? null
|
|
1286
|
-
} : { error: String(reportRes.reason) };
|
|
3466
|
+
const evidence = report ? reportEvidenceOf(report, args.reportId) : { error: String(reportRes.reason) };
|
|
1287
3467
|
return jsonText({
|
|
1288
3468
|
evidence,
|
|
1289
3469
|
reporter_thread: timeline ?? {
|
|
@@ -1299,94 +3479,82 @@ function createMushiServer(config) {
|
|
|
1299
3479
|
description: descOf("triage_issue"),
|
|
1300
3480
|
annotations: annotationsFor("triage_issue"),
|
|
1301
3481
|
inputSchema: z.object({
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
3482
|
+
reportId: z.string().uuid().describe("Report UUID to triage."),
|
|
3483
|
+
projectId: z.string().optional().describe("Project UUID. Defaults to configured project."),
|
|
3484
|
+
includeLogs: z.boolean().optional().describe("Include recent pipeline logs in triage packet (default: true).")
|
|
1305
3485
|
}),
|
|
1306
3486
|
outputSchema: z.object({
|
|
1307
|
-
report_id: z.string(),
|
|
1308
|
-
severity: z.
|
|
1309
|
-
category: z.
|
|
1310
|
-
status: z.
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
3487
|
+
report_id: z.string().describe("The triaged report UUID"),
|
|
3488
|
+
severity: z.string().nullable().describe("critical | high | medium | low, or unknown when the report could not be read"),
|
|
3489
|
+
category: z.string().nullable().describe("bug | slow | visual | confusing | other, or unknown"),
|
|
3490
|
+
status: z.string().nullable().describe("Workflow status, or unknown"),
|
|
3491
|
+
partial_errors: z.array(z.string()).describe('Sources that failed, as "source: message"'),
|
|
3492
|
+
notes: z.array(z.string()).describe("Sources that do not apply to this report, and why"),
|
|
3493
|
+
report: z.union([REPORT_DETAIL_OUTPUT, z.object({ error: z.string() })]).describe("The report (documented fields, no reporter identifiers), or the error reading it"),
|
|
3494
|
+
reporter_thread: z.looseObject({ report_id: z.string().optional(), timeline: z.array(z.unknown()).optional() }).nullable().describe("The unified report timeline (comments, fixes, QA), or null"),
|
|
3495
|
+
similar_bugs: z.array(
|
|
3496
|
+
z.looseObject({
|
|
3497
|
+
reportId: z.string(),
|
|
3498
|
+
similarity: z.number(),
|
|
3499
|
+
description: nullableString(),
|
|
3500
|
+
category: nullableString(),
|
|
3501
|
+
createdAt: nullableString(),
|
|
3502
|
+
reportGroupId: nullableString()
|
|
3503
|
+
})
|
|
3504
|
+
).nullable().describe("Up to five nearest reports by summary, excluding this one; null when not searched"),
|
|
3505
|
+
fix_context: z.object(FIX_CONTEXT_SHAPE).nullable().describe("Fix-context slice, as get_fix_context returns it"),
|
|
3506
|
+
blast_radius: z.looseObject({ affected: z.array(z.unknown()).optional() }).nullable().describe("Downstream nodes of the inventory action the report is filed against, or null"),
|
|
3507
|
+
recent_logs: z.looseObject({ entries: z.array(z.unknown()).optional() }).nullable().describe("Recent warn+ pipeline log entries for the project, or null"),
|
|
3508
|
+
recommended_actions: z.array(
|
|
3509
|
+
z.object({
|
|
3510
|
+
action: z.string(),
|
|
3511
|
+
reason: z.string(),
|
|
3512
|
+
tool: z.string().optional().describe("Tool to call next"),
|
|
3513
|
+
args: z.record(z.string(), z.unknown()).optional().describe("Arguments for that tool")
|
|
3514
|
+
})
|
|
3515
|
+
).describe("What to do next, most important first"),
|
|
3516
|
+
triage_summary: z.string().describe("One-line summary")
|
|
1319
3517
|
})
|
|
1320
3518
|
},
|
|
1321
3519
|
async (args) => {
|
|
1322
|
-
const { projectId: pid, headers } = await projectScopeHeaders(args.
|
|
1323
|
-
const includeLogs = args.
|
|
1324
|
-
const
|
|
1325
|
-
|
|
1326
|
-
apiCall(
|
|
3520
|
+
const { projectId: pid, headers } = await projectScopeHeaders(args.projectId);
|
|
3521
|
+
const includeLogs = args.includeLogs !== false;
|
|
3522
|
+
const reportPath = `/v1/admin/reports/${encodeURIComponent(args.reportId)}`;
|
|
3523
|
+
const [reportRes, evidenceRes, logsRes] = await Promise.allSettled([
|
|
3524
|
+
apiCall(reportPath, { headers }),
|
|
3525
|
+
apiCall(`${reportPath}/timeline`, { headers }),
|
|
3526
|
+
includeLogs ? apiCall(`/v1/admin/mcp/logs/${encodeURIComponent(pid)}?limit=20&level=warn`, {
|
|
1327
3527
|
headers
|
|
1328
|
-
})
|
|
1329
|
-
apiCall(`/v1/admin/reports/similarity`, {
|
|
1330
|
-
method: "POST",
|
|
1331
|
-
headers,
|
|
1332
|
-
body: JSON.stringify({ report_id: args.report_id })
|
|
1333
|
-
}).catch(() => null),
|
|
1334
|
-
apiCall(`/v1/admin/reports/${args.report_id}/fix-context`, { headers }).catch(
|
|
1335
|
-
() => null
|
|
1336
|
-
),
|
|
1337
|
-
apiCall(`/v1/admin/reports/${args.report_id}/blast-radius`, { headers }).catch(
|
|
1338
|
-
() => null
|
|
1339
|
-
),
|
|
1340
|
-
includeLogs ? apiCall(`/v1/admin/mcp/logs/${pid}?limit=20&level=warn`, { headers }).catch(
|
|
1341
|
-
() => null
|
|
1342
|
-
) : Promise.resolve(null)
|
|
3528
|
+
}) : Promise.resolve(null)
|
|
1343
3529
|
]);
|
|
1344
3530
|
const report = reportRes.status === "fulfilled" ? reportRes.value : null;
|
|
1345
|
-
const
|
|
1346
|
-
const
|
|
1347
|
-
const
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
const blockReason = typeof processingError === "string" && processingError || String(lastAttempt?.error ?? "previous fix attempt was skipped or failed");
|
|
1356
|
-
actions.push({
|
|
1357
|
-
action: "unblock_autofix",
|
|
1358
|
-
reason: `Auto-fix is blocked: ${blockReason}. Resolve the blocker (settings/integration/budget), then re-dispatch.`,
|
|
1359
|
-
tool: "diagnose_setup",
|
|
1360
|
-
args: { mode: "dispatch" }
|
|
1361
|
-
});
|
|
1362
|
-
actions.push({
|
|
1363
|
-
action: "redispatch_after_unblock",
|
|
1364
|
-
reason: "Once the blocker above is resolved, dispatch a fresh fix attempt.",
|
|
1365
|
-
tool: "dispatch_fix",
|
|
1366
|
-
args: { reportId: args.report_id }
|
|
1367
|
-
});
|
|
1368
|
-
} else if (status === "new" || status === "classified") {
|
|
1369
|
-
actions.push({
|
|
1370
|
-
action: "dispatch_fix",
|
|
1371
|
-
reason: "Report is classified and no fix has been attempted",
|
|
1372
|
-
tool: "dispatch_fix",
|
|
1373
|
-
args: { reportId: args.report_id }
|
|
1374
|
-
});
|
|
1375
|
-
} else if (status === "fixing") {
|
|
1376
|
-
actions.push({
|
|
1377
|
-
action: "check_fix_progress",
|
|
1378
|
-
reason: "Fix is in progress \u2014 check the fix timeline for latest status",
|
|
1379
|
-
tool: "get_fix_timeline",
|
|
1380
|
-
args: { reportId: args.report_id }
|
|
1381
|
-
});
|
|
1382
|
-
} else if (status === "fixed") {
|
|
1383
|
-
actions.push({
|
|
1384
|
-
action: "verify_fix",
|
|
1385
|
-
reason: "Fix was applied \u2014 verify it resolved the issue",
|
|
1386
|
-
tool: "get_fix_context",
|
|
1387
|
-
args: { reportId: args.report_id }
|
|
1388
|
-
});
|
|
3531
|
+
const notes = [];
|
|
3532
|
+
const similarityQuery = report ? similarityQueryOf(report) : null;
|
|
3533
|
+
const actionNodeId = report ? inventoryActionNodeIdOf(report) : null;
|
|
3534
|
+
if (report && !similarityQuery) {
|
|
3535
|
+
notes.push("similar_bugs: the report has no summary or description to match on.");
|
|
3536
|
+
}
|
|
3537
|
+
if (report && !actionNodeId) {
|
|
3538
|
+
notes.push(
|
|
3539
|
+
"blast_radius: the report is not filed against an inventory action, so there is no graph node to traverse from. Use get_knowledge_graph with the component as the seed instead."
|
|
3540
|
+
);
|
|
1389
3541
|
}
|
|
3542
|
+
const [similarRes, blastRes] = await Promise.allSettled([
|
|
3543
|
+
similarityQuery ? apiCall("/v1/admin/reports/similarity", {
|
|
3544
|
+
method: "POST",
|
|
3545
|
+
headers,
|
|
3546
|
+
body: JSON.stringify({ query: similarityQuery, k: 6, threshold: 0.3, projectId: pid })
|
|
3547
|
+
}) : Promise.resolve(null),
|
|
3548
|
+
actionNodeId ? apiCall(`/v1/admin/graph/blast-radius/${encodeURIComponent(actionNodeId)}`, {
|
|
3549
|
+
headers
|
|
3550
|
+
}) : Promise.resolve(null)
|
|
3551
|
+
]);
|
|
3552
|
+
const similarBugs = similarRes.status === "fulfilled" && similarRes.value ? (similarRes.value.results ?? []).filter((r) => r.reportId !== args.reportId).slice(0, 5) : null;
|
|
3553
|
+
const text = (v) => typeof v === "string" ? v : v == null ? null : String(v);
|
|
3554
|
+
const severity = report ? text(report.severity) : "unknown";
|
|
3555
|
+
const category = report ? text(report.category) : "unknown";
|
|
3556
|
+
const status = report ? text(report.status) : "unknown";
|
|
3557
|
+
const actions = triageRecommendedActions(report, args.reportId);
|
|
1390
3558
|
const partial_errors = [];
|
|
1391
3559
|
if (reportRes.status === "rejected") {
|
|
1392
3560
|
partial_errors.push(`report: ${String(reportRes.reason)}`);
|
|
@@ -1397,9 +3565,6 @@ function createMushiServer(config) {
|
|
|
1397
3565
|
if (similarRes.status === "rejected") {
|
|
1398
3566
|
partial_errors.push(`similarity: ${String(similarRes.reason)}`);
|
|
1399
3567
|
}
|
|
1400
|
-
if (fixCtxRes.status === "rejected") {
|
|
1401
|
-
partial_errors.push(`fix_context: ${String(fixCtxRes.reason)}`);
|
|
1402
|
-
}
|
|
1403
3568
|
if (blastRes.status === "rejected") {
|
|
1404
3569
|
partial_errors.push(`blast_radius: ${String(blastRes.reason)}`);
|
|
1405
3570
|
}
|
|
@@ -1407,29 +3572,33 @@ function createMushiServer(config) {
|
|
|
1407
3572
|
partial_errors.push(`recent_logs: ${String(logsRes.reason)}`);
|
|
1408
3573
|
}
|
|
1409
3574
|
const result = {
|
|
1410
|
-
report_id: args.
|
|
3575
|
+
report_id: args.reportId,
|
|
1411
3576
|
severity,
|
|
1412
3577
|
category,
|
|
1413
3578
|
status,
|
|
1414
3579
|
partial_errors,
|
|
1415
|
-
|
|
3580
|
+
notes,
|
|
3581
|
+
report: report ? projectReportDetail(report, false) : { error: String(reportRes.reason) },
|
|
1416
3582
|
reporter_thread: evidenceRes.status === "fulfilled" ? evidenceRes.value : null,
|
|
1417
|
-
similar_bugs:
|
|
1418
|
-
fix_context:
|
|
3583
|
+
similar_bugs: similarBugs,
|
|
3584
|
+
fix_context: report ? fixContextOf(report) : null,
|
|
1419
3585
|
blast_radius: blastRes.status === "fulfilled" ? blastRes.value : null,
|
|
1420
3586
|
recent_logs: logsRes.status === "fulfilled" ? logsRes.value : null,
|
|
1421
3587
|
recommended_actions: actions,
|
|
1422
|
-
triage_summary:
|
|
3588
|
+
triage_summary: triageSummaryOf(report, actions)
|
|
1423
3589
|
};
|
|
1424
3590
|
const res = jsonResult(result);
|
|
1425
3591
|
return {
|
|
1426
3592
|
...res,
|
|
1427
|
-
|
|
3593
|
+
content: [
|
|
3594
|
+
...res.content,
|
|
1428
3595
|
{
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
3596
|
+
type: "resource_link",
|
|
3597
|
+
uri: "project://dashboard",
|
|
3598
|
+
name: "project_dashboard",
|
|
3599
|
+
title: "Project dashboard",
|
|
3600
|
+
mimeType: "application/json"
|
|
3601
|
+
}
|
|
1433
3602
|
]
|
|
1434
3603
|
};
|
|
1435
3604
|
}
|
|
@@ -1494,7 +3663,7 @@ function createMushiServer(config) {
|
|
|
1494
3663
|
description: descOf("triage_next_steps"),
|
|
1495
3664
|
annotations: annotationsFor("triage_next_steps"),
|
|
1496
3665
|
inputSchema: z.object({
|
|
1497
|
-
|
|
3666
|
+
projectId: z.string().uuid().optional().describe("Project UUID (defaults to the configured project).")
|
|
1498
3667
|
}),
|
|
1499
3668
|
outputSchema: z.object({
|
|
1500
3669
|
steps: z.array(
|
|
@@ -1510,7 +3679,7 @@ function createMushiServer(config) {
|
|
|
1510
3679
|
})
|
|
1511
3680
|
},
|
|
1512
3681
|
async (args) => {
|
|
1513
|
-
const pid = await resolveProjectId(args.
|
|
3682
|
+
const pid = await resolveProjectId(args.projectId);
|
|
1514
3683
|
const extraHeaders = pid !== projectId ? { "X-Mushi-Project-Id": pid } : {};
|
|
1515
3684
|
const data = await apiCall(
|
|
1516
3685
|
"/v1/admin/reports?limit=100",
|
|
@@ -1539,7 +3708,7 @@ function createMushiServer(config) {
|
|
|
1539
3708
|
action: `Shepherd in-flight fix: ${label(r)}`,
|
|
1540
3709
|
reason: "A fix branch/PR is open \u2014 check CI and merge when green.",
|
|
1541
3710
|
tool: "get_report_timeline",
|
|
1542
|
-
args: {
|
|
3711
|
+
args: { reportId: r.id }
|
|
1543
3712
|
});
|
|
1544
3713
|
}
|
|
1545
3714
|
const openUserReports = reports.filter(
|
|
@@ -1554,7 +3723,7 @@ function createMushiServer(config) {
|
|
|
1554
3723
|
action: `Triage [${r.severity}] ${label(r)}`,
|
|
1555
3724
|
reason: "User-felt report awaiting triage \u2014 review the packet, then dispatch or dismiss.",
|
|
1556
3725
|
tool: "triage_issue",
|
|
1557
|
-
args: {
|
|
3726
|
+
args: { reportId: r.id }
|
|
1558
3727
|
});
|
|
1559
3728
|
}
|
|
1560
3729
|
const chores = reports.filter((r) => r.status === "classified" && isCron(r) && !isBlocked(r));
|
|
@@ -1579,7 +3748,8 @@ function createMushiServer(config) {
|
|
|
1579
3748
|
annotations: annotationsFor("dispatch_fix"),
|
|
1580
3749
|
inputSchema: z.object({
|
|
1581
3750
|
reportId: z.string().uuid().describe("Report UUID to fix"),
|
|
1582
|
-
agent: z.enum(
|
|
3751
|
+
agent: z.enum(DISPATCH_AGENTS).optional().describe("Which agent runs the fix. Omit for the project default (auto); cursor_cloud and github_cloud_agent dispatch a cloud agent."),
|
|
3752
|
+
projectId: z.string().optional().describe("Project UUID \u2014 defaults to the configured project."),
|
|
1583
3753
|
idempotencyKey: z.string().uuid().optional().describe(
|
|
1584
3754
|
"Optional RFC 4122 UUID. Resend the same key to safely retry without dispatching a duplicate fix job (Idempotency-Key IETF draft)."
|
|
1585
3755
|
),
|
|
@@ -1607,6 +3777,7 @@ function createMushiServer(config) {
|
|
|
1607
3777
|
} catch {
|
|
1608
3778
|
}
|
|
1609
3779
|
}
|
|
3780
|
+
const pid = args.projectId ? await resolveProjectId(args.projectId) : projectId;
|
|
1610
3781
|
const dispatch = await apiCall("/v1/admin/fixes/dispatch", {
|
|
1611
3782
|
method: "POST",
|
|
1612
3783
|
headers: args.idempotencyKey ? { "Idempotency-Key": args.idempotencyKey } : void 0,
|
|
@@ -1614,7 +3785,7 @@ function createMushiServer(config) {
|
|
|
1614
3785
|
reportId: args.reportId,
|
|
1615
3786
|
agent: args.agent,
|
|
1616
3787
|
inventoryActionNodeId: args.inventoryActionNodeId,
|
|
1617
|
-
...
|
|
3788
|
+
...pid ? { projectId: pid } : {}
|
|
1618
3789
|
})
|
|
1619
3790
|
});
|
|
1620
3791
|
if (!dispatch?.dispatchId) {
|
|
@@ -1692,6 +3863,10 @@ function createMushiServer(config) {
|
|
|
1692
3863
|
outputSchema: z.object({
|
|
1693
3864
|
merged: z.boolean().optional().describe("True when GitHub accepted the merge in this call"),
|
|
1694
3865
|
alreadyMerged: z.boolean().optional().describe("True when the PR was already merged (idempotent no-op)"),
|
|
3866
|
+
// The merge route also returns these; declaring them keeps a strict
|
|
3867
|
+
// client from rejecting a merge that already happened.
|
|
3868
|
+
justMerged: z.boolean().optional().describe("True when this call performed the merge"),
|
|
3869
|
+
sha: z.string().nullable().optional().describe("Merge commit SHA, when GitHub reported one"),
|
|
1695
3870
|
reportId: z.string().describe("Report UUID linked to this fix attempt"),
|
|
1696
3871
|
reportStatus: z.string().describe("Report workflow status after merge bookkeeping")
|
|
1697
3872
|
})
|
|
@@ -1781,31 +3956,6 @@ function createMushiServer(config) {
|
|
|
1781
3956
|
return jsonText(data);
|
|
1782
3957
|
}
|
|
1783
3958
|
);
|
|
1784
|
-
server.registerTool(
|
|
1785
|
-
"setup_repo_for_mushi",
|
|
1786
|
-
{
|
|
1787
|
-
title: titleOf("setup_repo_for_mushi"),
|
|
1788
|
-
description: descOf("setup_repo_for_mushi"),
|
|
1789
|
-
annotations: annotationsFor("setup_repo_for_mushi"),
|
|
1790
|
-
inputSchema: z.object({
|
|
1791
|
-
projectId: z.string().optional().describe("Project UUID \u2014 defaults to configured project")
|
|
1792
|
-
})
|
|
1793
|
-
},
|
|
1794
|
-
async (args) => {
|
|
1795
|
-
const pid = args.projectId ?? projectId;
|
|
1796
|
-
if (!pid)
|
|
1797
|
-
throw new MushiApiError(
|
|
1798
|
-
400,
|
|
1799
|
-
"MISSING_PROJECT",
|
|
1800
|
-
"projectId is required for setup_repo_for_mushi"
|
|
1801
|
-
);
|
|
1802
|
-
const data = await apiCall(`/v1/admin/projects/${pid}/repo/bootstrap`, {
|
|
1803
|
-
method: "POST",
|
|
1804
|
-
body: JSON.stringify({})
|
|
1805
|
-
});
|
|
1806
|
-
return jsonText(data);
|
|
1807
|
-
}
|
|
1808
|
-
);
|
|
1809
3959
|
server.registerTool(
|
|
1810
3960
|
"query_lessons",
|
|
1811
3961
|
{
|
|
@@ -1813,20 +3963,20 @@ function createMushiServer(config) {
|
|
|
1813
3963
|
description: descOf("query_lessons"),
|
|
1814
3964
|
annotations: annotationsFor("query_lessons"),
|
|
1815
3965
|
inputSchema: z.object({
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
3966
|
+
diffText: z.string().describe("The PR diff, code snippet, or description of the change being made."),
|
|
3967
|
+
maxTokens: z.number().optional().describe("Maximum tokens for returned lessons context (default 3000, max 8000)."),
|
|
3968
|
+
topK: z.number().optional().describe("Max number of lessons to return (default 15, max 50)."),
|
|
3969
|
+
projectId: z.string().optional().describe("Project UUID. Defaults to configured project.")
|
|
1820
3970
|
})
|
|
1821
3971
|
},
|
|
1822
3972
|
async (args) => {
|
|
1823
|
-
const pid = args.
|
|
3973
|
+
const pid = args.projectId ?? projectId;
|
|
1824
3974
|
const data = await apiCall("/v1/admin/lessons/query", {
|
|
1825
3975
|
method: "POST",
|
|
1826
3976
|
body: JSON.stringify({
|
|
1827
|
-
diff_text: args.
|
|
1828
|
-
...args.
|
|
1829
|
-
...args.
|
|
3977
|
+
diff_text: args.diffText,
|
|
3978
|
+
...args.maxTokens !== void 0 ? { max_tokens: args.maxTokens } : {},
|
|
3979
|
+
...args.topK !== void 0 ? { top_k: args.topK } : {},
|
|
1830
3980
|
...pid ? { project_id: pid } : {}
|
|
1831
3981
|
})
|
|
1832
3982
|
});
|
|
@@ -1842,14 +3992,14 @@ function createMushiServer(config) {
|
|
|
1842
3992
|
inputSchema: z.object({
|
|
1843
3993
|
severity: z.enum(["info", "warn", "critical"]).optional().describe("Filter to one severity level"),
|
|
1844
3994
|
limit: z.number().optional().describe("Max lessons to return (default 50, max 200)"),
|
|
1845
|
-
|
|
3995
|
+
projectId: z.string().optional().describe("Project UUID \u2014 defaults to configured project")
|
|
1846
3996
|
}),
|
|
1847
3997
|
outputSchema: z.object({
|
|
1848
3998
|
lessons: z.array(z.unknown()).describe("Promoted lesson rows ordered by frequency")
|
|
1849
3999
|
})
|
|
1850
4000
|
},
|
|
1851
4001
|
async (args) => {
|
|
1852
|
-
const pid = args.
|
|
4002
|
+
const pid = args.projectId ?? projectId;
|
|
1853
4003
|
const params = new URLSearchParams();
|
|
1854
4004
|
if (args.severity) params.set("severity", args.severity);
|
|
1855
4005
|
params.set("limit", String(Math.min(args.limit ?? 50, 200)));
|
|
@@ -1935,12 +4085,12 @@ function createMushiServer(config) {
|
|
|
1935
4085
|
description: descOf("award_bonus_points"),
|
|
1936
4086
|
annotations: annotationsFor("award_bonus_points"),
|
|
1937
4087
|
inputSchema: z.object({
|
|
1938
|
-
|
|
4088
|
+
externalUserId: z.string().describe("The host-app user id as passed to Mushi.identify()"),
|
|
1939
4089
|
points: z.number().int().min(1).max(5e4).describe("Bonus points to award (max 50,000 per call)"),
|
|
1940
4090
|
reason: z.string().max(200).describe("Human-readable reason, logged to end_user_activity")
|
|
1941
4091
|
})
|
|
1942
4092
|
},
|
|
1943
|
-
async ({
|
|
4093
|
+
async ({ externalUserId, points, reason }) => ({
|
|
1944
4094
|
content: [
|
|
1945
4095
|
{
|
|
1946
4096
|
type: "text",
|
|
@@ -1948,7 +4098,7 @@ function createMushiServer(config) {
|
|
|
1948
4098
|
await apiCall("/v1/admin/rewards/bonus-points", {
|
|
1949
4099
|
method: "POST",
|
|
1950
4100
|
headers: { "Content-Type": "application/json" },
|
|
1951
|
-
body: JSON.stringify({ external_user_id, points, reason })
|
|
4101
|
+
body: JSON.stringify({ external_user_id: externalUserId, points, reason })
|
|
1952
4102
|
}),
|
|
1953
4103
|
null,
|
|
1954
4104
|
2
|
|
@@ -1964,12 +4114,12 @@ function createMushiServer(config) {
|
|
|
1964
4114
|
description: descOf("set_tier"),
|
|
1965
4115
|
annotations: annotationsFor("set_tier"),
|
|
1966
4116
|
inputSchema: z.object({
|
|
1967
|
-
|
|
1968
|
-
|
|
4117
|
+
externalUserId: z.string().describe("The host-app user id as passed to Mushi.identify()"),
|
|
4118
|
+
tierSlug: z.string().describe('Tier slug to assign, e.g. "champion", "contributor", "explorer"'),
|
|
1969
4119
|
reason: z.string().max(200).optional().describe("Optional reason for manual override")
|
|
1970
4120
|
})
|
|
1971
4121
|
},
|
|
1972
|
-
async ({
|
|
4122
|
+
async ({ externalUserId, tierSlug, reason }) => ({
|
|
1973
4123
|
content: [
|
|
1974
4124
|
{
|
|
1975
4125
|
type: "text",
|
|
@@ -1977,7 +4127,7 @@ function createMushiServer(config) {
|
|
|
1977
4127
|
await apiCall("/v1/admin/rewards/set-tier", {
|
|
1978
4128
|
method: "POST",
|
|
1979
4129
|
headers: { "Content-Type": "application/json" },
|
|
1980
|
-
body: JSON.stringify({ external_user_id, tier_slug, reason })
|
|
4130
|
+
body: JSON.stringify({ external_user_id: externalUserId, tier_slug: tierSlug, reason })
|
|
1981
4131
|
}),
|
|
1982
4132
|
null,
|
|
1983
4133
|
2
|
|
@@ -2047,13 +4197,13 @@ function createMushiServer(config) {
|
|
|
2047
4197
|
description: "Current inventory snapshot for the active project \u2014 all Action nodes with their spec contract (expected_outcome), build-gate status, linked reports, and fix attempts. Subscribe to this resource to receive notifications/resources/updated when the inventory is re-crawled (e.g. after a PR merge or manual trigger). Orchestrators can use this to enumerate work items and pick the next Action to fix."
|
|
2048
4198
|
},
|
|
2049
4199
|
async () => {
|
|
2050
|
-
const
|
|
4200
|
+
const pid = await resolveProjectId(void 0);
|
|
2051
4201
|
return {
|
|
2052
4202
|
contents: [
|
|
2053
4203
|
{
|
|
2054
4204
|
uri: "inventory://current",
|
|
2055
4205
|
mimeType: "application/json",
|
|
2056
|
-
text: JSON.stringify(await apiCall(
|
|
4206
|
+
text: JSON.stringify(await apiCall(`/v1/admin/inventory/${encodeURIComponent(pid)}`), null, 2)
|
|
2057
4207
|
}
|
|
2058
4208
|
]
|
|
2059
4209
|
};
|
|
@@ -2066,15 +4216,94 @@ function createMushiServer(config) {
|
|
|
2066
4216
|
description: descOf("activation_status"),
|
|
2067
4217
|
annotations: annotationsFor("activation_status"),
|
|
2068
4218
|
inputSchema: z.object({
|
|
2069
|
-
|
|
4219
|
+
projectId: z.string().optional().describe("Project UUID (defaults to the configured project).")
|
|
2070
4220
|
})
|
|
2071
4221
|
},
|
|
2072
4222
|
async (args) => {
|
|
2073
|
-
const pid = args.
|
|
4223
|
+
const pid = args.projectId ?? projectId;
|
|
2074
4224
|
const qs = pid ? `?project_id=${encodeURIComponent(pid)}` : "";
|
|
2075
4225
|
return jsonResult(await apiCall(`/v1/admin/activation${qs}`));
|
|
2076
4226
|
}
|
|
2077
4227
|
);
|
|
4228
|
+
function eventsQuery(params) {
|
|
4229
|
+
const qs = new URLSearchParams();
|
|
4230
|
+
for (const [k, v] of Object.entries(params)) {
|
|
4231
|
+
if (v !== void 0 && v !== "") qs.set(k, String(v));
|
|
4232
|
+
}
|
|
4233
|
+
return qs.toString();
|
|
4234
|
+
}
|
|
4235
|
+
function trailingRange(windowDays) {
|
|
4236
|
+
const to = /* @__PURE__ */ new Date();
|
|
4237
|
+
const from = new Date(to.getTime() - windowDays * 24 * 60 * 60 * 1e3);
|
|
4238
|
+
return { from: from.toISOString(), to: to.toISOString() };
|
|
4239
|
+
}
|
|
4240
|
+
server.registerTool(
|
|
4241
|
+
"query_funnel",
|
|
4242
|
+
{
|
|
4243
|
+
title: titleOf("query_funnel"),
|
|
4244
|
+
description: descOf("query_funnel"),
|
|
4245
|
+
annotations: annotationsFor("query_funnel"),
|
|
4246
|
+
inputSchema: z.object({
|
|
4247
|
+
steps: z.array(z.string().min(1)).min(2).max(8).describe('Ordered event names, 2\u20138 (e.g. ["landing_view", "signup_completed", "first_report_received"]).'),
|
|
4248
|
+
windowDays: z.number().int().min(1).max(365).optional().describe("Trailing window in days (default 30)."),
|
|
4249
|
+
stepWindow: z.enum(["1h", "1d", "7d", "30d"]).optional().describe("Max time allowed between consecutive steps (default 7d)."),
|
|
4250
|
+
breakdown: z.string().optional().describe('Event property to split every step by (e.g. "utm_source", "$surface").'),
|
|
4251
|
+
projectId: z.string().optional().describe("Project UUID (defaults to the configured project).")
|
|
4252
|
+
})
|
|
4253
|
+
},
|
|
4254
|
+
async (args) => {
|
|
4255
|
+
const qs = eventsQuery({
|
|
4256
|
+
steps: args.steps.join(","),
|
|
4257
|
+
window: args.stepWindow ?? "7d",
|
|
4258
|
+
...trailingRange(args.windowDays ?? 30),
|
|
4259
|
+
breakdown: args.breakdown,
|
|
4260
|
+
project_id: args.projectId ?? projectId
|
|
4261
|
+
});
|
|
4262
|
+
return jsonResult(await apiCall(`/v1/admin/events/funnel?${qs}`));
|
|
4263
|
+
}
|
|
4264
|
+
);
|
|
4265
|
+
server.registerTool(
|
|
4266
|
+
"get_product_events_summary",
|
|
4267
|
+
{
|
|
4268
|
+
title: titleOf("get_product_events_summary"),
|
|
4269
|
+
description: descOf("get_product_events_summary"),
|
|
4270
|
+
annotations: annotationsFor("get_product_events_summary"),
|
|
4271
|
+
inputSchema: z.object({
|
|
4272
|
+
windowDays: z.number().int().min(1).max(365).optional().describe("Trailing window in days (default 30)."),
|
|
4273
|
+
projectId: z.string().optional().describe("Project UUID (defaults to the configured project).")
|
|
4274
|
+
})
|
|
4275
|
+
},
|
|
4276
|
+
async (args) => {
|
|
4277
|
+
const qs = eventsQuery({
|
|
4278
|
+
window: args.windowDays ?? 30,
|
|
4279
|
+
project_id: args.projectId ?? projectId
|
|
4280
|
+
});
|
|
4281
|
+
return jsonResult(await apiCall(`/v1/admin/events/summary?${qs}`));
|
|
4282
|
+
}
|
|
4283
|
+
);
|
|
4284
|
+
server.registerTool(
|
|
4285
|
+
"get_user_paths",
|
|
4286
|
+
{
|
|
4287
|
+
title: titleOf("get_user_paths"),
|
|
4288
|
+
description: descOf("get_user_paths"),
|
|
4289
|
+
annotations: annotationsFor("get_user_paths"),
|
|
4290
|
+
inputSchema: z.object({
|
|
4291
|
+
fromEvent: z.string().min(1).describe('Event name to start from (e.g. "key_minted").'),
|
|
4292
|
+
windowDays: z.number().int().min(1).max(365).optional().describe("Trailing window in days (default 30)."),
|
|
4293
|
+
limit: z.number().int().min(1).max(50).optional().describe("Max distinct paths to return (default 20, max 50)."),
|
|
4294
|
+
projectId: z.string().optional().describe("Project UUID (defaults to the configured project).")
|
|
4295
|
+
})
|
|
4296
|
+
},
|
|
4297
|
+
async (args) => {
|
|
4298
|
+
const qs = eventsQuery({
|
|
4299
|
+
from_event: args.fromEvent,
|
|
4300
|
+
...trailingRange(args.windowDays ?? 30),
|
|
4301
|
+
limit: args.limit ?? 20,
|
|
4302
|
+
project_id: args.projectId ?? projectId
|
|
4303
|
+
});
|
|
4304
|
+
return jsonResult(await apiCall(`/v1/admin/events/paths?${qs}`));
|
|
4305
|
+
}
|
|
4306
|
+
);
|
|
2078
4307
|
server.registerResource(
|
|
2079
4308
|
"activation_status",
|
|
2080
4309
|
"mushi://activation",
|
|
@@ -2269,7 +4498,11 @@ Be specific. No generic advice.`
|
|
|
2269
4498
|
qaStoryId: z.string().describe("UUID of the inserted qa_stories row"),
|
|
2270
4499
|
prUrl: z.string().nullable().describe("GitHub PR URL when openPr=true and GitHub is connected"),
|
|
2271
4500
|
approvalStatus: z.string().describe("pending_review | approved \u2014 follows automationMode and project defaults"),
|
|
2272
|
-
needsHumanReview: z.boolean().describe("True when the story requires operator approval before scheduled runs")
|
|
4501
|
+
needsHumanReview: z.boolean().describe("True when the story requires operator approval before scheduled runs"),
|
|
4502
|
+
// test-gen-from-story also returns these; undeclared, a strict client
|
|
4503
|
+
// rejected a generation that had already opened its PR.
|
|
4504
|
+
path: z.string().optional().describe("Repo path of the generated Playwright spec"),
|
|
4505
|
+
firecrawlActionsYaml: z.string().nullable().optional().describe("Firecrawl actions YAML for the story, when the generator produced one")
|
|
2273
4506
|
})
|
|
2274
4507
|
},
|
|
2275
4508
|
async ({ projectId: projectId2, storyNodeId, automationMode, baseUrl, openPr }) => {
|
|
@@ -2531,12 +4764,12 @@ Be specific. No generic advice.`
|
|
|
2531
4764
|
description: descOf("run_fullstack_audit"),
|
|
2532
4765
|
annotations: annotationsFor("run_fullstack_audit"),
|
|
2533
4766
|
inputSchema: z.object({
|
|
2534
|
-
|
|
4767
|
+
projectId: z.string().optional().describe("Project ID to audit. Defaults to the configured project.")
|
|
2535
4768
|
})
|
|
2536
4769
|
},
|
|
2537
|
-
async ({
|
|
2538
|
-
const pid =
|
|
2539
|
-
if (!pid) throw new MushiApiError(400, "MISSING_PROJECT_ID", "
|
|
4770
|
+
async ({ projectId: requested }) => {
|
|
4771
|
+
const pid = requested ?? config.projectId;
|
|
4772
|
+
if (!pid) throw new MushiApiError(400, "MISSING_PROJECT_ID", "projectId is required");
|
|
2540
4773
|
const data = await apiCall(`/v1/admin/projects/${pid}/audit`, {
|
|
2541
4774
|
method: "POST",
|
|
2542
4775
|
body: "{}"
|
|
@@ -2551,22 +4784,22 @@ Be specific. No generic advice.`
|
|
|
2551
4784
|
description: descOf("get_backend_health"),
|
|
2552
4785
|
annotations: annotationsFor("get_backend_health"),
|
|
2553
4786
|
inputSchema: z.object({
|
|
2554
|
-
|
|
2555
|
-
|
|
4787
|
+
projectId: z.string().optional().describe("Project ID. Defaults to the configured project."),
|
|
4788
|
+
includeLogs: z.boolean().optional().describe("Whether to include recent backend error logs (default: true).")
|
|
2556
4789
|
})
|
|
2557
4790
|
},
|
|
2558
|
-
async ({
|
|
2559
|
-
const pid =
|
|
2560
|
-
if (!pid) throw new MushiApiError(400, "MISSING_PROJECT_ID", "
|
|
4791
|
+
async ({ projectId: requested, includeLogs = true }) => {
|
|
4792
|
+
const pid = requested ?? config.projectId;
|
|
4793
|
+
if (!pid) throw new MushiApiError(400, "MISSING_PROJECT_ID", "projectId is required");
|
|
2561
4794
|
const [schemaRes, advisorsRes, logsRes] = await Promise.allSettled([
|
|
2562
4795
|
apiCall(`/v1/admin/projects/${pid}/backend/schema`),
|
|
2563
4796
|
apiCall(`/v1/admin/projects/${pid}/db-advisors`),
|
|
2564
|
-
|
|
4797
|
+
includeLogs ? apiCall(`/v1/admin/projects/${pid}/backend/logs?service=api`) : Promise.resolve(null)
|
|
2565
4798
|
]);
|
|
2566
4799
|
const result = {
|
|
2567
4800
|
schema: schemaRes.status === "fulfilled" ? schemaRes.value : { error: String(schemaRes.reason) },
|
|
2568
4801
|
advisors: advisorsRes.status === "fulfilled" ? advisorsRes.value : { error: String(advisorsRes.reason) },
|
|
2569
|
-
logs: logsRes.status === "fulfilled" ? logsRes.value :
|
|
4802
|
+
logs: logsRes.status === "fulfilled" ? logsRes.value : includeLogs ? { error: String(logsRes.reason) } : null
|
|
2570
4803
|
};
|
|
2571
4804
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
2572
4805
|
}
|
|
@@ -2578,14 +4811,28 @@ Be specific. No generic advice.`
|
|
|
2578
4811
|
description: descOf("get_usage"),
|
|
2579
4812
|
annotations: annotationsFor("get_usage"),
|
|
2580
4813
|
inputSchema: z.object({
|
|
2581
|
-
|
|
4814
|
+
projectId: z.string().optional().describe("Project UUID \u2014 defaults to the configured project.")
|
|
4815
|
+
}),
|
|
4816
|
+
// Loose: the billing stats route returns ~30 fields (plan, report and
|
|
4817
|
+
// diagnosis quotas, spend cap, period); the documented ones are typed.
|
|
4818
|
+
outputSchema: z.looseObject({
|
|
4819
|
+
projectId: z.string().nullable().optional(),
|
|
4820
|
+
planId: z.string().optional().describe("Active plan id, e.g. free_cloud, hobby, pro"),
|
|
4821
|
+
planDisplayName: z.string().optional(),
|
|
4822
|
+
diagnosesUsed: z.number().optional().describe("Diagnoses used this billing period"),
|
|
4823
|
+
diagnosesLimit: z.number().nullable().optional().describe("Diagnoses included in the plan (null = unlimited)"),
|
|
4824
|
+
diagnosesUsagePct: z.number().nullable().optional(),
|
|
4825
|
+
overDiagnosisQuota: z.boolean().optional(),
|
|
4826
|
+
approachingDiagnosisQuota: z.boolean().optional(),
|
|
4827
|
+
monthlySpendCapUsd: z.number().nullable().optional(),
|
|
4828
|
+
periodEnd: z.string().nullable().optional().describe("ISO timestamp the billing period ends"),
|
|
4829
|
+
freeLimitDiagnoses: z.number().optional()
|
|
2582
4830
|
})
|
|
2583
4831
|
},
|
|
2584
|
-
async (
|
|
2585
|
-
const
|
|
2586
|
-
const
|
|
2587
|
-
|
|
2588
|
-
return jsonText(data);
|
|
4832
|
+
async (args) => {
|
|
4833
|
+
const headers = args.projectId ? (await projectScopeHeaders(args.projectId)).headers : void 0;
|
|
4834
|
+
const data = await apiCall("/v1/admin/billing/stats", { headers });
|
|
4835
|
+
return jsonResult(data);
|
|
2589
4836
|
}
|
|
2590
4837
|
);
|
|
2591
4838
|
server.registerTool(
|
|
@@ -2595,7 +4842,7 @@ Be specific. No generic advice.`
|
|
|
2595
4842
|
description: descOf("list_skills"),
|
|
2596
4843
|
annotations: annotationsFor("list_skills"),
|
|
2597
4844
|
inputSchema: z.object({
|
|
2598
|
-
category: z.
|
|
4845
|
+
category: z.enum(SKILL_CATEGORIES).optional().describe("Only skills in this category (the slug prefix)."),
|
|
2599
4846
|
search: z.string().optional().describe("Free-text search across slug, title, description"),
|
|
2600
4847
|
page: z.number().optional().describe("Page number (default 1)"),
|
|
2601
4848
|
limit: z.number().optional().describe("Max results per page (default 200, max 200)")
|
|
@@ -2634,20 +4881,25 @@ Be specific. No generic advice.`
|
|
|
2634
4881
|
description: descOf("start_skill_pipeline"),
|
|
2635
4882
|
annotations: annotationsFor("start_skill_pipeline"),
|
|
2636
4883
|
inputSchema: z.object({
|
|
2637
|
-
|
|
2638
|
-
|
|
4884
|
+
rootSkillSlug: z.string().describe('Root skill slug to run, e.g. "workflow-fix-and-ship"'),
|
|
4885
|
+
reportId: z.string().optional().describe("Report UUID to attach the pipeline to"),
|
|
2639
4886
|
mode: z.enum(["handoff", "cloud"]).optional().describe(
|
|
2640
4887
|
"handoff (default): get context packet for local agent. cloud: auto-dispatch via Cursor Cloud."
|
|
2641
4888
|
),
|
|
2642
|
-
|
|
4889
|
+
projectId: z.string().optional().describe("Project UUID. Falls back to the configured project.")
|
|
2643
4890
|
})
|
|
2644
4891
|
},
|
|
2645
4892
|
async (args) => {
|
|
2646
|
-
const resolvedProjectId = args.
|
|
2647
|
-
if (!resolvedProjectId) return jsonText({ error: "No
|
|
4893
|
+
const resolvedProjectId = args.projectId ?? projectId;
|
|
4894
|
+
if (!resolvedProjectId) return jsonText({ error: "No projectId provided or configured." });
|
|
2648
4895
|
const data = await apiCall("/v1/admin/skills/pipelines", {
|
|
2649
4896
|
method: "POST",
|
|
2650
|
-
body: JSON.stringify({
|
|
4897
|
+
body: JSON.stringify({
|
|
4898
|
+
root_skill_slug: args.rootSkillSlug,
|
|
4899
|
+
report_id: args.reportId,
|
|
4900
|
+
mode: args.mode,
|
|
4901
|
+
project_id: resolvedProjectId
|
|
4902
|
+
})
|
|
2651
4903
|
});
|
|
2652
4904
|
return jsonText(data);
|
|
2653
4905
|
}
|
|
@@ -2659,11 +4911,11 @@ Be specific. No generic advice.`
|
|
|
2659
4911
|
description: descOf("get_pipeline_run"),
|
|
2660
4912
|
annotations: annotationsFor("get_pipeline_run"),
|
|
2661
4913
|
inputSchema: z.object({
|
|
2662
|
-
|
|
4914
|
+
runId: z.string().describe("Pipeline run UUID")
|
|
2663
4915
|
})
|
|
2664
4916
|
},
|
|
2665
4917
|
async (args) => {
|
|
2666
|
-
const data = await apiCall(`/v1/admin/skills/pipelines/${args.
|
|
4918
|
+
const data = await apiCall(`/v1/admin/skills/pipelines/${args.runId}`);
|
|
2667
4919
|
return jsonText(data);
|
|
2668
4920
|
}
|
|
2669
4921
|
);
|
|
@@ -2674,21 +4926,20 @@ Be specific. No generic advice.`
|
|
|
2674
4926
|
description: descOf("checkin_pipeline_step"),
|
|
2675
4927
|
annotations: annotationsFor("checkin_pipeline_step"),
|
|
2676
4928
|
inputSchema: z.object({
|
|
2677
|
-
|
|
2678
|
-
|
|
4929
|
+
runId: z.string().describe("Pipeline run UUID"),
|
|
4930
|
+
stepIndex: z.number().describe("Step index (0-based)"),
|
|
2679
4931
|
status: z.enum(["running", "passed", "failed", "skipped"]).describe("Step status"),
|
|
2680
4932
|
notes: z.string().optional().describe("Optional notes or output summary"),
|
|
2681
|
-
|
|
2682
|
-
|
|
4933
|
+
prUrl: z.string().optional().describe("PR URL opened during this step"),
|
|
4934
|
+
agentRef: z.string().optional().describe("Cursor agentId or external agent reference")
|
|
2683
4935
|
})
|
|
2684
4936
|
},
|
|
2685
4937
|
async (args) => {
|
|
2686
|
-
|
|
2687
|
-
await apiCall(`/v1/admin/skills/pipelines/${run_id}/steps/${step_index}/checkin`, {
|
|
4938
|
+
await apiCall(`/v1/admin/skills/pipelines/${args.runId}/steps/${args.stepIndex}/checkin`, {
|
|
2688
4939
|
method: "POST",
|
|
2689
|
-
body: JSON.stringify(
|
|
4940
|
+
body: JSON.stringify({ status: args.status, notes: args.notes, pr_url: args.prUrl, agent_ref: args.agentRef })
|
|
2690
4941
|
});
|
|
2691
|
-
return jsonText({ ok: true, message: `Step ${
|
|
4942
|
+
return jsonText({ ok: true, message: `Step ${args.stepIndex} \u2192 ${args.status}` });
|
|
2692
4943
|
}
|
|
2693
4944
|
);
|
|
2694
4945
|
server.registerTool(
|
|
@@ -2698,24 +4949,24 @@ Be specific. No generic advice.`
|
|
|
2698
4949
|
description: descOf("ask_codebase", CODEBASE_TOOL_CATALOG),
|
|
2699
4950
|
annotations: annotationsFor("ask_codebase", CODEBASE_TOOL_CATALOG),
|
|
2700
4951
|
inputSchema: z.object({
|
|
2701
|
-
|
|
4952
|
+
projectId: z.string().optional().describe("Project UUID (defaults to configured project)"),
|
|
2702
4953
|
question: z.string().describe("Plain-English question about the repo"),
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
4954
|
+
threadId: z.string().optional().describe("Optional thread UUID to continue a conversation"),
|
|
4955
|
+
filePath: z.string().optional().describe("Optional file path focus"),
|
|
4956
|
+
symbolName: z.string().optional().describe("Optional symbol name focus")
|
|
2706
4957
|
})
|
|
2707
4958
|
},
|
|
2708
4959
|
async (args) => {
|
|
2709
|
-
const pid = args.
|
|
2710
|
-
if (!pid) throw new MushiApiError(400, "MISSING_PROJECT", "
|
|
4960
|
+
const pid = args.projectId ?? projectId;
|
|
4961
|
+
if (!pid) throw new MushiApiError(400, "MISSING_PROJECT", "projectId is required");
|
|
2711
4962
|
const body = {
|
|
2712
4963
|
messages: [{ role: "user", content: args.question }]
|
|
2713
4964
|
};
|
|
2714
|
-
if (args.
|
|
2715
|
-
if (args.
|
|
4965
|
+
if (args.threadId) body.threadId = args.threadId;
|
|
4966
|
+
if (args.filePath) {
|
|
2716
4967
|
body.fileFocus = {
|
|
2717
|
-
file_path: args.
|
|
2718
|
-
symbol_name: args.
|
|
4968
|
+
file_path: args.filePath,
|
|
4969
|
+
symbol_name: args.symbolName ?? null
|
|
2719
4970
|
};
|
|
2720
4971
|
}
|
|
2721
4972
|
const data = await apiCall(`/v1/admin/projects/${pid}/codebase/chat`, {
|
|
@@ -2732,17 +4983,17 @@ Be specific. No generic advice.`
|
|
|
2732
4983
|
description: descOf("get_file_summary", CODEBASE_TOOL_CATALOG),
|
|
2733
4984
|
annotations: annotationsFor("get_file_summary", CODEBASE_TOOL_CATALOG),
|
|
2734
4985
|
inputSchema: z.object({
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
4986
|
+
projectId: z.string().optional().describe("Project UUID (defaults to configured project)"),
|
|
4987
|
+
filePath: z.string().describe("Indexed file path"),
|
|
4988
|
+
symbolName: z.string().optional().describe("Optional symbol name within the file"),
|
|
2738
4989
|
force: z.boolean().optional().describe("Bypass cache and regenerate")
|
|
2739
4990
|
})
|
|
2740
4991
|
},
|
|
2741
4992
|
async (args) => {
|
|
2742
|
-
const pid = args.
|
|
2743
|
-
if (!pid) throw new MushiApiError(400, "MISSING_PROJECT", "
|
|
2744
|
-
const qs = new URLSearchParams({ file_path: args.
|
|
2745
|
-
if (args.
|
|
4993
|
+
const pid = args.projectId ?? projectId;
|
|
4994
|
+
if (!pid) throw new MushiApiError(400, "MISSING_PROJECT", "projectId is required");
|
|
4995
|
+
const qs = new URLSearchParams({ file_path: args.filePath });
|
|
4996
|
+
if (args.symbolName) qs.set("symbol_name", args.symbolName);
|
|
2746
4997
|
if (args.force) qs.set("force", "1");
|
|
2747
4998
|
const data = await apiCall(`/v1/admin/projects/${pid}/codebase/summary?${qs}`);
|
|
2748
4999
|
return jsonText(data);
|
|
@@ -2755,13 +5006,13 @@ Be specific. No generic advice.`
|
|
|
2755
5006
|
description: descOf("get_codebase_tour", CODEBASE_TOOL_CATALOG),
|
|
2756
5007
|
annotations: annotationsFor("get_codebase_tour", CODEBASE_TOOL_CATALOG),
|
|
2757
5008
|
inputSchema: z.object({
|
|
2758
|
-
|
|
5009
|
+
projectId: z.string().optional().describe("Project UUID (defaults to configured project)"),
|
|
2759
5010
|
force: z.boolean().optional().describe("Bypass cache and regenerate")
|
|
2760
5011
|
})
|
|
2761
5012
|
},
|
|
2762
5013
|
async (args) => {
|
|
2763
|
-
const pid = args.
|
|
2764
|
-
if (!pid) throw new MushiApiError(400, "MISSING_PROJECT", "
|
|
5014
|
+
const pid = args.projectId ?? projectId;
|
|
5015
|
+
if (!pid) throw new MushiApiError(400, "MISSING_PROJECT", "projectId is required");
|
|
2765
5016
|
const qs = args.force ? "?force=1" : "";
|
|
2766
5017
|
const data = await apiCall(`/v1/admin/projects/${pid}/codebase/tour${qs}`);
|
|
2767
5018
|
return jsonText(data);
|
|
@@ -2774,20 +5025,21 @@ Be specific. No generic advice.`
|
|
|
2774
5025
|
description: descOf("search_codebase", CODEBASE_TOOL_CATALOG),
|
|
2775
5026
|
annotations: annotationsFor("search_codebase", CODEBASE_TOOL_CATALOG),
|
|
2776
5027
|
inputSchema: z.object({
|
|
2777
|
-
|
|
5028
|
+
projectId: z.string().optional().describe("Project UUID (defaults to configured project)"),
|
|
2778
5029
|
query: z.string().describe("Plain-English search query against indexed source files"),
|
|
2779
5030
|
k: z.number().int().min(1).max(20).optional().describe("Max hits to return (default 8, max 20)"),
|
|
2780
|
-
|
|
5031
|
+
scopePrefix: z.string().optional().describe("Optional repo subdirectory prefix to limit search scope"),
|
|
5032
|
+
mode: z.enum(CODEBASE_SEARCH_MODES).optional().describe("semantic (default): embedding similarity. name: substring match on file path and symbol name.")
|
|
2781
5033
|
}),
|
|
2782
5034
|
outputSchema: z.object({
|
|
2783
5035
|
results: z.array(z.unknown()).describe("Ranked file/symbol hits with paths, line ranges, and similarity"),
|
|
2784
5036
|
query: z.string().describe("Normalized query string that was searched"),
|
|
2785
|
-
mode: z.
|
|
5037
|
+
mode: z.enum(CODEBASE_SEARCH_MODES).optional().describe("Which search ran: semantic or name")
|
|
2786
5038
|
})
|
|
2787
5039
|
},
|
|
2788
5040
|
async (args) => {
|
|
2789
|
-
const pid = args.
|
|
2790
|
-
if (!pid) throw new MushiApiError(400, "MISSING_PROJECT", "
|
|
5041
|
+
const pid = args.projectId ?? projectId;
|
|
5042
|
+
if (!pid) throw new MushiApiError(400, "MISSING_PROJECT", "projectId is required");
|
|
2791
5043
|
const data = await apiCall(
|
|
2792
5044
|
`/v1/admin/projects/${pid}/codebase/search`,
|
|
2793
5045
|
{
|
|
@@ -2795,7 +5047,8 @@ Be specific. No generic advice.`
|
|
|
2795
5047
|
body: JSON.stringify({
|
|
2796
5048
|
query: args.query,
|
|
2797
5049
|
k: args.k ?? 8,
|
|
2798
|
-
scope_prefix: args.
|
|
5050
|
+
scope_prefix: args.scopePrefix ?? void 0,
|
|
5051
|
+
mode: args.mode
|
|
2799
5052
|
})
|
|
2800
5053
|
}
|
|
2801
5054
|
);
|
|
@@ -2809,17 +5062,17 @@ Be specific. No generic advice.`
|
|
|
2809
5062
|
description: descOf("get_codebase_domains", CODEBASE_TOOL_CATALOG),
|
|
2810
5063
|
annotations: annotationsFor("get_codebase_domains", CODEBASE_TOOL_CATALOG),
|
|
2811
5064
|
inputSchema: z.object({
|
|
2812
|
-
|
|
5065
|
+
projectId: z.string().optional().describe("Project UUID (defaults to configured project)"),
|
|
2813
5066
|
force: z.boolean().optional().describe("Bypass cache and regenerate"),
|
|
2814
|
-
|
|
5067
|
+
scopePrefix: z.string().optional().describe("Optional subdirectory scope prefix")
|
|
2815
5068
|
})
|
|
2816
5069
|
},
|
|
2817
5070
|
async (args) => {
|
|
2818
|
-
const pid = args.
|
|
2819
|
-
if (!pid) throw new MushiApiError(400, "MISSING_PROJECT", "
|
|
5071
|
+
const pid = args.projectId ?? projectId;
|
|
5072
|
+
if (!pid) throw new MushiApiError(400, "MISSING_PROJECT", "projectId is required");
|
|
2820
5073
|
const qs = new URLSearchParams();
|
|
2821
5074
|
if (args.force) qs.set("force", "1");
|
|
2822
|
-
if (args.
|
|
5075
|
+
if (args.scopePrefix) qs.set("scope_prefix", args.scopePrefix);
|
|
2823
5076
|
const suffix = qs.toString() ? `?${qs}` : "";
|
|
2824
5077
|
const data = await apiCall(`/v1/admin/projects/${pid}/codebase/domains${suffix}`);
|
|
2825
5078
|
return jsonText(data);
|
|
@@ -2832,11 +5085,11 @@ Be specific. No generic advice.`
|
|
|
2832
5085
|
description: descOf("analyze_codebase_impact", CODEBASE_TOOL_CATALOG),
|
|
2833
5086
|
annotations: annotationsFor("analyze_codebase_impact", CODEBASE_TOOL_CATALOG),
|
|
2834
5087
|
inputSchema: z.object({
|
|
2835
|
-
|
|
5088
|
+
projectId: z.string().optional().describe("Project UUID (defaults to configured project)"),
|
|
2836
5089
|
paths: z.array(z.string()).optional().describe("Changed repo-relative file paths to analyze"),
|
|
2837
5090
|
source: z.enum(["last_push", "compare", "fix"]).optional().describe("Auto-resolve changed paths instead of manual paths"),
|
|
2838
5091
|
compare: z.string().optional().describe("GitHub compare range base...head when source=compare"),
|
|
2839
|
-
|
|
5092
|
+
fixId: z.string().optional().describe("Fix attempt UUID \u2014 uses that PR's changed files when source=fix")
|
|
2840
5093
|
}),
|
|
2841
5094
|
outputSchema: z.object({
|
|
2842
5095
|
changed_paths: z.array(z.string()).describe("Resolved set of changed paths analyzed"),
|
|
@@ -2847,13 +5100,13 @@ Be specific. No generic advice.`
|
|
|
2847
5100
|
})
|
|
2848
5101
|
},
|
|
2849
5102
|
async (args) => {
|
|
2850
|
-
const pid = args.
|
|
2851
|
-
if (!pid) throw new MushiApiError(400, "MISSING_PROJECT", "
|
|
5103
|
+
const pid = args.projectId ?? projectId;
|
|
5104
|
+
if (!pid) throw new MushiApiError(400, "MISSING_PROJECT", "projectId is required");
|
|
2852
5105
|
const qs = new URLSearchParams();
|
|
2853
5106
|
if (args.paths?.length) qs.set("paths", args.paths.join(","));
|
|
2854
5107
|
if (args.source === "last_push") qs.set("ref", "last_push");
|
|
2855
5108
|
if (args.compare) qs.set("compare", args.compare);
|
|
2856
|
-
if (args.
|
|
5109
|
+
if (args.fixId) qs.set("fix_id", args.fixId);
|
|
2857
5110
|
if (!qs.toString()) {
|
|
2858
5111
|
throw new MushiApiError(
|
|
2859
5112
|
400,
|
|
@@ -2872,12 +5125,12 @@ Be specific. No generic advice.`
|
|
|
2872
5125
|
description: descOf("analyze_wiki_knowledge", CODEBASE_TOOL_CATALOG),
|
|
2873
5126
|
annotations: annotationsFor("analyze_wiki_knowledge", CODEBASE_TOOL_CATALOG),
|
|
2874
5127
|
inputSchema: z.object({
|
|
2875
|
-
|
|
5128
|
+
projectId: z.string().optional().describe("Project UUID (defaults to configured project)")
|
|
2876
5129
|
})
|
|
2877
5130
|
},
|
|
2878
5131
|
async (args) => {
|
|
2879
|
-
const pid = args.
|
|
2880
|
-
if (!pid) throw new MushiApiError(400, "MISSING_PROJECT", "
|
|
5132
|
+
const pid = args.projectId ?? projectId;
|
|
5133
|
+
if (!pid) throw new MushiApiError(400, "MISSING_PROJECT", "projectId is required");
|
|
2881
5134
|
const data = await apiCall(`/v1/admin/projects/${pid}/codebase/knowledge/graph`);
|
|
2882
5135
|
return jsonText(data);
|
|
2883
5136
|
}
|
|
@@ -2895,23 +5148,29 @@ Be specific. No generic advice.`
|
|
|
2895
5148
|
annotations: annotationsFor("use_mushi")
|
|
2896
5149
|
},
|
|
2897
5150
|
async (args) => {
|
|
2898
|
-
const
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
const
|
|
2902
|
-
const
|
|
5151
|
+
const registered = server._registeredTools;
|
|
5152
|
+
const isAvailable = (tool) => Object.prototype.hasOwnProperty.call(registered, tool);
|
|
5153
|
+
const route = routeUseMushiIntent(args.intent ?? "", isAvailable);
|
|
5154
|
+
const projectLine = projectId ? `Connected project: \`${projectId}\`. ` : "No project configured \u2014 set MUSHI_PROJECT_ID, or pass projectId to project-scoped tools. ";
|
|
5155
|
+
const hiddenGroups = [
|
|
5156
|
+
...new Set(route.hidden.map((t) => TOOL_FEATURE_MAP[t]).filter((g) => !!g))
|
|
5157
|
+
];
|
|
2903
5158
|
const orientation = [
|
|
2904
|
-
`## Mushi \u2014 ${
|
|
5159
|
+
`## Mushi \u2014 ${route.label}`,
|
|
2905
5160
|
"",
|
|
2906
|
-
projectLine +
|
|
5161
|
+
projectLine + route.hint,
|
|
2907
5162
|
"",
|
|
2908
5163
|
"### Recommended tools for this intent",
|
|
2909
|
-
|
|
2910
|
-
"",
|
|
2911
|
-
|
|
2912
|
-
|
|
5164
|
+
route.tools.length > 0 ? route.tools.map((t) => `- \`${t}\``).join("\n") : "- (none enabled)",
|
|
5165
|
+
...route.firstTool ? ["", "### First step", `Call \`${route.firstTool}\` to get started.`] : [],
|
|
5166
|
+
...route.hidden.length > 0 ? [
|
|
5167
|
+
"",
|
|
5168
|
+
"### Also relevant, not enabled on this connection",
|
|
5169
|
+
route.hidden.map((t) => `- \`${t}\``).join("\n"),
|
|
5170
|
+
hiddenGroups.length > 0 ? `Enable them by adding ${hiddenGroups.map((g) => `\`${g}\``).join(", ")} to MUSHI_FEATURES (or set it to \`all\`).` : "They need a key with more scope."
|
|
5171
|
+
] : [],
|
|
2913
5172
|
"",
|
|
2914
|
-
|
|
5173
|
+
`> Tip: \`use_mushi\` is a read-only helper that never calls other tools itself. This connection exposes ${Object.keys(registered).length} tools.`
|
|
2915
5174
|
].join("\n");
|
|
2916
5175
|
return { content: [{ type: "text", text: orientation }] };
|
|
2917
5176
|
}
|
|
@@ -2930,6 +5189,34 @@ Be specific. No generic advice.`
|
|
|
2930
5189
|
}
|
|
2931
5190
|
}
|
|
2932
5191
|
}
|
|
5192
|
+
{
|
|
5193
|
+
const untrustedTools = new Set(
|
|
5194
|
+
ALL_TOOL_CATALOG.filter((t) => t.returnsUntrusted).map((t) => t.name)
|
|
5195
|
+
);
|
|
5196
|
+
const llUntrustedServer = server.server;
|
|
5197
|
+
const sdkToolsCallHandler = llUntrustedServer?._requestHandlers?.get("tools/call");
|
|
5198
|
+
if (typeof sdkToolsCallHandler === "function") {
|
|
5199
|
+
llUntrustedServer._requestHandlers.set("tools/call", async (...args) => {
|
|
5200
|
+
const result = await sdkToolsCallHandler(...args);
|
|
5201
|
+
const name = args[0]?.params?.name;
|
|
5202
|
+
if (!name || !untrustedTools.has(name) || result === null || typeof result !== "object") {
|
|
5203
|
+
return result;
|
|
5204
|
+
}
|
|
5205
|
+
const r = result;
|
|
5206
|
+
if (r.isError || !Array.isArray(r.content)) return result;
|
|
5207
|
+
return {
|
|
5208
|
+
...r,
|
|
5209
|
+
content: r.content.map((block) => {
|
|
5210
|
+
const b = block;
|
|
5211
|
+
if (b?.type !== "text" || typeof b.text !== "string" || isWrappedUntrusted(b.text)) {
|
|
5212
|
+
return block;
|
|
5213
|
+
}
|
|
5214
|
+
return { ...b, text: wrapUntrustedJson(b.text, name) };
|
|
5215
|
+
})
|
|
5216
|
+
};
|
|
5217
|
+
});
|
|
5218
|
+
}
|
|
5219
|
+
}
|
|
2933
5220
|
const _nativeFetch = globalThis.fetch;
|
|
2934
5221
|
let _firstToolCallSignalled = false;
|
|
2935
5222
|
const _signalFirstToolCall = () => {
|
|
@@ -2991,8 +5278,76 @@ Be specific. No generic advice.`
|
|
|
2991
5278
|
}
|
|
2992
5279
|
return server;
|
|
2993
5280
|
}
|
|
5281
|
+
var SETUP_MODE_TOOLS = ["search_mushi_docs", "get_mushi_doc", "diagnose_setup"];
|
|
5282
|
+
var MUSHI_CONSOLE_URL = "https://kensaur.us/mushi-mushi/admin";
|
|
5283
|
+
var MUSHI_MCP_QUICKSTART_URL = "https://kensaur.us/mushi-mushi/docs/quickstart/mcp";
|
|
5284
|
+
function createSetupModeServer(config) {
|
|
5285
|
+
const server = createMushiServer({
|
|
5286
|
+
version: config.version,
|
|
5287
|
+
// Nothing reaches the API in setup mode: every API-backed tool, resource
|
|
5288
|
+
// and prompt is removed below before a client can list them.
|
|
5289
|
+
apiEndpoint: "https://setup-mode.invalid",
|
|
5290
|
+
apiKey: "",
|
|
5291
|
+
features: "all",
|
|
5292
|
+
...config.fetch ? { fetch: config.fetch } : {},
|
|
5293
|
+
...config.timeoutMs ? { timeoutMs: config.timeoutMs } : {},
|
|
5294
|
+
instructions: "Mushi is running in setup mode: no API key is configured, so only search_mushi_docs, get_mushi_doc and diagnose_setup work. Call diagnose_setup for the exact steps to connect (run `npx mushi-mushi` or `mushi login`, or put MUSHI_API_KEY in this server's env block), then restart the MCP server."
|
|
5295
|
+
});
|
|
5296
|
+
const internals = server;
|
|
5297
|
+
for (const [name, tool] of Object.entries(internals._registeredTools)) {
|
|
5298
|
+
if (name === "diagnose_setup" || !SETUP_MODE_TOOLS.includes(name)) tool.remove();
|
|
5299
|
+
}
|
|
5300
|
+
for (const registry of [
|
|
5301
|
+
internals._registeredResources,
|
|
5302
|
+
internals._registeredResourceTemplates,
|
|
5303
|
+
internals._registeredPrompts
|
|
5304
|
+
]) {
|
|
5305
|
+
for (const entry of Object.values(registry ?? {})) entry.remove();
|
|
5306
|
+
}
|
|
5307
|
+
const spec = TOOL_CATALOG.find((t) => t.name === "diagnose_setup");
|
|
5308
|
+
if (!spec) throw new Error("[mushi-mcp] diagnose_setup is missing from TOOL_CATALOG");
|
|
5309
|
+
const nextAction = "Run `npx mushi-mushi` in your project (signs you in and writes the key) or `mushi login`, then restart this MCP server.";
|
|
5310
|
+
server.registerTool(
|
|
5311
|
+
"diagnose_setup",
|
|
5312
|
+
{
|
|
5313
|
+
title: spec.title,
|
|
5314
|
+
description: spec.description,
|
|
5315
|
+
annotations: {
|
|
5316
|
+
title: spec.title,
|
|
5317
|
+
readOnlyHint: true,
|
|
5318
|
+
idempotentHint: true,
|
|
5319
|
+
openWorldHint: false
|
|
5320
|
+
},
|
|
5321
|
+
inputSchema: z.object({
|
|
5322
|
+
mode: z.enum(["full", "ingest", "dispatch"]).optional(),
|
|
5323
|
+
projectId: z.string().optional()
|
|
5324
|
+
})
|
|
5325
|
+
},
|
|
5326
|
+
async () => {
|
|
5327
|
+
const diagnosis = {
|
|
5328
|
+
ready: false,
|
|
5329
|
+
mode: "setup",
|
|
5330
|
+
steps: [
|
|
5331
|
+
{
|
|
5332
|
+
label: "MCP server has an API key",
|
|
5333
|
+
complete: false,
|
|
5334
|
+
required: true,
|
|
5335
|
+
hint: `${nextAction} Or mint a key in the console (${MUSHI_CONSOLE_URL} \u2192 Settings \u2192 API keys) and add MUSHI_API_KEY (and MUSHI_PROJECT_ID) to the "env" block of this server in your MCP client config \u2014 a key exported in your shell does not reach the server.`
|
|
5336
|
+
}
|
|
5337
|
+
],
|
|
5338
|
+
nextAction,
|
|
5339
|
+
alternative: `No local key at all: connect the hosted server over OAuth instead \u2014 ${MUSHI_MCP_QUICKSTART_URL}`,
|
|
5340
|
+
details: config.missingKeyReport.trim()
|
|
5341
|
+
};
|
|
5342
|
+
return { content: [{ type: "text", text: JSON.stringify(diagnosis, null, 2) }] };
|
|
5343
|
+
}
|
|
5344
|
+
);
|
|
5345
|
+
return server;
|
|
5346
|
+
}
|
|
2994
5347
|
export {
|
|
2995
5348
|
MushiApiError,
|
|
5349
|
+
SETUP_MODE_TOOLS,
|
|
2996
5350
|
createMushiServer,
|
|
5351
|
+
createSetupModeServer,
|
|
2997
5352
|
resolveTimeoutMsFromEnv
|
|
2998
5353
|
};
|