@mushi-mushi/mcp 0.16.1 → 0.16.2

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/CONTRIBUTING.md CHANGED
@@ -27,12 +27,24 @@ pnpm build
27
27
 
28
28
  ```bash
29
29
  pnpm dev # Start all dev servers (admin on :6464)
30
+ ```
31
+
32
+ | Surface | URL |
33
+ | --- | --- |
34
+ | Admin console | http://localhost:6464 |
35
+ | Docs site | http://localhost:3000 |
36
+ | CLI console override | `MUSHI_CONSOLE_URL=http://localhost:6464` |
37
+
38
+ ```bash
30
39
  pnpm test # Run Vitest across all packages
31
40
  pnpm typecheck # TypeScript checks
32
41
  pnpm lint # ESLint
33
42
  pnpm format # Prettier
43
+ pnpm smoke:cli-setup # Playwright: onboarding ?setup=cli + success panel (skips headless if unsigned)
34
44
  ```
35
45
 
46
+ Set `CLI_SETUP_SMOKE_STRICT=1` to fail the smoke script when Playwright is not signed in (CI gate).
47
+
36
48
  Ad-hoc screenshots captured during UI reviews can live temporarily at the repo
37
49
  root, but root-level `*.png` files are intentionally ignored. Canonical
38
50
  screenshots that should be versioned belong under `docs/screenshots/`.
@@ -58,7 +70,7 @@ packages/
58
70
  react-native/ React Native SDK (MIT)
59
71
  cli/ CLI tool (MIT)
60
72
  mcp/ MCP server for coding agents (MIT)
61
- server/ Supabase Edge Functions (AGPLv3)
73
+ server/ Supabase Edge Functions (AGPLv3; ee/ is commercial)
62
74
  agents/ Agentic fix pipeline (AGPLv3)
63
75
  verify/ Fix verification (AGPLv3)
64
76
  apps/
@@ -69,6 +81,18 @@ tooling/
69
81
  tsconfig/ Shared TypeScript configs
