@mushi-mushi/mcp 0.4.0 → 0.6.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/CONTRIBUTING.md CHANGED
@@ -91,6 +91,33 @@ pnpm changeset
91
91
 
92
92
  Select the affected packages, the semver bump type, and write a summary. The changeset file gets committed with your PR.
93
93
 
94
+ ## Release flow
95
+
96
+ Releases are fully automated. Maintainers don't run `npm publish` by hand.
97
+
98
+ 1. PRs land on `master` with one or more changeset files in `.changeset/`.
99
+ 2. `release.yml` runs on every push to `master`. It opens (or updates) a `chore: version packages` PR that bumps every affected `package.json`, rolls up the changelogs, and deletes the consumed changesets.
100
+ 3. Merging that "Version Packages" PR re-fires `release.yml`. The publish step authenticates to npm via **OpenID Connect (OIDC) Trusted Publishers** — no long-lived `NPM_TOKEN` is exchanged — and every tarball ships with a **Sigstore provenance attestation** uploaded to the public transparency log.
101
+
102
+ If GitHub's anti-loop protection suppresses the auto re-fire (the squash merge can be attributed to `github-actions[bot]`), trigger the workflow manually: **Actions → release → Run workflow → master**.
103
+
104
+ ### Adding a brand-new publishable package
105
+
106
+ Trusted Publisher bindings are configured **per package** on `npmjs.com` and require the package to already exist on the registry. New packages therefore need a one-time bootstrap before OIDC can take over.
107
+
108
+ 1. Add the package under `packages/<name>/` with a real `version`, `files`, `publishConfig.access: "public"`, `LICENSE`, and the standard fields enforced by `pnpm check:publish-readiness`.
109
+ 2. Build it locally: `pnpm install && pnpm -r build`.
110
+ 3. Mint a short-lived granular access token at `https://www.npmjs.com/settings/<your-user>/tokens/granular-access-tokens/new` — **Bypass 2FA: ON**, **Read and write: All packages**, **Expiration: 7 days**.
111
+ 4. Bootstrap-publish:
112
+ ```bash
113
+ NPM_TOKEN=npm_xxx pnpm bootstrap:new-package
114
+ ```
115
+ The script auto-detects which workspace packages are missing on npm and publishes them via `pnpm publish --no-provenance` (so `workspace:^` specifiers get rewritten to real semver in the tarball).
116
+ 5. The script prints one URL per freshly-published package. Open each, click **GitHub Actions** under "Trusted Publisher", confirm the auto-filled fields (`<owner>` / `<repo>` / `release.yml`), and tap your security key.
117
+ 6. Revoke the bootstrap token at `https://www.npmjs.com/settings/<your-user>/tokens`.
118
+
119
+ From the next changeset bump onward, that package publishes through the normal `release.yml` flow with full OIDC provenance — same as the rest.
120
+
94
121
  ## Code Style
95
122
 
96
123
  - **TypeScript strict mode** — no `any` unless absolutely necessary
package/README.md CHANGED
@@ -1,6 +1,8 @@
1
1
  # @mushi-mushi/mcp
2
2
 
