@nyuchi/mzizi-mcp 0.3.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.
@@ -0,0 +1,295 @@
1
+ // oauth-utils.ts
2
+ // Vendored from Cloudflare's `remote-mcp-authkit` demo (Apache-2.0) — the same
3
+ // helpers `nyuchi/mongodb-mcp` uses. OAuth helpers with CSRF protection and
4
+ // session-bound state validation.
5
+ export class OAuthError extends Error {
6
+ code;
7
+ description;
8
+ statusCode;
9
+ constructor(code, description, statusCode = 400) {
10
+ super(description);
11
+ this.code = code;
12
+ this.description = description;
13
+ this.statusCode = statusCode;
14
+ this.name = "OAuthError";
15
+ }
16
+ toResponse() {
17
+ return new Response(JSON.stringify({ error: this.code, error_description: this.description }), {
18
+ status: this.statusCode,
19
+ headers: { "Content-Type": "application/json" },
20
+ });
21
+ }
22
+ }
23
+ export function sanitizeText(text) {
24
+ return text
25
+ .replace(/&/g, "&")
26
+ .replace(/</g, "&lt;")
27
+ .replace(/>/g, "&gt;")
28
+ .replace(/"/g, "&quot;")
29
+ .replace(/'/g, "&#039;");
30
+ }
31
+ export function sanitizeUrl(url) {
32
+ const normalized = url.trim();
33
+ if (normalized.length === 0)
34
+ return "";
35
+ for (let i = 0; i < normalized.length; i++) {
36
+ const code = normalized.charCodeAt(i);
37
+ if ((code >= 0x00 && code <= 0x1f) || (code >= 0x7f && code <= 0x9f))
38
+ return "";
39
+ }
40
+ let parsedUrl;
41
+ try {
42
+ parsedUrl = new URL(normalized);
43
+ }
44
+ catch {
45
+ return "";
46
+ }
47
+ const allowedSchemes = ["https", "http"];
48
+ const scheme = parsedUrl.protocol.slice(0, -1).toLowerCase();
49
+ if (!allowedSchemes.includes(scheme))
50
+ return "";
51
+ return normalized;
52
+ }
53
+ export function generateCSRFProtection() {
54
+ const csrfCookieName = "__Host-CSRF_TOKEN";
55
+ const token = crypto.randomUUID();
56
+ const setCookie = `${csrfCookieName}=${token}; HttpOnly; Secure; Path=/; SameSite=Lax; Max-Age=600`;
57
+ return { token, setCookie };
58
+ }
59
+ export function validateCSRFToken(formData, request) {
60
+ const csrfCookieName = "__Host-CSRF_TOKEN";
61
+ const tokenFromForm = formData.get("csrf_token");
62
+ if (!tokenFromForm || typeof tokenFromForm !== "string") {
63
+ throw new OAuthError("invalid_request", "Missing CSRF token in form data", 400);
64
+ }
65
+ const cookieHeader = request.headers.get("Cookie") || "";
66
+ const cookies = cookieHeader.split(";").map((c) => c.trim());
67
+ const csrfCookie = cookies.find((c) => c.startsWith(`${csrfCookieName}=`));
68
+ const tokenFromCookie = csrfCookie ? csrfCookie.substring(csrfCookieName.length + 1) : null;
69
+ if (!tokenFromCookie) {
70
+ throw new OAuthError("invalid_request", "Missing CSRF token cookie", 400);
71
+ }
72
+ if (tokenFromForm !== tokenFromCookie) {
73
+ throw new OAuthError("invalid_request", "CSRF token mismatch", 400);
74
+ }
75
+ const clearCookie = `${csrfCookieName}=; HttpOnly; Secure; Path=/; SameSite=Lax; Max-Age=0`;
76
+ return { clearCookie };
77
+ }
78
+ export async function createOAuthState(oauthReqInfo, kv, stateTTL = 600) {
79
+ const stateToken = crypto.randomUUID();
80
+ await kv.put(`oauth:state:${stateToken}`, JSON.stringify(oauthReqInfo), {
81
+ expirationTtl: stateTTL,
82
+ });
83
+ return { stateToken };
84
+ }
85
+ export async function bindStateToSession(stateToken) {
86
+ const consentedStateCookieName = "__Host-CONSENTED_STATE";
87
+ const encoder = new TextEncoder();
88
+ const data = encoder.encode(stateToken);
89
+ const hashBuffer = await crypto.subtle.digest("SHA-256", data);
90
+ const hashArray = Array.from(new Uint8Array(hashBuffer));
91
+ const hashHex = hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
92
+ const setCookie = `${consentedStateCookieName}=${hashHex}; HttpOnly; Secure; Path=/; SameSite=Lax; Max-Age=600`;
93
+ return { setCookie };
94
+ }
95
+ export async function validateOAuthState(request, kv) {
96
+ const consentedStateCookieName = "__Host-CONSENTED_STATE";
97
+ const url = new URL(request.url);
98
+ const stateFromQuery = url.searchParams.get("state");
99
+ if (!stateFromQuery) {
100
+ throw new OAuthError("invalid_request", "Missing state parameter", 400);
101
+ }
102
+ const storedDataJson = await kv.get(`oauth:state:${stateFromQuery}`);
103
+ if (!storedDataJson) {
104
+ throw new OAuthError("invalid_request", "Invalid or expired state", 400);
105
+ }
106
+ const cookieHeader = request.headers.get("Cookie") || "";
107
+ const cookies = cookieHeader.split(";").map((c) => c.trim());
108
+ const consentedStateCookie = cookies.find((c) => c.startsWith(`${consentedStateCookieName}=`));
109
+ const consentedStateHash = consentedStateCookie
110
+ ? consentedStateCookie.substring(consentedStateCookieName.length + 1)
111
+ : null;
112
+ if (!consentedStateHash) {
113
+ throw new OAuthError("invalid_request", "Missing session binding cookie - authorization flow must be restarted", 400);
114
+ }
115
+ const encoder = new TextEncoder();
116
+ const data = encoder.encode(stateFromQuery);
117
+ const hashBuffer = await crypto.subtle.digest("SHA-256", data);
118
+ const hashArray = Array.from(new Uint8Array(hashBuffer));
119
+ const stateHash = hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
120
+ if (stateHash !== consentedStateHash) {
121
+ throw new OAuthError("invalid_request", "State token does not match session - possible CSRF attack detected", 400);
122
+ }
123
+ let oauthReqInfo;
124
+ try {
125
+ oauthReqInfo = JSON.parse(storedDataJson);
126
+ }
127
+ catch {
128
+ throw new OAuthError("server_error", "Invalid state data", 500);
129
+ }
130
+ await kv.delete(`oauth:state:${stateFromQuery}`);
131
+ const clearCookie = `${consentedStateCookieName}=; HttpOnly; Secure; Path=/; SameSite=Lax; Max-Age=0`;
132
+ return { oauthReqInfo, clearCookie };
133
+ }
134
+ export async function isClientApproved(request, clientId, cookieSecret) {
135
+ const approvedClients = await getApprovedClientsFromCookie(request, cookieSecret);
136
+ return approvedClients?.includes(clientId) ?? false;
137
+ }
138
+ export async function addApprovedClient(request, clientId, cookieSecret) {
139
+ const approvedClientsCookieName = "__Host-APPROVED_CLIENTS";
140
+ const THIRTY_DAYS_IN_SECONDS = 2592000;
141
+ const existingApprovedClients = (await getApprovedClientsFromCookie(request, cookieSecret)) || [];
142
+ const updatedApprovedClients = Array.from(new Set([...existingApprovedClients, clientId]));
143
+ const payload = JSON.stringify(updatedApprovedClients);
144
+ const signature = await signData(payload, cookieSecret);
145
+ const cookieValue = `${signature}.${btoa(payload)}`;
146
+ return `${approvedClientsCookieName}=${cookieValue}; HttpOnly; Secure; Path=/; SameSite=Lax; Max-Age=${THIRTY_DAYS_IN_SECONDS}`;
147
+ }
148
+ export function renderApprovalDialog(request, options) {
149
+ const { client, server, state, csrfToken, setCookie } = options;
150
+ const encodedState = btoa(JSON.stringify(state));
151
+ const serverName = sanitizeText(server.name);
152
+ const clientName = client?.clientName ? sanitizeText(client.clientName) : "Unknown MCP Client";
153
+ const serverDescription = server.description ? sanitizeText(server.description) : "";
154
+ const logoUrl = server.logo ? sanitizeText(sanitizeUrl(server.logo)) : "";
155
+ const clientUri = client?.clientUri ? sanitizeText(sanitizeUrl(client.clientUri)) : "";
156
+ const policyUri = client?.policyUri ? sanitizeText(sanitizeUrl(client.policyUri)) : "";
157
+ const tosUri = client?.tosUri ? sanitizeText(sanitizeUrl(client.tosUri)) : "";
158
+ const contacts = client?.contacts && client.contacts.length > 0 ? sanitizeText(client.contacts.join(", ")) : "";
159
+ const redirectUris = client?.redirectUris && client.redirectUris.length > 0
160
+ ? client.redirectUris
161
+ .map((uri) => {
162
+ const validated = sanitizeUrl(uri);
163
+ return validated ? sanitizeText(validated) : "";
164
+ })
165
+ .filter((uri) => uri !== "")
166
+ : [];
167
+ const htmlContent = `<!DOCTYPE html>
168
+ <html lang="en">
169
+ <head>
170
+ <meta charset="UTF-8">
171
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
172
+ <title>${clientName} | Authorization Request</title>
173
+ <style>
174
+ :root { --primary-color: #1f6f4f; --border-color: #e5e7eb; --text-color: #111; }
175
+ body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
176
+ line-height: 1.6; color: var(--text-color); background: #f9fafb; margin: 0; }
177
+ .container { max-width: 600px; margin: 2rem auto; padding: 1rem; }
178
+ .precard { padding: 2rem; text-align: center; }
179
+ .card { background: #fff; border-radius: 8px; box-shadow: 0 8px 36px 8px rgba(0,0,0,0.08); padding: 2rem; }
180
+ .header { display: flex; align-items: center; justify-content: center; margin-bottom: 1.5rem; }
181
+ .logo { width: 48px; height: 48px; margin-right: 1rem; border-radius: 8px; }
182
+ .title { margin: 0; font-size: 1.3rem; font-weight: 500; }
183
+ .alert { font-size: 1.4rem; font-weight: 400; margin: 1rem 0; text-align: center; }
184
+ .client-info { border: 1px solid var(--border-color); border-radius: 6px; padding: 1rem 1rem 0.5rem; margin-bottom: 1.5rem; }
185
+ .client-detail { display: flex; margin-bottom: 0.5rem; align-items: baseline; }
186
+ .detail-label { font-weight: 500; min-width: 120px; }
187
+ .detail-value { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; word-break: break-all; }
188
+ .detail-value.small { font-size: 0.85em; }
189
+ .actions { display: flex; justify-content: flex-end; gap: 1rem; margin-top: 2rem; }
190
+ .button { padding: 0.75rem 1.5rem; border-radius: 6px; font-weight: 500; cursor: pointer; border: none; font-size: 1rem; }
191
+ .button-primary { background: var(--primary-color); color: #fff; }
192
+ .button-secondary { background: transparent; border: 1px solid var(--border-color); color: var(--text-color); }
193
+ </style>
194
+ </head>
195
+ <body>
196
+ <div class="container">
197
+ <div class="precard">
198
+ <div class="header">
199
+ ${logoUrl ? `<img src="${logoUrl}" alt="${serverName} Logo" class="logo">` : ""}
200
+ <h1 class="title"><strong>${serverName}</strong></h1>
201
+ </div>
202
+ ${serverDescription ? `<p>${serverDescription}</p>` : ""}
203
+ </div>
204
+ <div class="card">
205
+ <h2 class="alert"><strong>${clientName}</strong> is requesting access</h2>
206
+ <div class="client-info">
207
+ <div class="client-detail">
208
+ <div class="detail-label">Name:</div>
209
+ <div class="detail-value">${clientName}</div>
210
+ </div>
211
+ ${clientUri ? `<div class="client-detail"><div class="detail-label">Website:</div><div class="detail-value small"><a href="${clientUri}" target="_blank" rel="noopener noreferrer">${clientUri}</a></div></div>` : ""}
212
+ ${policyUri ? `<div class="client-detail"><div class="detail-label">Privacy Policy:</div><div class="detail-value"><a href="${policyUri}" target="_blank" rel="noopener noreferrer">${policyUri}</a></div></div>` : ""}
213
+ ${tosUri ? `<div class="client-detail"><div class="detail-label">Terms of Service:</div><div class="detail-value"><a href="${tosUri}" target="_blank" rel="noopener noreferrer">${tosUri}</a></div></div>` : ""}
214
+ ${redirectUris.length > 0 ? `<div class="client-detail"><div class="detail-label">Redirect URIs:</div><div class="detail-value small">${redirectUris.map((u) => `<div>${u}</div>`).join("")}</div></div>` : ""}
215
+ ${contacts ? `<div class="client-detail"><div class="detail-label">Contact:</div><div class="detail-value">${contacts}</div></div>` : ""}
216
+ </div>
217
+ <p>This MCP Client is requesting access to the Mzizi design-system registry through <strong>${serverName}</strong>. mzizi-mcp is a free, public tool — if you approve, you will be redirected to sign in or create a free account.</p>
218
+ <form method="post" action="${new URL(request.url).pathname}">
219
+ <input type="hidden" name="state" value="${encodedState}">
220
+ <input type="hidden" name="csrf_token" value="${csrfToken}">
221
+ <div class="actions">
222
+ <button type="button" class="button button-secondary" onclick="window.history.back()">Cancel</button>
223
+ <button type="submit" class="button button-primary">Approve</button>
224
+ </div>
225
+ </form>
226
+ </div>
227
+ </div>
228
+ </body>
229
+ </html>`;
230
+ return new Response(htmlContent, {
231
+ headers: {
232
+ "Content-Security-Policy": "frame-ancestors 'none'",
233
+ "Content-Type": "text/html; charset=utf-8",
234
+ "Set-Cookie": setCookie,
235
+ "X-Frame-Options": "DENY",
236
+ },
237
+ });
238
+ }
239
+ async function getApprovedClientsFromCookie(request, cookieSecret) {
240
+ const approvedClientsCookieName = "__Host-APPROVED_CLIENTS";
241
+ const cookieHeader = request.headers.get("Cookie");
242
+ if (!cookieHeader)
243
+ return null;
244
+ const cookies = cookieHeader.split(";").map((c) => c.trim());
245
+ const targetCookie = cookies.find((c) => c.startsWith(`${approvedClientsCookieName}=`));
246
+ if (!targetCookie)
247
+ return null;
248
+ const cookieValue = targetCookie.substring(approvedClientsCookieName.length + 1);
249
+ const parts = cookieValue.split(".");
250
+ const signatureHex = parts[0];
251
+ const base64Payload = parts[1];
252
+ if (parts.length !== 2 || !signatureHex || !base64Payload)
253
+ return null;
254
+ const payload = atob(base64Payload);
255
+ const isValid = await verifySignature(signatureHex, payload, cookieSecret);
256
+ if (!isValid)
257
+ return null;
258
+ try {
259
+ const approvedClients = JSON.parse(payload);
260
+ if (!Array.isArray(approvedClients) ||
261
+ !approvedClients.every((item) => typeof item === "string")) {
262
+ return null;
263
+ }
264
+ return approvedClients;
265
+ }
266
+ catch {
267
+ return null;
268
+ }
269
+ }
270
+ async function signData(data, secret) {
271
+ const key = await importKey(secret);
272
+ const enc = new TextEncoder();
273
+ const signatureBuffer = await crypto.subtle.sign("HMAC", key, enc.encode(data));
274
+ return Array.from(new Uint8Array(signatureBuffer))
275
+ .map((b) => b.toString(16).padStart(2, "0"))
276
+ .join("");
277
+ }
278
+ async function verifySignature(signatureHex, data, secret) {
279
+ const key = await importKey(secret);
280
+ const enc = new TextEncoder();
281
+ try {
282
+ const signatureBytes = new Uint8Array(signatureHex.match(/.{1,2}/g).map((byte) => Number.parseInt(byte, 16)));
283
+ return await crypto.subtle.verify("HMAC", key, signatureBytes.buffer, enc.encode(data));
284
+ }
285
+ catch {
286
+ return false;
287
+ }
288
+ }
289
+ async function importKey(secret) {
290
+ if (!secret)
291
+ throw new Error("cookieSecret is required for signing cookies");
292
+ const enc = new TextEncoder();
293
+ return crypto.subtle.importKey("raw", enc.encode(secret), { hash: "SHA-256", name: "HMAC" }, false, ["sign", "verify"]);
294
+ }
295
+ //# sourceMappingURL=oauth-utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth-utils.js","sourceRoot":"","sources":["../../src/auth/oauth-utils.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ import type { User } from "@workos-inc/node";
2
+ /**
3
+ * Identity carried on the access token the OAuth provider issues to an MCP
4
+ * client. Populated in the AuthKit `/callback` and made available to the MCP
5
+ * API handler as `ctx.props`.
6
+ *
7
+ * mzizi-mcp does not gate on `organizationId` or `permissions` — they are kept
8
+ * only so downstream tooling (fundi, gated collections) can read them later.
9
+ */
10
+ export interface Props {
11
+ user: User;
12
+ accessToken: string;
13
+ refreshToken: string;
14
+ permissions: string[];
15
+ organizationId?: string;
16
+ [key: string]: unknown;
17
+ }
18
+ //# sourceMappingURL=props.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"props.d.ts","sourceRoot":"","sources":["../../src/auth/props.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAA;AAE5C;;;;;;;GAOG;AACH,MAAM,WAAW,KAAK;IACpB,IAAI,EAAE,IAAI,CAAA;IACV,WAAW,EAAE,MAAM,CAAA;IACnB,YAAY,EAAE,MAAM,CAAA;IACpB,WAAW,EAAE,MAAM,EAAE,CAAA;IACrB,cAAc,CAAC,EAAE,MAAM,CAAA;IAGvB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CACvB"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=props.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"props.js","sourceRoot":"","sources":["../../src/auth/props.ts"],"names":[],"mappings":""}
package/dist/env.d.ts ADDED
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Worker environment bindings — shared across the OAuth provider, the WorkOS
3
+ * AuthKit handler, and the MCP API handler.
4
+ *
5
+ * mzizi-mcp is a free, public tool: anyone can sign up through WorkOS AuthKit
6
+ * and use it. There is deliberately NO org allowlist and NO required-permission
7
+ * gate (that is what `nyuchi/mongodb-mcp` does, because it is internal). This
8
+ * auth layer is the reusable foundation the rest of the Mzizi tooling (fundi
9
+ * and friends) can sit behind later.
10
+ */
11
+ import type { OAuthHelpers } from "@cloudflare/workers-oauth-provider";
12
+ export interface Env {
13
+ SUPABASE_URL: string;
14
+ SUPABASE_PUBLISHABLE_KEY: string;
15
+ SUPABASE_SECRET_KEY?: string;
16
+ WORKOS_CLIENT_ID: string;
17
+ WORKOS_CLIENT_SECRET: string;
18
+ COOKIE_ENCRYPTION_KEY: string;
19
+ OAUTH_KV: KVNamespace;
20
+ OAUTH_PROVIDER: OAuthHelpers;
21
+ }
22
+ //# sourceMappingURL=env.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../src/env.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAA;AAEtE,MAAM,WAAW,GAAG;IAElB,YAAY,EAAE,MAAM,CAAA;IACpB,wBAAwB,EAAE,MAAM,CAAA;IAChC,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAG5B,gBAAgB,EAAE,MAAM,CAAA;IACxB,oBAAoB,EAAE,MAAM,CAAA;IAG5B,qBAAqB,EAAE,MAAM,CAAA;IAG7B,QAAQ,EAAE,WAAW,CAAA;IACrB,cAAc,EAAE,YAAY,CAAA;CAC7B"}
package/dist/env.js ADDED
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Worker environment bindings — shared across the OAuth provider, the WorkOS
3
+ * AuthKit handler, and the MCP API handler.
4
+ *
5
+ * mzizi-mcp is a free, public tool: anyone can sign up through WorkOS AuthKit
6
+ * and use it. There is deliberately NO org allowlist and NO required-permission
7
+ * gate (that is what `nyuchi/mongodb-mcp` does, because it is internal). This
8
+ * auth layer is the reusable foundation the rest of the Mzizi tooling (fundi
9
+ * and friends) can sit behind later.
10
+ */
11
+ export {};
12
+ //# sourceMappingURL=env.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env.js","sourceRoot":"","sources":["../src/env.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG"}
package/dist/http.d.ts ADDED
@@ -0,0 +1,22 @@
1
+ /**
2
+ * HTTP / fetch-handler entry — Cloudflare Workers + Next.js routes + any
3
+ * Web-standard Request/Response runtime.
4
+ *
5
+ * Uses `@supabase/server`'s `createSupabaseContext(request, { auth: 'none' })`
6
+ * to mint a per-request anon-scoped `SupabaseClient`. RLS on
7
+ * `component_documents` enforces read-only.
8
+ */
9
+ import { type WithSupabaseConfig } from "@supabase/server";
10
+ export interface HttpHandlerOptions {
11
+ /**
12
+ * Override the default `@supabase/server` config. Defaults to
13
+ * `{ auth: 'none', cors: false }` — public read via the anon client.
14
+ */
15
+ supabaseConfig?: WithSupabaseConfig;
16
+ }
17
+ /**
18
+ * Build a fetch handler that serves the Mzizi MCP over Streamable HTTP.
19
+ * Stateless — each request creates a fresh transport + server pair.
20
+ */
21
+ export declare function createMziziHttpHandler(options?: HttpHandlerOptions): (request: Request) => Promise<Response>;
22
+ //# sourceMappingURL=http.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EAAyB,KAAK,kBAAkB,EAAE,MAAM,kBAAkB,CAAA;AAoCjF,MAAM,WAAW,kBAAkB;IACjC;;;OAGG;IACH,cAAc,CAAC,EAAE,kBAAkB,CAAA;CACpC;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,GAAE,kBAAuB,IAGxC,SAAS,OAAO,KAAG,OAAO,CAAC,QAAQ,CAAC,CA8BlE"}
package/dist/http.js ADDED
@@ -0,0 +1,72 @@
1
+ /**
2
+ * HTTP / fetch-handler entry — Cloudflare Workers + Next.js routes + any
3
+ * Web-standard Request/Response runtime.
4
+ *
5
+ * Uses `@supabase/server`'s `createSupabaseContext(request, { auth: 'none' })`
6
+ * to mint a per-request anon-scoped `SupabaseClient`. RLS on
7
+ * `component_documents` enforces read-only.
8
+ */
9
+ import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";
10
+ import { createSupabaseContext } from "@supabase/server";
11
+ import { createMziziMcpServer } from "./server.js";
12
+ const CORS_HEADERS = {
13
+ "Access-Control-Allow-Origin": "*",
14
+ "Access-Control-Allow-Methods": "GET, POST, DELETE, OPTIONS",
15
+ "Access-Control-Allow-Headers": "Content-Type, MCP-Protocol-Version, MCP-Session-Id, apikey",
16
+ };
17
+ function withCors(response) {
18
+ const headers = new Headers(response.headers);
19
+ for (const [key, value] of Object.entries(CORS_HEADERS))
20
+ headers.set(key, value);
21
+ return new Response(response.body, {
22
+ status: response.status,
23
+ statusText: response.statusText,
24
+ headers,
25
+ });
26
+ }
27
+ // OAuth / OIDC discovery paths an MCP client probes to learn whether — and
28
+ // how — it must authenticate (RFC 9728 / RFC 8414 + the MCP Authorization
29
+ // spec). This server is public (`auth: "none"`); it advertises no auth
30
+ // scheme. RFC 9728 §3.1 also allows the resource identifier to carry a path,
31
+ // so the protected-resource probe can arrive with a trailing path segment.
32
+ const OAUTH_DISCOVERY_PREFIXES = [
33
+ "/.well-known/oauth-protected-resource",
34
+ "/.well-known/oauth-authorization-server",
35
+ "/.well-known/openid-configuration",
36
+ ];
37
+ function isOAuthDiscoveryPath(pathname) {
38
+ return OAUTH_DISCOVERY_PREFIXES.some((prefix) => pathname === prefix || pathname.startsWith(`${prefix}/`));
39
+ }
40
+ /**
41
+ * Build a fetch handler that serves the Mzizi MCP over Streamable HTTP.
42
+ * Stateless — each request creates a fresh transport + server pair.
43
+ */
44
+ export function createMziziHttpHandler(options = {}) {
45
+ const config = options.supabaseConfig ?? { auth: "none", cors: false };
46
+ return async function handle(request) {
47
+ if (request.method === "OPTIONS") {
48
+ return new Response(null, { status: 204, headers: CORS_HEADERS });
49
+ }
50
+ // Answer auth-discovery probes with 404 so compliant MCP clients fall back
51
+ // to anonymous access. Without this the request reaches the Streamable HTTP
52
+ // transport, which rejects a discovery GET (no `text/event-stream` Accept)
53
+ // with a 406 — a response clients can't read as auth metadata, so they
54
+ // abort the connection instead of connecting unauthenticated.
55
+ if (isOAuthDiscoveryPath(new URL(request.url).pathname)) {
56
+ return withCors(new Response("Not Found", { status: 404 }));
57
+ }
58
+ const { data: ctx, error } = await createSupabaseContext(request, config);
59
+ if (error || !ctx) {
60
+ return withCors(Response.json({ error: error?.message ?? "Failed to create Supabase context" }, { status: error?.status ?? 500 }));
61
+ }
62
+ const transport = new WebStandardStreamableHTTPServerTransport({
63
+ sessionIdGenerator: undefined,
64
+ enableJsonResponse: true,
65
+ });
66
+ const server = await createMziziMcpServer(ctx.supabase);
67
+ await server.connect(transport);
68
+ const response = await transport.handleRequest(request);
69
+ return withCors(response);
70
+ };
71
+ }
72
+ //# sourceMappingURL=http.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Mzizi MCP — registry-driven server factory.
3
+ *
4
+ * The tool surface is NOT hardcoded. It is read at startup from the public
5
+ * `mcp_tool_registry` table (the same registry that powers the design system's
6
+ * MCP), and each tool is dispatched dynamically to its backing implementation:
7
+ *
8
+ * • `sql_function` → `supabase.rpc(fn, args)` (the common case)
9
+ * • `edge_function` → `supabase.functions.invoke(fn, { body: args })`
10
+ * • `source_table` → `supabase.from(table).select()` (fallback)
11
+ *
12
+ * This makes mzizi-mcp a drop-in replacement for the legacy design MCP: adding,
13
+ * renaming, or retiring a tool is a registry edit, not a code change. Argument
14
+ * names in the registry's `input_schema` (and in the curated fallback schemas
15
+ * below) match the SQL function parameters exactly, so arguments pass straight
16
+ * through to `rpc()` with no remapping.
17
+ *
18
+ * The factory takes a pre-built `SupabaseClient` so the same factory works
19
+ * across transports (HTTP per-request anon client; stdio long-lived anon
20
+ * client). RLS grants public read on `mcp_tool_registry`, `component_documents`,
21
+ * and EXECUTE on the document/registry RPCs.
22
+ */
23
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
24
+ import type { SupabaseClient } from "@supabase/supabase-js";
25
+ export declare const MZIZI_MCP_VERSION = "0.3.0";
26
+ /**
27
+ * Build the Mzizi MCP server bound to a Supabase client with public read on
28
+ * `mcp_tool_registry` / `component_documents` and EXECUTE on the backing RPCs.
29
+ * The tool catalog is loaded from the registry; write-kind tools are excluded
30
+ * from this (anonymous, public) surface.
31
+ */
32
+ export declare function createMziziMcpServer(supabase: SupabaseClient): Promise<McpServer>;
33
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAMnE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAE3D,eAAO,MAAM,iBAAiB,UAAU,CAAA;AAiGxC;;;;;GAKG;AACH,wBAAsB,oBAAoB,CAAC,QAAQ,EAAE,cAAc,GAAG,OAAO,CAAC,SAAS,CAAC,CAqLvF"}