70
82
  ```
71
83
 
84
+ ## What we don't accept
85
+
86
+ Knowing this upfront saves everyone time.
87
+
88
+ - **Automated / bulk / AI-slop PRs** — any PR that was opened by a script, an LLM agent acting without meaningful human review, or as part of a parallel bounty-farming sweep is closed on sight. The commit history, branch name, or helper scripts included in the PR make this obvious.
89
+ - **PRs that target the wrong base** — all PRs must branch from and target `master`. PRs targeting `main` or any other branch are closed (and will show an inflated diff that is not your work).
90
+ - **One PR per issue, one issue per PR** — splitting or duplicating work just to collect acknowledgements isn't welcome.
91
+ - **Good-first-issues without claiming first** — comment on the issue to claim it before starting. If two people open PRs for the same issue, the one that commented first is prioritised.
92
+ - **Documentation-only PRs that merely duplicate existing content** — if the information already exists (e.g. in another file in this repo), adding a redundant copy adds noise without value.
93
+
94
+ If a PR violates these policies it will be closed with a brief explanation. No hard feelings — a genuine contribution on the same issue is always welcome.
95
+
72
96
  ## Making Changes
73
97
 
74
98
  1. Create a feature branch from `master`
@@ -187,7 +211,7 @@ chore: bump dependencies
187
211
  ## License
188
212
 
189
213
  - SDK packages are MIT — your contributions will be MIT-licensed
190
- - Server/agents/verify are AGPLv3 — contributions to those packages fall under AGPLv3
214
+ - Server/agents/verify are AGPLv3 — contributions to those packages fall under AGPLv3. A [commercial license](./COMMERCIAL-LICENSE.md) is available for hosted-SaaS use cases that cannot publish modifications. The `packages/server/ee/` directory is source-available but commercial (production use needs a license); contributions there are accepted under the same EE terms.
191
215
 
192
216
  ## Questions?
193
217
 
package/SECURITY.md CHANGED
@@ -113,7 +113,7 @@ What we treat as in-scope attacker capabilities, and what we don't.
113
113
  | Compromised Supabase service-role key | ❌ | Treated as a tier-0 incident; would require key rotation and audit-log forensics. Not defendable in software. |
114
114
  | Compromise of `kensaurus@gmail.com` | ❌ | Treated as a project-fork event; downstream consumers should pin to the last known-good version and follow the new release channel. |
115
115
  | Physical / OS-level attacker on an end-user device | ❌ | Out of scope. |
116
- | Malicious fork using the Mushi name to ship malware | ❌ (technical) ✅ (legal) | The MIT/BSL grant lets the fork exist; the trademark policy (`TRADEMARK.md`) makes shipping it under the Mushi name an infringement we will pursue. |
116
+ | Malicious fork using the Mushi name to ship malware | ❌ (technical) ✅ (legal) | The MIT / AGPLv3 grant lets the fork exist; the trademark policy (`TRADEMARK.md`) makes shipping it under the Mushi name an infringement we will pursue. |
117
117
 
118
118
  ## Data handling and PII
119
119
 
package/dist/catalog.js CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  RESOURCE_CATALOG,
6
6
  TDD_TOOL_CATALOG,
7
7
  TOOL_CATALOG
8
- } from "./chunk-UYZEIJ5G.js";
8
+ } from "./chunk-MM3IMFM4.js";
9
9
  export {
10
10
  ALL_SCOPES,
11
11
  CODEBASE_TOOL_CATALOG,
@@ -534,6 +534,15 @@ var TDD_TOOL_CATALOG = [
534
534
  hints: { readOnly: false, destructive: false, idempotent: false, openWorld: true },
535
535
  useCase: 'Did "Add to Slack" work? Send a test ping to confirm.'
536
536
  },
537
+ // ── Billing / usage tools ─────────────────────────────────────────────────
538
+ {
539
+ name: "get_usage",
540
+ title: "Usage & billing summary",
541
+ description: 'Read-only diagnoses quota and billing summary for the current project: diagnoses used / limit / percentage, spend cap, period start/end, plan name, and whether the project is approaching or over its quota. Use this to answer "how many diagnoses do I have left?" or "am I close to my spend cap?".',
542
+ scope: "mcp:read",
543
+ hints: { readOnly: true, idempotent: true, openWorld: false },
544
+ useCase: "How many diagnoses do I have left this month?"
545
+ },
537
546
  // ── Skill Pipeline tools ─────────────────────────────────────────────────────
538
547
  {
539
548
  name: "list_skills",
@@ -10,6 +10,7 @@ var FEATURE_GROUPS = [
10
10
  "admin",
11
11
  "audit",
12
12
  "docs",
13
+ "usage",
13
14
  /** Deprecated aliases kept for one release — hidden unless `legacy` or `all`. */
14
15
  "legacy"
15
16
  ];
@@ -82,6 +83,8 @@ var TOOL_FEATURE_MAP = {
82
83
  project_settings: "admin",
83
84
  privacy_status: "admin",
84
85
  evolution_history: "admin",
86
+ // usage / billing
87
+ get_usage: "usage",
85
88
  // qa / tdd
86
89
  map_user_stories: "qa",
87
90
  get_map_run_status: "qa",
@@ -4,7 +4,7 @@
4
4
  * When `features` is omitted → all tools (backward compatible).
5
5
  * New installs default to DEFAULT_FEATURE_GROUPS via deeplink / docs.
6
6
  */
7
- declare const FEATURE_GROUPS: readonly ["triage", "fixes", "inventory", "setup", "qa", "skills", "rewards", "admin", "audit", "docs", "legacy"];
7
+ declare const FEATURE_GROUPS: readonly ["triage", "fixes", "inventory", "setup", "qa", "skills", "rewards", "admin", "audit", "docs", "usage", "legacy"];
8
8
  type FeatureGroup = (typeof FEATURE_GROUPS)[number];
9
9
  /** Lean default for new Cursor installs (~45 tools vs 70 full). */
10
10
  declare const DEFAULT_FEATURE_GROUPS: readonly FeatureGroup[];
@@ -9,7 +9,7 @@ import {
9
9
  parseFeaturesCsv,
10
10
  parseFeaturesParam,
11
11
  toolMatchesFeatures
12
- } from "./chunk-B2QEG35D.js";
12
+ } from "./chunk-ZX3XIJ7S.js";
13
13
  export {
14
14
  ALL_FEATURE_GROUPS,
15
15
  DEFAULT_FEATURE_GROUPS,
package/dist/index.js CHANGED
@@ -457,6 +457,15 @@ var TDD_TOOL_CATALOG = [
457
457
  hints: { readOnly: false, destructive: false, idempotent: false, openWorld: true },
458
458
  useCase: 'Did "Add to Slack" work? Send a test ping to confirm.'
459
459
  },
460
+ // ── Billing / usage tools ─────────────────────────────────────────────────
461
+ {
462
+ name: "get_usage",
463
+ title: "Usage & billing summary",
464
+ description: 'Read-only diagnoses quota and billing summary for the current project: diagnoses used / limit / percentage, spend cap, period start/end, plan name, and whether the project is approaching or over its quota. Use this to answer "how many diagnoses do I have left?" or "am I close to my spend cap?".',
465
+ scope: "mcp:read",
466
+ hints: { readOnly: true, idempotent: true, openWorld: false },
467
+ useCase: "How many diagnoses do I have left this month?"
468
+ },
460
469
  // ── Skill Pipeline tools ─────────────────────────────────────────────────────
461
470
  {
462
471
  name: "list_skills",
@@ -603,6 +612,7 @@ var FEATURE_GROUPS = [
603
612
  "admin",
604
613
  "audit",
605
614
  "docs",
615
+ "usage",
606
616
  /** Deprecated aliases kept for one release — hidden unless `legacy` or `all`. */
607
617
  "legacy"
608
618
  ];
@@ -668,6 +678,8 @@ var TOOL_FEATURE_MAP = {
668
678
  project_settings: "admin",
669
679
  privacy_status: "admin",
670
680
  evolution_history: "admin",
681
+ // usage / billing
682
+ get_usage: "usage",
671
683
  // qa / tdd
672
684
  map_user_stories: "qa",
673
685
  get_map_run_status: "qa",
@@ -2699,6 +2711,23 @@ Be specific. No generic advice.`
2699
2711
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
2700
2712
  }