3
- [Model Context Protocol](https://spec.modelcontextprotocol.io/) server that exposes Mushi Mushi reports, fixes, and project state to coding agents (Claude Code, Cursor, Codex, Continue, Cline, Zed, Windsurf, and any other MCP-compatible client).
3
+ [Model Context Protocol](https://spec.modelcontextprotocol.io/) server that wires Mushi Mushi's closed-loop bug intelligence into your AI coding agent so Cursor, Claude Code, Copilot, and any other MCP-compatible client can read classified reports, query the lesson library, and dispatch AI-generated fixes directly from the editor.
4
+
5
+ **The evolutionary angle:** the lesson library (`lessons.query` tool) is the agent's institutional memory. Every bug cluster your team has ever named is available to the agent before it touches a single line of code — so it doesn't repeat the same class of mistake the last agent made. That's the "cumulative selection" loop the [main README](https://www.npmjs.com/package/mushi-mushi) describes, delivered to your IDE.
4
6
 
5
7
  > **What this is, and what it isn't**
6
8
  >
@@ -21,9 +23,9 @@ Add to `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS)
21
23
  "command": "npx",
22
24
  "args": ["-y", "@mushi-mushi/mcp@latest"],
23
25
  "env": {
24
- "MUSHI_API_KEY": "key_xxx",
25
- "MUSHI_PROJECT_ID": "proj_xxx",
26
- "MUSHI_API_ENDPOINT": "https://api.mushimushi.dev"
26
+ "MUSHI_API_KEY": "mushi_xxxxxxxxxxxxxxxxxxxx",
27
+ "MUSHI_PROJECT_ID": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
28
+ "MUSHI_API_ENDPOINT": "https://<your-ref>.supabase.co/functions/v1/api"
27
29
  }
28
30
  }
29
31
  }
@@ -32,6 +34,13 @@ Add to `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS)
32
34
 
33
35
  Restart Claude Desktop. You should see a hammer icon in the chat input — click it to see the Mushi Mushi tools.
34
36
 
37
+ > **Where do I find these values?**
38
+ > - **`MUSHI_API_KEY`**: [Admin console](https://kensaur.us/mushi-mushi/settings) → **Settings → API Keys**
39
+ > - **`MUSHI_PROJECT_ID`**: [Admin console](https://kensaur.us/mushi-mushi/projects) → click your project → copy the UUID below the project name (e.g. `542b34e0-019e-41fe-b900-7b637717bb86`)
40
+ > - **`MUSHI_API_ENDPOINT`**: [Admin console](https://kensaur.us/mushi-mushi/settings) → **Settings → API Keys** — shown alongside your key
41
+ >
42
+ > Or visit **Admin → MCP** in the console for a one-click pre-filled config snippet.
43
+
35
44
  ### 2. With Cursor
36
45
 
37
46
  In Cursor settings, open **MCP** → **Add new MCP server** and paste:
@@ -40,12 +49,22 @@ In Cursor settings, open **MCP** → **Add new MCP server** and paste:
40
49
  npx -y @mushi-mushi/mcp@latest
41
50
  ```
42
51
 
43
- Set the same three env vars (`MUSHI_API_KEY`, `MUSHI_PROJECT_ID`, optional `MUSHI_API_ENDPOINT`).
52
+ Then set the same three environment variables:
53
+
54
+ | Variable | Where to find it |
55
+ |---|---|
56
+ | `MUSHI_API_KEY` | Admin console → Settings → API Keys |
57
+ | `MUSHI_PROJECT_ID` | Admin console → Projects → click project → UUID below the name |
58
+ | `MUSHI_API_ENDPOINT` | Admin console → Settings → API Keys (shown alongside your key) |
59
+
60
+ Without `MUSHI_PROJECT_ID` the server starts but scoped tools return an empty result with a message pointing you here.
44
61
 
45
62
  ### 3. From the command line
46
63
 
47
64
  ```bash
48
- MUSHI_API_KEY=key_xxx MUSHI_PROJECT_ID=proj_xxx npx -y @mushi-mushi/mcp@latest
65
+ MUSHI_API_KEY=mushi_xxxxxxxxxxxxxxxxxxxx \
66
+ MUSHI_PROJECT_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx \
67
+ npx -y @mushi-mushi/mcp@latest
49
68
  ```
50
69
 
51
70
  The server speaks stdio MCP transport by default — your client launches it as a subprocess.
@@ -59,10 +78,10 @@ The Mushi backend now exposes the same tool catalog over the **Streamable HTTP**
59
78
  {
60
79
  "mcpServers": {
61
80
  "mushi-mushi-hosted": {
62
- "url": "https://api.mushimushi.dev/functions/v1/mcp",
81
+ "url": "https://<your-ref>.supabase.co/functions/v1/mcp",
63
82
  "headers": {
64
- "X-Mushi-Api-Key": "mushi_live_…",
65
- "X-Mushi-Project-Id": "proj_…"
83
+ "X-Mushi-Api-Key": "mushi_xxxxxxxxxxxxxxxxxxxx",
84
+ "X-Mushi-Project": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
66
85
  }
67
86
  }
68
87
  }
package/dist/index.js CHANGED
@@ -176,6 +176,31 @@ var TOOL_CATALOG = [
176
176
  // client prompts the user on every call.
177
177
  hints: { readOnly: false, destructive: true, idempotent: true, openWorld: true },
178
178
  useCase: "Dismiss this duplicate / mark it fixed."
179
+ },
180
+ // --- Rewards (P3) -------------------------------------------------------
181
+ {
182
+ name: "list_top_contributors",
183
+ title: "Top contributors leaderboard",
184
+ description: "Return the top N contributors for the organization, ranked by points in a time window (30d | 90d | all). Each row includes display name, tier, total points, report count, and anti-fraud flag. Use this to identify your most engaged power users, write them a thank-you message, or decide who deserves a bonus.",
185
+ scope: "mcp:read",
186
+ hints: { readOnly: true, idempotent: true, openWorld: true },
187
+ useCase: "Who are my top 10 contributors this month?"
188
+ },
189
+ {
190
+ name: "award_bonus_points",
191
+ title: "Award bonus points",
192
+ description: "Award ad-hoc bonus points to a contributor by their external user id (as passed to Mushi.identify()). Points are applied server-side, audit-logged, and trigger tier re-evaluation. Requires mcp:write scope. Use this to thank a contributor who found a critical bug or to run a one-off promotional campaign.",
193
+ scope: "mcp:write",
194
+ hints: { readOnly: false, destructive: false, idempotent: false, openWorld: true },
195
+ useCase: "Give this contributor 500 bonus points for the critical bug they found."
196
+ },
197
+ {
198
+ name: "set_tier",
199
+ title: "Override contributor tier",
200
+ description: `Override a contributor's tier by tier slug (e.g. "champion"). This is an admin escape hatch for manual promotions \u2014 normal tier transitions happen automatically via point thresholds. The override is logged in end_user_activity with action=tier_override_manual. Requires mcp:write scope.`,
201
+ scope: "mcp:write",
202
+ hints: { readOnly: false, destructive: false, idempotent: true, openWorld: true },
203
+ useCase: "Manually promote this user to Champion tier as a thank-you."
179
204
  }
180
205
  ];
181
206
 
@@ -690,6 +715,82 @@ function createMushiServer(config) {
690
715
  contents: [{ uri: "project://dashboard", mimeType: "application/json", text: JSON.stringify(await apiCall("/v1/admin/dashboard"), null, 2) }]
691
716
  })
692
717
  );
