@nyuchi/mzizi-mcp 0.9.1 → 0.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/README.md +117 -86
- package/dist/access.d.ts +69 -0
- package/dist/access.d.ts.map +1 -0
- package/dist/access.js +72 -0
- package/dist/access.js.map +1 -0
- package/dist/auth/authkit-handler.d.ts +4 -3
- package/dist/auth/authkit-handler.d.ts.map +1 -1
- package/dist/auth/authkit-handler.js +52 -6
- package/dist/auth/authkit-handler.js.map +1 -1
- package/dist/auth/front.d.ts +17 -0
- package/dist/auth/front.d.ts.map +1 -0
- package/dist/auth/front.js +25 -0
- package/dist/auth/front.js.map +1 -0
- package/dist/auth/mcp-api-handler.d.ts +20 -7
- package/dist/auth/mcp-api-handler.d.ts.map +1 -1
- package/dist/auth/mcp-api-handler.js +43 -29
- package/dist/auth/mcp-api-handler.js.map +1 -1
- package/dist/desk-context.d.ts +33 -0
- package/dist/desk-context.d.ts.map +1 -0
- package/dist/desk-context.js +49 -0
- package/dist/desk-context.js.map +1 -0
- package/dist/docs-federation.d.ts +68 -0
- package/dist/docs-federation.d.ts.map +1 -0
- package/dist/docs-federation.js +282 -0
- package/dist/docs-federation.js.map +1 -0
- package/dist/env.d.ts +7 -8
- package/dist/env.d.ts.map +1 -1
- package/dist/env.js +5 -5
- package/dist/fundi-a2a.d.ts +1 -1
- package/dist/fundi-a2a.js +2 -2
- package/dist/fundi-a2a.js.map +1 -1
- package/dist/fundi-client.d.ts +24 -4
- package/dist/fundi-client.d.ts.map +1 -1
- package/dist/fundi-client.js +91 -4
- package/dist/fundi-client.js.map +1 -1
- package/dist/generated/registry-pin.d.ts +5 -0
- package/dist/generated/registry-pin.d.ts.map +1 -0
- package/dist/generated/registry-pin.js +3 -0
- package/dist/generated/registry-pin.js.map +1 -0
- package/dist/generated/registry.d.ts +23 -0
- package/dist/generated/registry.d.ts.map +1 -0
- package/dist/generated/registry.js +1204 -0
- package/dist/generated/registry.js.map +1 -0
- package/dist/generated/skills.d.ts +18 -0
- package/dist/generated/skills.d.ts.map +1 -0
- package/dist/generated/skills.js +67 -0
- package/dist/generated/skills.js.map +1 -0
- package/dist/http.d.ts +18 -9
- package/dist/http.d.ts.map +1 -1
- package/dist/http.js +16 -13
- package/dist/http.js.map +1 -1
- package/dist/issue-desk.d.ts +152 -0
- package/dist/issue-desk.d.ts.map +1 -0
- package/dist/issue-desk.js +392 -0
- package/dist/issue-desk.js.map +1 -0
- package/dist/issue-log.d.ts +80 -0
- package/dist/issue-log.d.ts.map +1 -0
- package/dist/issue-log.js +146 -0
- package/dist/issue-log.js.map +1 -0
- package/dist/issue-report.d.ts +138 -0
- package/dist/issue-report.d.ts.map +1 -0
- package/dist/issue-report.js +112 -0
- package/dist/issue-report.js.map +1 -0
- package/dist/rate-limit.d.ts +41 -0
- package/dist/rate-limit.d.ts.map +1 -0
- package/dist/rate-limit.js +58 -0
- package/dist/rate-limit.js.map +1 -0
- package/dist/registry.d.ts +126 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +280 -0
- package/dist/registry.js.map +1 -0
- package/dist/server.d.ts +96 -22
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +76 -32
- package/dist/server.js.map +1 -1
- package/dist/stdio.d.ts +6 -13
- package/dist/stdio.d.ts.map +1 -1
- package/dist/stdio.js +6 -29
- package/dist/stdio.js.map +1 -1
- package/dist/tools.d.ts +45 -6
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +299 -82
- package/dist/tools.js.map +1 -1
- package/dist/worker.d.ts +26 -14
- package/dist/worker.d.ts.map +1 -1
- package/dist/worker.js +42 -14
- package/dist/worker.js.map +1 -1
- package/package.json +25 -20
- package/dist/api-client.d.ts +0 -101
- package/dist/api-client.d.ts.map +0 -1
- package/dist/api-client.js +0 -200
- package/dist/api-client.js.map +0 -1
- package/dist/defaults.d.ts +0 -29
- package/dist/defaults.d.ts.map +0 -1
- package/dist/defaults.js +0 -29
- package/dist/defaults.js.map +0 -1
|
@@ -13,9 +13,10 @@
|
|
|
13
13
|
* secret), verify the id-token against <authkit>/oauth2/jwks,
|
|
14
14
|
* then complete the MCP authorization
|
|
15
15
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
16
|
+
* Sign-in is optional: `/mcp` serves every non-Fundi tool without it (see
|
|
17
|
+
* worker.ts). A client signs in to use the Fundi tools. Every WorkOS user who
|
|
18
|
+
* signs up is let through (no org gate, no required-permission gate); any
|
|
19
|
+
* entitlement for fundi is checked by fundi, not here.
|
|
19
20
|
*
|
|
20
21
|
* This is the auth pattern every PUBLIC Mzizi MCP uses (internal MCPs use WorkOS
|
|
21
22
|
* M2M instead); it is intentionally self-contained (no WorkOS server SDK) so it
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"authkit-handler.d.ts","sourceRoot":"","sources":["../../src/auth/authkit-handler.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"authkit-handler.d.ts","sourceRoot":"","sources":["../../src/auth/authkit-handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,KAAK,EAEV,YAAY,EACb,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EAAgB,IAAI,EAAE,MAAM,MAAM,CAAC;AAE1C,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAiBrC,KAAK,OAAO,GAAG;IAAE,QAAQ,EAAE,GAAG,GAAG;QAAE,cAAc,EAAE,YAAY,CAAA;KAAE,CAAA;CAAE,CAAC;AAmVpE,eAAO,MAAM,cAAc,sDAAM,CAAC"}
|
|
@@ -13,9 +13,10 @@
|
|
|
13
13
|
* secret), verify the id-token against <authkit>/oauth2/jwks,
|
|
14
14
|
* then complete the MCP authorization
|
|
15
15
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
16
|
+
* Sign-in is optional: `/mcp` serves every non-Fundi tool without it (see
|
|
17
|
+
* worker.ts). A client signs in to use the Fundi tools. Every WorkOS user who
|
|
18
|
+
* signs up is let through (no org gate, no required-permission gate); any
|
|
19
|
+
* entitlement for fundi is checked by fundi, not here.
|
|
19
20
|
*
|
|
20
21
|
* This is the auth pattern every PUBLIC Mzizi MCP uses (internal MCPs use WorkOS
|
|
21
22
|
* M2M instead); it is intentionally self-contained (no WorkOS server SDK) so it
|
|
@@ -23,12 +24,31 @@
|
|
|
23
24
|
*/
|
|
24
25
|
import { Hono } from "hono";
|
|
25
26
|
import * as jose from "jose";
|
|
27
|
+
import { fundiConfigured } from "../fundi-client.js";
|
|
26
28
|
import { authkitDomain } from "../identity.js";
|
|
29
|
+
import { mziziToolCatalogue } from "../server.js";
|
|
27
30
|
import { addApprovedClient, bindStateToSession, createOAuthState, generateCSRFProtection, isClientApproved, OAuthError, renderApprovalDialog, validateCSRFToken, validateOAuthState, } from "./oauth-utils.js";
|
|
28
31
|
const SERVER_NAME = "Mzizi Design System Registry";
|
|
29
|
-
const SERVER_DESCRIPTION = "Free, public MCP over the Mzizi design-system registry — components, design tokens, brand,
|
|
32
|
+
const SERVER_DESCRIPTION = "Free, public MCP over the Mzizi design-system registry — components, design tokens, brand, the ecosystem architecture nodes, agent skills and the docs. No account needed; sign in (free) only to use Fundi.";
|
|
30
33
|
const ICON_REDIRECT = "https://www.nyuchi.com/icon-light.png";
|
|
31
|
-
|
|
34
|
+
/**
|
|
35
|
+
* `offline_access` is what makes the connection outlive its first access token.
|
|
36
|
+
*
|
|
37
|
+
* OIDC issues a refresh token only when this scope is asked for. Without it
|
|
38
|
+
* WorkOS returns none, `props.refreshToken` is `undefined` on every grant that
|
|
39
|
+
* has ever existed here, and the access token baked into the grant simply dies
|
|
40
|
+
* a few minutes into the session — which is why `mzizi_fundi` answered
|
|
41
|
+
* `401 ... "exp" claim timestamp check failed` on every call that was not the
|
|
42
|
+
* first.
|
|
43
|
+
*
|
|
44
|
+
* The refresh machinery in fundi-client.ts was added before this line was
|
|
45
|
+
* found, and on its own it could never have fired: it had nothing to spend.
|
|
46
|
+
* Both halves are required, and this is the half that was missing.
|
|
47
|
+
*
|
|
48
|
+
* Grants created before this scope was added carry no refresh token and cannot
|
|
49
|
+
* gain one — those connections must be re-authorised once.
|
|
50
|
+
*/
|
|
51
|
+
const OAUTH_SCOPE = "openid profile email offline_access";
|
|
32
52
|
const PKCE_TTL_SECONDS = 600;
|
|
33
53
|
const app = new Hono();
|
|
34
54
|
// ── PKCE helpers ─────────────────────────────────────────────────────────────
|
|
@@ -72,7 +92,33 @@ function redirectToIcon() {
|
|
|
72
92
|
},
|
|
73
93
|
});
|
|
74
94
|
}
|
|
75
|
-
app.get("/", (c) => c.html(`<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>${SERVER_NAME}</title><style>body{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;background:#f9fafb;color:#111;margin:0}.wrap{max-width:640px;margin:4rem auto;padding:2rem}h1{font-size:1.6rem}code{background:#eef2f0;padding:.15rem .4rem;border-radius:4px}a{color:#1f6f4f}</style></head><body><div class="wrap"><h1>${SERVER_NAME}</h1><p>${SERVER_DESCRIPTION}</p><p>This is a remote <strong>Model Context Protocol</strong> endpoint. Add it as a connector in an MCP client (e.g. claude.ai) and connect to <code>/mcp</code
|
|
95
|
+
app.get("/", (c) => c.html(`<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>${SERVER_NAME}</title><style>body{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;background:#f9fafb;color:#111;margin:0}.wrap{max-width:640px;margin:4rem auto;padding:2rem}h1{font-size:1.6rem}code{background:#eef2f0;padding:.15rem .4rem;border-radius:4px}a{color:#1f6f4f}</style></head><body><div class="wrap"><h1>${SERVER_NAME}</h1><p>${SERVER_DESCRIPTION}</p><p>This is a remote <strong>Model Context Protocol</strong> endpoint. Add it as a connector in an MCP client (e.g. claude.ai) and connect to <code>/mcp</code>: every tool works without an account except the Fundi ones (<code>mzizi_fundi</code>, <code>mzizi_report_issue</code>). To use those, connect to <code>/mcp/signin</code> instead, and you will be prompted to sign in or create a free account.</p><p>Learn more at <a href="https://mzizi.dev">mzizi.dev</a>.</p></div></body></html>`, 200, { "Cache-Control": "public, max-age=300" }));
|
|
96
|
+
/**
|
|
97
|
+
* `GET /catalogue.json` — the tool surface as plain JSON, for readers that are
|
|
98
|
+
* not MCP clients.
|
|
99
|
+
*
|
|
100
|
+
* The portal (mzizi.dev) reads its live tool count from here. It predates the
|
|
101
|
+
* free tier, when an anonymous `tools/list` on `/mcp` answered 401; it stays
|
|
102
|
+
* because a browser reading a count should not have to speak JSON-RPC.
|
|
103
|
+
*
|
|
104
|
+
* Each tool carries `access` ("free" or "sign-in") and the top-level `access`
|
|
105
|
+
* block says how to sign in (see access.ts). Computed via `mziziToolCatalogue`,
|
|
106
|
+
* the same registration path the live server uses for an anonymous caller, so
|
|
107
|
+
* it cannot report a tool this Worker does not have.
|
|
108
|
+
*/
|
|
109
|
+
app.get("/catalogue.json", async (c) => c.json(await mziziToolCatalogue({ fundi: fundiConfigured(c.env) }), 200, {
|
|
110
|
+
// Cross-origin on purpose: mzizi.dev reads this from the browser.
|
|
111
|
+
"Access-Control-Allow-Origin": "*",
|
|
112
|
+
"Cache-Control": "public, max-age=300",
|
|
113
|
+
}));
|
|
114
|
+
app.options("/catalogue.json", () => new Response(null, {
|
|
115
|
+
status: 204,
|
|
116
|
+
headers: {
|
|
117
|
+
"Access-Control-Allow-Origin": "*",
|
|
118
|
+
"Access-Control-Allow-Methods": "GET, OPTIONS",
|
|
119
|
+
"Access-Control-Allow-Headers": "Content-Type",
|
|
120
|
+
},
|
|
121
|
+
}));
|
|
76
122
|
app.get("/favicon.ico", () => redirectToIcon());
|
|
77
123
|
app.get("/icon.png", () => redirectToIcon());
|
|
78
124
|
app.get("/authorize", async (c) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"authkit-handler.js","sourceRoot":"","sources":["../../src/auth/authkit-handler.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"authkit-handler.js","sourceRoot":"","sources":["../../src/auth/authkit-handler.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which front serves a request: the free tier, or the OAuth provider.
|
|
3
|
+
*
|
|
4
|
+
* Its own module, not `worker.ts`: the Worker's entry module may export only
|
|
5
|
+
* handlers and Durable Object classes, and this is tested directly.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* True for a request the free tier serves: the MCP path, no credential at all.
|
|
9
|
+
*
|
|
10
|
+
* ANY `Authorization` header sends the request to the provider, not only a
|
|
11
|
+
* well-formed `Bearer`. A malformed credential is a client that meant to sign
|
|
12
|
+
* in, and it should hear the provider's 401, not be quietly served anonymously.
|
|
13
|
+
* OPTIONS stays with the provider too, which already answers CORS preflight on
|
|
14
|
+
* its API route.
|
|
15
|
+
*/
|
|
16
|
+
export declare function isAnonymousMcpRequest(request: Request): boolean;
|
|
17
|
+
//# sourceMappingURL=front.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"front.d.ts","sourceRoot":"","sources":["../../src/auth/front.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAI/D"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which front serves a request: the free tier, or the OAuth provider.
|
|
3
|
+
*
|
|
4
|
+
* Its own module, not `worker.ts`: the Worker's entry module may export only
|
|
5
|
+
* handlers and Durable Object classes, and this is tested directly.
|
|
6
|
+
*/
|
|
7
|
+
/** The paths served to a caller with no token. `/mcp/signin` is deliberately not one. */
|
|
8
|
+
const ANONYMOUS_PATHS = new Set(["/mcp", "/mcp/"]);
|
|
9
|
+
/**
|
|
10
|
+
* True for a request the free tier serves: the MCP path, no credential at all.
|
|
11
|
+
*
|
|
12
|
+
* ANY `Authorization` header sends the request to the provider, not only a
|
|
13
|
+
* well-formed `Bearer`. A malformed credential is a client that meant to sign
|
|
14
|
+
* in, and it should hear the provider's 401, not be quietly served anonymously.
|
|
15
|
+
* OPTIONS stays with the provider too, which already answers CORS preflight on
|
|
16
|
+
* its API route.
|
|
17
|
+
*/
|
|
18
|
+
export function isAnonymousMcpRequest(request) {
|
|
19
|
+
if (request.method === "OPTIONS")
|
|
20
|
+
return false;
|
|
21
|
+
if (request.headers.has("Authorization"))
|
|
22
|
+
return false;
|
|
23
|
+
return ANONYMOUS_PATHS.has(new URL(request.url).pathname);
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=front.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"front.js","sourceRoot":"","sources":["../../src/auth/front.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,yFAAyF;AACzF,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AAEnD;;;;;;;;GAQG;AACH,MAAM,UAAU,qBAAqB,CAAC,OAAgB;IACpD,IAAI,OAAO,CAAC,MAAM,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IAC/C,IAAI,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC;QAAE,OAAO,KAAK,CAAC;IACvD,OAAO,eAAe,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC;AAC5D,CAAC"}
|
|
@@ -1,19 +1,32 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The MCP API handler
|
|
2
|
+
* The MCP API handler, for both fronts in `worker.ts`.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
4
|
+
* signed in the OAuth provider validated a WorkOS-backed access token and
|
|
5
|
+
* put the identity on `ctx.props`; every tool works, the Fundi
|
|
6
|
+
* tools included.
|
|
7
|
+
* anonymous the free tier: no token, so no `ctx.props`. Every tool works
|
|
8
|
+
* except the Fundi ones, which are listed and refuse with how to
|
|
9
|
+
* sign in (see `access.ts`).
|
|
10
|
+
*
|
|
11
|
+
* The handler itself decides nothing about auth: it reads whether `ctx.props`
|
|
12
|
+
* carries a user. The front is what guarantees that `ctx.props` is only ever
|
|
13
|
+
* set from a token the provider validated.
|
|
9
14
|
*/
|
|
10
15
|
import type { Env } from "../env.js";
|
|
11
16
|
import type { Props } from "./props.js";
|
|
12
17
|
type AuthedContext = ExecutionContext & {
|
|
13
18
|
props?: Props;
|
|
14
19
|
};
|
|
20
|
+
/**
|
|
21
|
+
* The free tier: no user. `fundi` says whether to LIST mzizi_fundi (so its
|
|
22
|
+
* refusal can say how to sign in); it wires no caller and no desk.
|
|
23
|
+
*
|
|
24
|
+
* Takes no `ctx` on purpose, so the anonymous front in `worker.ts` cannot pick
|
|
25
|
+
* up a `ctx.props` the provider did not set.
|
|
26
|
+
*/
|
|
27
|
+
export declare function serveAnonymous(request: Request, env: Env): Promise<Response>;
|
|
15
28
|
export declare const mcpApiHandler: {
|
|
16
|
-
fetch(request: Request, env: Env,
|
|
29
|
+
fetch(request: Request, env: Env, ctx: AuthedContext): Promise<Response>;
|
|
17
30
|
};
|
|
18
31
|
export {};
|
|
19
32
|
//# sourceMappingURL=mcp-api-handler.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-api-handler.d.ts","sourceRoot":"","sources":["../../src/auth/mcp-api-handler.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"mcp-api-handler.d.ts","sourceRoot":"","sources":["../../src/auth/mcp-api-handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAIH,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,WAAW,CAAA;AACpC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AAGvC,KAAK,aAAa,GAAG,gBAAgB,GAAG;IAAE,KAAK,CAAC,EAAE,KAAK,CAAA;CAAE,CAAA;AAEzD;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,QAAQ,CAAC,CAE5E;AAED,eAAO,MAAM,aAAa;mBACH,OAAO,OAAO,GAAG,OAAO,aAAa,GAAG,OAAO,CAAC,QAAQ,CAAC;CA4B/E,CAAA"}
|
|
@@ -1,39 +1,53 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The MCP API handler
|
|
2
|
+
* The MCP API handler, for both fronts in `worker.ts`.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
4
|
+
* signed in the OAuth provider validated a WorkOS-backed access token and
|
|
5
|
+
* put the identity on `ctx.props`; every tool works, the Fundi
|
|
6
|
+
* tools included.
|
|
7
|
+
* anonymous the free tier: no token, so no `ctx.props`. Every tool works
|
|
8
|
+
* except the Fundi ones, which are listed and refuse with how to
|
|
9
|
+
* sign in (see `access.ts`).
|
|
10
|
+
*
|
|
11
|
+
* The handler itself decides nothing about auth: it reads whether `ctx.props`
|
|
12
|
+
* carries a user. The front is what guarantees that `ctx.props` is only ever
|
|
13
|
+
* set from a token the provider validated.
|
|
9
14
|
*/
|
|
10
15
|
import { createMziziHttpHandler } from "../http.js";
|
|
11
|
-
import { createFundiCaller, fundiConfigured } from "../fundi-client.js";
|
|
16
|
+
import { createFundiCaller, createUserTokenResolver, fundiConfigured } from "../fundi-client.js";
|
|
17
|
+
import { deskFor } from "../desk-context.js";
|
|
18
|
+
/**
|
|
19
|
+
* The free tier: no user. `fundi` says whether to LIST mzizi_fundi (so its
|
|
20
|
+
* refusal can say how to sign in); it wires no caller and no desk.
|
|
21
|
+
*
|
|
22
|
+
* Takes no `ctx` on purpose, so the anonymous front in `worker.ts` cannot pick
|
|
23
|
+
* up a `ctx.props` the provider did not set.
|
|
24
|
+
*/
|
|
25
|
+
export function serveAnonymous(request, env) {
|
|
26
|
+
return createMziziHttpHandler({ anonymous: { fundi: fundiConfigured(env) } })(request);
|
|
27
|
+
}
|
|
12
28
|
export const mcpApiHandler = {
|
|
13
|
-
async fetch(request, env,
|
|
29
|
+
async fetch(request, env, ctx) {
|
|
30
|
+
const props = ctx.props;
|
|
31
|
+
// A validated token whose grant carries no subject is not a user.
|
|
32
|
+
if (!props?.userId)
|
|
33
|
+
return serveAnonymous(request, env);
|
|
14
34
|
// fundi is M2M and only runs through a sanctioned tool: when this Worker
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
35
|
+
// can reach fundi and the caller is an authenticated user, expose the fundi
|
|
36
|
+
// trigger bound to that user's token.
|
|
37
|
+
// Gate on the IDENTITY, not on the freshness of the token it arrived with.
|
|
38
|
+
// Gating on `props.accessToken` being currently valid would make the fundi
|
|
39
|
+
// tools vanish from the surface the moment the grant aged — the exact
|
|
40
|
+
// disappearing-capability failure `fundiConfigured` documents. The resolver
|
|
41
|
+
// below is what deals with age.
|
|
42
|
+
const fundi = fundiConfigured(env)
|
|
43
|
+
? { caller: createFundiCaller(env), resolveUserToken: createUserTokenResolver(env, props) }
|
|
20
44
|
: undefined;
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
url: env.SUPABASE_URL,
|
|
28
|
-
publishableKeys: { default: env.SUPABASE_PUBLISHABLE_KEY },
|
|
29
|
-
// See worker history: `@supabase/server` eagerly builds `supabaseAdmin`
|
|
30
|
-
// even when `auth: "none"`, so pass the publishable key as a harmless
|
|
31
|
-
// placeholder unless a real service-role key is configured.
|
|
32
|
-
secretKeys: { default: env.SUPABASE_SECRET_KEY ?? env.SUPABASE_PUBLISHABLE_KEY },
|
|
33
|
-
},
|
|
34
|
-
},
|
|
35
|
-
});
|
|
36
|
-
return handler(request);
|
|
45
|
+
// The desk is keyed by the WorkOS subject, so it IS the user's desk — there
|
|
46
|
+
// is no filtering to get wrong and no way to read someone else's reports.
|
|
47
|
+
// `idFromName` is deterministic, so the same person reaches the same object
|
|
48
|
+
// across sessions, devices and years; that is what makes the history real.
|
|
49
|
+
const desk = deskFor(env.ISSUE_DESK.get(env.ISSUE_DESK.idFromName(props.userId)), props, fundi?.resolveUserToken);
|
|
50
|
+
return createMziziHttpHandler({ fundi, desk })(request);
|
|
37
51
|
},
|
|
38
52
|
};
|
|
39
53
|
//# sourceMappingURL=mcp-api-handler.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-api-handler.js","sourceRoot":"","sources":["../../src/auth/mcp-api-handler.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"mcp-api-handler.js","sourceRoot":"","sources":["../../src/auth/mcp-api-handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAA;AACnD,OAAO,EAAE,iBAAiB,EAAE,uBAAuB,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAA;AAGhG,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAA;AAI5C;;;;;;GAMG;AACH,MAAM,UAAU,cAAc,CAAC,OAAgB,EAAE,GAAQ;IACvD,OAAO,sBAAsB,CAAC,EAAE,SAAS,EAAE,EAAE,KAAK,EAAE,eAAe,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,CAAA;AACxF,CAAC;AAED,MAAM,CAAC,MAAM,aAAa,GAAG;IAC3B,KAAK,CAAC,KAAK,CAAC,OAAgB,EAAE,GAAQ,EAAE,GAAkB;QACxD,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAA;QACvB,kEAAkE;QAClE,IAAI,CAAC,KAAK,EAAE,MAAM;YAAE,OAAO,cAAc,CAAC,OAAO,EAAE,GAAG,CAAC,CAAA;QAEvD,yEAAyE;QACzE,4EAA4E;QAC5E,sCAAsC;QACtC,2EAA2E;QAC3E,2EAA2E;QAC3E,sEAAsE;QACtE,4EAA4E;QAC5E,gCAAgC;QAChC,MAAM,KAAK,GAAG,eAAe,CAAC,GAAG,CAAC;YAChC,CAAC,CAAC,EAAE,MAAM,EAAE,iBAAiB,CAAC,GAAG,CAAC,EAAE,gBAAgB,EAAE,uBAAuB,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE;YAC3F,CAAC,CAAC,SAAS,CAAA;QACb,4EAA4E;QAC5E,0EAA0E;QAC1E,4EAA4E;QAC5E,2EAA2E;QAC3E,MAAM,IAAI,GAAG,OAAO,CAClB,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,UAAU,CAAC,UAAU,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,EAC3D,KAAK,EACL,KAAK,EAAE,gBAAgB,CACxB,CAAA;QAED,OAAO,sBAAsB,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,CAAA;IACzD,CAAC;CACF,CAAA"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The bridge between the Durable Object and the tool layer.
|
|
3
|
+
*
|
|
4
|
+
* Extracted into its own module for one reason: it can be tested without a
|
|
5
|
+
* workerd runtime. The bug it fixes shipped precisely because nothing in the
|
|
6
|
+
* suite exercised this seam — `test/issue-desk.test.ts` drives the DO's pure
|
|
7
|
+
* helpers directly and passes an actor by hand, so it could never see the
|
|
8
|
+
* wiring that failed to pass one.
|
|
9
|
+
*/
|
|
10
|
+
import type { IssueDesk } from "./issue-desk.js";
|
|
11
|
+
import type { IssueDeskContext } from "./tools.js";
|
|
12
|
+
import type { Props } from "./auth/props.js";
|
|
13
|
+
/**
|
|
14
|
+
* Adapt the raw Durable Object stub to the shape the tool layer calls.
|
|
15
|
+
*
|
|
16
|
+
* The tool layer must not take an actor from its caller — the reporter's
|
|
17
|
+
* identity comes from the validated OAuth grant, never from tool arguments, or
|
|
18
|
+
* anyone could file as anyone. So `IssueDeskContext.open(input)` takes one
|
|
19
|
+
* argument while `IssueDesk.open(input, caller)` takes two, and something has
|
|
20
|
+
* to bridge them.
|
|
21
|
+
*
|
|
22
|
+
* That something used to be `as unknown as IssueDeskContext`. A double
|
|
23
|
+
* assertion silences the arity difference instead of bridging it, so every
|
|
24
|
+
* report reached the draft stage with `caller === undefined` and threw on
|
|
25
|
+
* `caller.actor.email`. `list` and `get` kept working because they take no
|
|
26
|
+
* caller — which is exactly the shape of the bug reported in
|
|
27
|
+
* mzizi-dev/mzizi#313.
|
|
28
|
+
*
|
|
29
|
+
* This function is the bridge. It closes over the grant, so the actor cannot be
|
|
30
|
+
* spoofed and cannot be forgotten.
|
|
31
|
+
*/
|
|
32
|
+
export declare function deskFor(stub: Pick<IssueDesk, "open" | "retry" | "get" | "getRefreshed" | "list">, props: Pick<Props, "userId" | "email">, resolveUserToken?: () => Promise<string>): IssueDeskContext;
|
|
33
|
+
//# sourceMappingURL=desk-context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"desk-context.d.ts","sourceRoot":"","sources":["../src/desk-context.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAc,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC7D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACnD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AAE7C;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,OAAO,CACrB,IAAI,EAAE,IAAI,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,GAAG,KAAK,GAAG,cAAc,GAAG,MAAM,CAAC,EACzE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO,CAAC,EACtC,gBAAgB,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,GACvC,gBAAgB,CAmBlB"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The bridge between the Durable Object and the tool layer.
|
|
3
|
+
*
|
|
4
|
+
* Extracted into its own module for one reason: it can be tested without a
|
|
5
|
+
* workerd runtime. The bug it fixes shipped precisely because nothing in the
|
|
6
|
+
* suite exercised this seam — `test/issue-desk.test.ts` drives the DO's pure
|
|
7
|
+
* helpers directly and passes an actor by hand, so it could never see the
|
|
8
|
+
* wiring that failed to pass one.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Adapt the raw Durable Object stub to the shape the tool layer calls.
|
|
12
|
+
*
|
|
13
|
+
* The tool layer must not take an actor from its caller — the reporter's
|
|
14
|
+
* identity comes from the validated OAuth grant, never from tool arguments, or
|
|
15
|
+
* anyone could file as anyone. So `IssueDeskContext.open(input)` takes one
|
|
16
|
+
* argument while `IssueDesk.open(input, caller)` takes two, and something has
|
|
17
|
+
* to bridge them.
|
|
18
|
+
*
|
|
19
|
+
* That something used to be `as unknown as IssueDeskContext`. A double
|
|
20
|
+
* assertion silences the arity difference instead of bridging it, so every
|
|
21
|
+
* report reached the draft stage with `caller === undefined` and threw on
|
|
22
|
+
* `caller.actor.email`. `list` and `get` kept working because they take no
|
|
23
|
+
* caller — which is exactly the shape of the bug reported in
|
|
24
|
+
* mzizi-dev/mzizi#313.
|
|
25
|
+
*
|
|
26
|
+
* This function is the bridge. It closes over the grant, so the actor cannot be
|
|
27
|
+
* spoofed and cannot be forgotten.
|
|
28
|
+
*/
|
|
29
|
+
export function deskFor(stub, props, resolveUserToken) {
|
|
30
|
+
const caller = async () => ({
|
|
31
|
+
actor: { userId: props.userId, email: props.email },
|
|
32
|
+
// Resolved per call and immediately forwarded, so it cannot go stale in
|
|
33
|
+
// flight. A failure here is deliberately swallowed: an expired connection
|
|
34
|
+
// should cost the reproduction, not the report. The desk records the
|
|
35
|
+
// absence and files anyway.
|
|
36
|
+
userToken: await resolveUserToken?.().catch(() => undefined),
|
|
37
|
+
});
|
|
38
|
+
return {
|
|
39
|
+
open: async (input) => stub.open(input, await caller()),
|
|
40
|
+
retry: async (id) => stub.retry(id, await caller()),
|
|
41
|
+
// `getRefreshed`, not `get`: reading one report asks fundi where its issue
|
|
42
|
+
// got to, which is the only way a reporter sees "filed", "in progress" or
|
|
43
|
+
// "closed" without leaving the conversation. It needs a caller for the same
|
|
44
|
+
// reason `open` does — fundi acts for a user or not at all.
|
|
45
|
+
get: async (id) => stub.getRefreshed(id, await caller()),
|
|
46
|
+
list: (limit) => stub.list(limit),
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=desk-context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"desk-context.js","sourceRoot":"","sources":["../src/desk-context.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAMH;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,OAAO,CACrB,IAAyE,EACzE,KAAsC,EACtC,gBAAwC;IAExC,MAAM,MAAM,GAAG,KAAK,IAAyB,EAAE,CAAC,CAAC;QAC/C,KAAK,EAAE,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE;QACnD,wEAAwE;QACxE,0EAA0E;QAC1E,qEAAqE;QACrE,4BAA4B;QAC5B,SAAS,EAAE,MAAM,gBAAgB,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC;KAC7D,CAAC,CAAC;IACH,OAAO;QACL,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,MAAM,MAAM,EAAE,CAAC;QACvD,KAAK,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,EAAE,MAAM,MAAM,EAAE,CAAC;QACnD,2EAA2E;QAC3E,0EAA0E;QAC1E,4EAA4E;QAC5E,4DAA4D;QAC5D,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,EAAE,EAAE,MAAM,MAAM,EAAE,CAAC;QACxD,IAAI,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC;KAClC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Docs, joined in from the Mintlify MCP at docs.mzizi.dev.
|
|
3
|
+
*
|
|
4
|
+
* The documentation site runs its own MCP server (streamable HTTP, public,
|
|
5
|
+
* stateless — `initialize` answers serverInfo "Mzizi"). It is what drives the
|
|
6
|
+
* docs, so this server does not keep a second copy of them: it lists that
|
|
7
|
+
* server's tools under a `docs_` prefix and forwards each call, passing the
|
|
8
|
+
* result through untouched.
|
|
9
|
+
*
|
|
10
|
+
* tools/list → upstream tools/list, cached for DOCS_TOOLS_TTL_MS
|
|
11
|
+
* tools/call → upstream tools/call with the un-prefixed name
|
|
12
|
+
*
|
|
13
|
+
* FAILURE POLICY.
|
|
14
|
+
* - A failed call is an MCP tool error naming the docs server and the reason,
|
|
15
|
+
* never an exception: an agent should learn "docs are unreachable", not see
|
|
16
|
+
* a transport fault.
|
|
17
|
+
* - A failed LIST falls back to the last good list (however old), and with no
|
|
18
|
+
* list at all to FALLBACK_TOOLS — a snapshot of the upstream definitions —
|
|
19
|
+
* so the tool surface does not change shape while docs are down. Calls then
|
|
20
|
+
* fail with the clear error above until the upstream is back.
|
|
21
|
+
*/
|
|
22
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
23
|
+
export declare const DOCS_MCP_URL = "https://docs.mzizi.dev/mcp";
|
|
24
|
+
export declare const DOCS_TOOL_PREFIX = "docs_";
|
|
25
|
+
/** How long a fetched tool list is reused before asking upstream again. */
|
|
26
|
+
export declare const DOCS_TOOLS_TTL_MS: number;
|
|
27
|
+
/**
|
|
28
|
+
* After a failed list, how long to answer from the stale list or snapshot
|
|
29
|
+
* without asking upstream again. Every request builds a fresh server, so
|
|
30
|
+
* without this a down or slow docs server would add a full LIST_TIMEOUT_MS to
|
|
31
|
+
* every request — including mzizi_* calls that never touch docs.
|
|
32
|
+
*/
|
|
33
|
+
export declare const DOCS_FAILURE_BACKOFF_MS = 60000;
|
|
34
|
+
export type UpstreamTool = {
|
|
35
|
+
name: string;
|
|
36
|
+
title?: string;
|
|
37
|
+
description?: string;
|
|
38
|
+
inputSchema?: Record<string, unknown>;
|
|
39
|
+
annotations?: Record<string, unknown>;
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* The upstream definitions as of 2026-09-28, used only when docs.mzizi.dev
|
|
43
|
+
* cannot be reached and nothing has been cached yet.
|
|
44
|
+
*/
|
|
45
|
+
export declare const FALLBACK_TOOLS: readonly UpstreamTool[];
|
|
46
|
+
export type DocsFederationOptions = {
|
|
47
|
+
url?: string;
|
|
48
|
+
fetchImpl?: typeof fetch;
|
|
49
|
+
/** Clock, injectable for cache tests. */
|
|
50
|
+
now?: () => number;
|
|
51
|
+
};
|
|
52
|
+
/** For tests: forget every cached tool list and failure. */
|
|
53
|
+
export declare function clearDocsToolCache(): void;
|
|
54
|
+
/**
|
|
55
|
+
* The upstream tool list: cached, then fresh, then stale, then the snapshot.
|
|
56
|
+
* Never throws — the federated surface always has a shape.
|
|
57
|
+
*/
|
|
58
|
+
export declare function docsTools(opts?: DocsFederationOptions): Promise<{
|
|
59
|
+
tools: UpstreamTool[];
|
|
60
|
+
source: "cache" | "upstream" | "stale-cache" | "fallback";
|
|
61
|
+
error?: string;
|
|
62
|
+
}>;
|
|
63
|
+
/**
|
|
64
|
+
* Register every docs tool on `server` as `docs_<upstream name>`.
|
|
65
|
+
* Returns the bare (un-prefixed-by-mzizi) names that were registered.
|
|
66
|
+
*/
|
|
67
|
+
export declare function registerDocsTools(server: McpServer, opts?: DocsFederationOptions): Promise<string[]>;
|
|
68
|
+
//# sourceMappingURL=docs-federation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"docs-federation.d.ts","sourceRoot":"","sources":["../src/docs-federation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAGH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAGzE,eAAO,MAAM,YAAY,+BAA+B,CAAC;AACzD,eAAO,MAAM,gBAAgB,UAAU,CAAC;AACxC,2EAA2E;AAC3E,eAAO,MAAM,iBAAiB,QAAa,CAAC;AAC5C;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,QAAS,CAAC;AAI9C,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtC,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACvC,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,cAAc,EAAE,SAAS,YAAY,EAwEjD,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,yCAAyC;IACzC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB,CAAC;AA+EF,4DAA4D;AAC5D,wBAAgB,kBAAkB,IAAI,IAAI,CAGzC;AAsBD;;;GAGG;AACH,wBAAsB,SAAS,CAAC,IAAI,GAAE,qBAA0B,GAAG,OAAO,CAAC;IACzE,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB,MAAM,EAAE,OAAO,GAAG,UAAU,GAAG,aAAa,GAAG,UAAU,CAAC;IAC1D,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC,CAkCD;AAcD;;;GAGG;AACH,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,SAAS,EACjB,IAAI,GAAE,qBAA0B,GAC/B,OAAO,CAAC,MAAM,EAAE,CAAC,CA6CnB"}
|