2701
2713
  );
2714
+ server.registerTool(
2715
+ "get_usage",
2716
+ {
2717
+ title: titleOf("get_usage"),
2718
+ description: descOf("get_usage"),
2719
+ annotations: annotationsFor("get_usage"),
2720
+ inputSchema: {
2721
+ project_id: z.string().optional().describe("Project ID. Defaults to the configured project.")
2722
+ }
2723
+ },
2724
+ async ({ project_id }) => {
2725
+ const pid = project_id ?? config.projectId;
2726
+ const path = pid ? `/v1/admin/billing/stats?project_id=${pid}` : "/v1/admin/billing/stats";
2727
+ const data = await apiCall(path);
2728
+ return jsonText(data);
2729
+ }
2730
+ );
2702
2731
  server.registerTool(
2703
2732
  "list_skills",
2704
2733
  {
@@ -3020,6 +3049,32 @@ Be specific. No generic advice.`
3020
3049
  }
3021
3050
  }
3022
3051
  }
3052
+ const _nativeFetch = globalThis.fetch;
3053
+ let _firstToolCallSignalled = false;
3054
+ const _signalFirstToolCall = () => {
3055
+ if (_firstToolCallSignalled || !projectId) return;
3056
+ _firstToolCallSignalled = true;
3057
+ void _nativeFetch(`${apiEndpoint}/v1/cli/funnel`, {
3058
+ method: "POST",
3059
+ headers: {
3060
+ "Content-Type": "application/json",
3061
+ "X-Mushi-Api-Key": apiKey,
3062
+ "X-Mushi-Project": projectId
3063
+ },
3064
+ body: JSON.stringify({ event: "mcp_first_tool_call" }),
3065
+ signal: AbortSignal.timeout(4e3)
3066
+ }).catch(() => {
3067
+ });
3068
+ };
3069
+ const llServer = server.server;
3070
+ const existingToolsCallHandler = llServer?._requestHandlers?.get("tools/call");
3071
+ if (typeof existingToolsCallHandler === "function") {
3072
+ llServer._requestHandlers.set("tools/call", async (...args) => {
3073
+ const result = await existingToolsCallHandler(...args);
3074
+ _signalFirstToolCall();
3075
+ return result;
3076
+ });
3077
+ }
3023
3078
  return server;
3024
3079
  }
3025
3080
 
package/dist/server.js CHANGED
@@ -2,13 +2,13 @@ import {
2
2
  CODEBASE_TOOL_CATALOG,
3
3
  TDD_TOOL_CATALOG,
4
4
  TOOL_CATALOG
5
- } from "./chunk-UYZEIJ5G.js";
5
+ } from "./chunk-MM3IMFM4.js";
6
6
  import {
7
7
  MUSHI_SERVER_METADATA
8
8
  } from "./chunk-EHB2BGWX.js";
9
9
  import {
10
10
  toolMatchesFeatures
11
- } from "./chunk-B2QEG35D.js";
11
+ } from "./chunk-ZX3XIJ7S.js";
12
12
 
13
13
  // src/server.ts
14
14
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
@@ -1977,6 +1977,23 @@ Be specific. No generic advice.`
1977
1977
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
1978
1978
  }
1979
1979
  );