718
+ server.registerTool(
719
+ "list_top_contributors",
720
+ {
721
+ title: titleOf("list_top_contributors"),
722
+ description: descOf("list_top_contributors"),
723
+ inputSchema: {
724
+ limit: z.number().int().min(1).max(100).optional().default(10).describe("Max rows to return (default 10, max 100)"),
725
+ range: z.enum(["30d", "90d", "all"]).optional().default("30d").describe("Time window for points calculation")
726
+ },
727
+ annotations: annotationsFor("list_top_contributors")
728
+ },
729
+ async ({ limit, range }) => ({
730
+ content: [{
731
+ type: "text",
732
+ text: JSON.stringify(
733
+ await apiCall(`/v1/admin/rewards/leaderboard?range=${range}&limit=${limit}`),
734
+ null,
735
+ 2
736
+ )
737
+ }]
738
+ })
739
+ );
740
+ server.registerTool(
741
+ "award_bonus_points",
742
+ {
743
+ title: titleOf("award_bonus_points"),
744
+ description: descOf("award_bonus_points"),
745
+ inputSchema: {
746
+ external_user_id: z.string().describe("The host-app user id as passed to Mushi.identify()"),
747
+ points: z.number().int().min(1).max(5e4).describe("Bonus points to award (max 50,000 per call)"),
748
+ reason: z.string().max(200).describe("Human-readable reason, logged to end_user_activity")
749
+ },
750
+ annotations: annotationsFor("award_bonus_points")
751
+ },
752
+ async ({ external_user_id, points, reason }) => ({
753
+ content: [{
754
+ type: "text",
755
+ text: JSON.stringify(
756
+ await apiCall("/v1/admin/rewards/bonus-points", {
757
+ method: "POST",
758
+ headers: { "Content-Type": "application/json" },
759
+ body: JSON.stringify({ external_user_id, points, reason })
760
+ }),
761
+ null,
762
+ 2
763
+ )
764
+ }]
765
+ })
766
+ );
767
+ server.registerTool(
768
+ "set_tier",
769
+ {
770
+ title: titleOf("set_tier"),
771
+ description: descOf("set_tier"),
772
+ inputSchema: {
773
+ external_user_id: z.string().describe("The host-app user id as passed to Mushi.identify()"),
774
+ tier_slug: z.string().describe('Tier slug to assign, e.g. "champion", "contributor", "explorer"'),
775
+ reason: z.string().max(200).optional().describe("Optional reason for manual override")
776
+ },
777
+ annotations: annotationsFor("set_tier")
778
+ },
779
+ async ({ external_user_id, tier_slug, reason }) => ({
780
+ content: [{
781
+ type: "text",
782
+ text: JSON.stringify(
783
+ await apiCall("/v1/admin/rewards/set-tier", {
784
+ method: "POST",
785
+ headers: { "Content-Type": "application/json" },
786
+ body: JSON.stringify({ external_user_id, tier_slug, reason })
787
+ }),
788
+ null,
789
+ 2
790
+ )
791
+ }]
792
+ })
793
+ );
693
794
  server.resource(
694
795
  "project_integration_health",
695
796
  "project://integration-health",
@@ -810,6 +911,11 @@ async function main() {
810
911
  "[mushi-mcp] MUSHI_API_ENDPOINT is not set. All tool calls will fail.\nSet MUSHI_API_ENDPOINT to your Supabase edge function URL, e.g. https://xyz.supabase.co/functions/v1/api"
811
912
  );
812
913
  }
