@mushi-mushi/mcp 0.4.0 → 0.5.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 +27 -0
- package/dist/index.js +106 -0
- package/package.json +9 -9
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/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://your-admin-url/projects)\n 2. Find your project \u2014 the UUID chip below the name is the value to use.\n 3. Copy it and set:\n MUSHI_PROJECT_ID=<paste-uuid-here>\n\nYou can also visit Admin \u2192 MCP for a pre-filled .env.local snippet."
|
|
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.
|
|
3
|
+
"version": "0.5.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.
|
|
28
|
-
"zod": "^4.
|
|
29
|
-
"@mushi-mushi/core": "^1.
|
|
27
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
28
|
+
"zod": "^4.4.2",
|
|
29
|
+
"@mushi-mushi/core": "^1.1.0"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
|
-
"@types/node": "^22.
|
|
33
|
-
"eslint": "^10.
|
|
34
|
-
"tsup": "^8.
|
|
35
|
-
"typescript": "^6.0.
|
|
36
|
-
"vitest": "^4.1.
|
|
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": {
|