1980
+ server.registerTool(
1981
+ "get_usage",
1982
+ {
1983
+ title: titleOf("get_usage"),
1984
+ description: descOf("get_usage"),
1985
+ annotations: annotationsFor("get_usage"),
1986
+ inputSchema: {
1987
+ project_id: z.string().optional().describe("Project ID. Defaults to the configured project.")
1988
+ }
1989
+ },
1990
+ async ({ project_id }) => {
1991
+ const pid = project_id ?? config.projectId;
1992
+ const path = pid ? `/v1/admin/billing/stats?project_id=${pid}` : "/v1/admin/billing/stats";
1993
+ const data = await apiCall(path);
1994
+ return jsonText(data);
1995
+ }
1996
+ );
1980
1997
  server.registerTool(
1981
1998
  "list_skills",
1982
1999
  {
@@ -2298,6 +2315,32 @@ Be specific. No generic advice.`
2298
2315
  }
2299
2316
  }
2300
2317
  }
2318
+ const _nativeFetch = globalThis.fetch;
2319
+ let _firstToolCallSignalled = false;
2320
+ const _signalFirstToolCall = () => {
2321
+ if (_firstToolCallSignalled || !projectId) return;
2322
+ _firstToolCallSignalled = true;
2323
+ void _nativeFetch(`${apiEndpoint}/v1/cli/funnel`, {
2324
+ method: "POST",
2325
+ headers: {
2326
+ "Content-Type": "application/json",
2327
+ "X-Mushi-Api-Key": apiKey,
2328
+ "X-Mushi-Project": projectId
2329
+ },
2330
+ body: JSON.stringify({ event: "mcp_first_tool_call" }),
2331
+ signal: AbortSignal.timeout(4e3)
2332
+ }).catch(() => {
2333
+ });
2334
+ };
2335
+ const llServer = server.server;
2336
+ const existingToolsCallHandler = llServer?._requestHandlers?.get("tools/call");
2337
+ if (typeof existingToolsCallHandler === "function") {
2338
+ llServer._requestHandlers.set("tools/call", async (...args) => {
2339
+ const result = await existingToolsCallHandler(...args);
2340
+ _signalFirstToolCall();
2341
+ return result;
2342
+ });
2343
+ }
2301
2344
  return server;
2302
2345
  }
2303
2346
  export {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@mushi-mushi/mcp",
3
3
  "mcpName": "io.github.kensaurus/mushi-mushi",
4
- "version": "0.16.1",
4
+ "version": "0.16.2",
5
5
  "license": "MIT",
6
6
  "description": "MCP server for Mushi Mushi — triage user-felt bugs, pull evidence (screenshots, console logs, network requests), check blast radius, and dispatch fix PRs, all from your AI coding agent",
7
7
  "type": "module",
@@ -42,15 +42,15 @@
42
42
  ],
43
43
  "dependencies": {
44
44
  "@modelcontextprotocol/sdk": "^1.29.0",
45
- "zod": "^4.4.2",
46
- "@mushi-mushi/core": "^1.16.0"
45
+ "zod": "^4.4.3",
46
+ "@mushi-mushi/core": "^1.20.0"
47
47
  },
48
48
  "devDependencies": {
49
- "@types/node": "^22.19.17",
50
- "eslint": "^10.3.0",
49
+ "@types/node": "^22.19.21",
50
+ "eslint": "^10.4.1",
51
51
  "tsup": "^8.5.1",
52
52
  "typescript": "^6.0.3",
53
- "vitest": "^4.1.5",
53
+ "vitest": "^4.1.8",
54
54
  "@mushi-mushi/eslint-config": "0.0.0"
55
55
  },
56
56
  "repository": {
@@ -58,8 +58,8 @@
58
58
  "url": "https://github.com/kensaurus/mushi-mushi.git",
59
59
  "directory": "packages/mcp"
60
60
  },
61
- "author": "Kenji Sakuramoto",
62
- "homepage": "https://github.com/kensaurus/mushi-mushi/tree/main/packages/mcp#readme",
61
+ "author": "Kenji Sakuramoto (https://bsky.app/profile/mushimushi.dev)",
62
+ "homepage": "https://kensaur.us/mushi-mushi",
63
63
  "bugs": {
64
64
  "url": "https://github.com/kensaurus/mushi-mushi/issues"
65
65
  },
@@ -94,6 +94,10 @@
94
94
  "engines": {
95
95
  "node": ">=20"
96
96
  },
97
+ "funding": {
98
+ "type": "github",
99
+ "url": "https://github.com/sponsors/kensaurus"
100
+ },
97
101
  "scripts": {
98
102
  "build": "tsup",
99
103
  "clean:types": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\"",