914
+ if (!PROJECT_ID) {
915
+ console.error(
916
+ "[mushi-mcp] MUSHI_PROJECT_ID is not set.\n\nTools that scope to a project (get_recent_reports, get_report_detail,\nsearch_reports, etc.) will require you to pass projectId explicitly on\nevery call. To set it once and never pass it again:\n\n 1. Open the Mushi admin console \u2192 Projects\n https://kensaur.us/mushi-mushi/projects\n 2. Click your project \u2014 copy the UUID below the project name.\n 3. Add it to your MCP env config:\n MUSHI_PROJECT_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\n\nOr visit Admin \u2192 MCP for a pre-filled config snippet with your actual UUID."
917
+ );
918
+ }
813
919
  log.info("Starting Mushi MCP server", { version: VERSION, endpoint: API_ENDPOINT || "(unset)", hasProjectId: !!PROJECT_ID });
814
920
  const server = createMushiServer({
815
921
  version: VERSION,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mushi-mushi/mcp",
3
- "version": "0.4.0",
3
+ "version": "0.6.0",
4
4
  "license": "MIT",
5
5
  "description": "MCP server exposing Mushi Mushi reports to coding agents",
6
6
  "type": "module",
@@ -24,16 +24,16 @@
24
24
  "SECURITY.md"
25
25
  ],
26
26
  "dependencies": {
27
- "@modelcontextprotocol/sdk": "^1.12.1",
28
- "zod": "^4.3.6",
29
- "@mushi-mushi/core": "^1.0.0"
27
+ "@modelcontextprotocol/sdk": "^1.29.0",
28
+ "zod": "^4.4.2",
29
+ "@mushi-mushi/core": "^1.2.0"
30
30
  },
31
31
  "devDependencies": {
32
- "@types/node": "^22.0.0",
33
- "eslint": "^10.2.0",
34
- "tsup": "^8.4.0",
35
- "typescript": "^6.0.2",
36
- "vitest": "^4.1.4",
32
+ "@types/node": "^22.19.17",
33
+ "eslint": "^10.3.0",
34
+ "tsup": "^8.5.1",
35
+ "typescript": "^6.0.3",
36
+ "vitest": "^4.1.5",
37
37
  "@mushi-mushi/eslint-config": "0.0.0"
38
38
  },
39
39
  "repository": {