@productbrain/mcp 0.0.1-beta.129 → 0.0.1-beta.134
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/dist/{chunk-2ZWGQ6PK.js → chunk-MOPOQUJP.js} +35 -1
- package/dist/chunk-MOPOQUJP.js.map +1 -0
- package/dist/{chunk-73AECI6K.js → chunk-UUUJXF2W.js} +94 -43
- package/dist/chunk-UUUJXF2W.js.map +1 -0
- package/dist/cli/index.js +1 -1
- package/dist/http.js +20 -4
- package/dist/http.js.map +1 -1
- package/dist/index.js +2 -2
- package/dist/{setup-2JWIZ3QO.js → setup-YYADLH22.js} +2 -2
- package/dist/views/src/graph-constellation/index.html +1 -1
- package/package.json +1 -1
- package/dist/chunk-2ZWGQ6PK.js.map +0 -1
- package/dist/chunk-73AECI6K.js.map +0 -1
- /package/dist/{setup-2JWIZ3QO.js.map → setup-YYADLH22.js.map} +0 -0
package/dist/cli/index.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// src/cli/index.ts
|
|
4
4
|
var subcommand = process.argv[2];
|
|
5
5
|
if (subcommand === "setup") {
|
|
6
|
-
const { runSetup } = await import("../setup-
|
|
6
|
+
const { runSetup } = await import("../setup-YYADLH22.js");
|
|
7
7
|
await runSetup();
|
|
8
8
|
} else {
|
|
9
9
|
await import("../index.js");
|
package/dist/http.js
CHANGED
|
@@ -4,12 +4,12 @@ import {
|
|
|
4
4
|
createProductBrainServer,
|
|
5
5
|
initFeatureFlags,
|
|
6
6
|
runWithAuth
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-UUUJXF2W.js";
|
|
8
8
|
import {
|
|
9
9
|
getPostHogClient,
|
|
10
10
|
initAnalytics,
|
|
11
11
|
shutdownAnalytics
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-MOPOQUJP.js";
|
|
13
13
|
|
|
14
14
|
// src/http.ts
|
|
15
15
|
import { createHash, randomUUID } from "crypto";
|
|
@@ -124,7 +124,7 @@ function esc(s) {
|
|
|
124
124
|
);
|
|
125
125
|
}
|
|
126
126
|
app.get("/authorize", (req, res) => {
|
|
127
|
-
const { redirect_uri, code_challenge, code_challenge_method, state } = req.query;
|
|
127
|
+
const { redirect_uri, code_challenge, code_challenge_method, state, client_id } = req.query;
|
|
128
128
|
res.type("html").send(`<!DOCTYPE html>
|
|
129
129
|
<html lang="en"><head>
|
|
130
130
|
<meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
|
|
@@ -153,6 +153,7 @@ button:hover{background:#6d28d9}
|
|
|
153
153
|
<input type="hidden" name="code_challenge" value="${esc(code_challenge)}">
|
|
154
154
|
<input type="hidden" name="code_challenge_method" value="${esc(code_challenge_method)}">
|
|
155
155
|
<input type="hidden" name="state" value="${esc(state)}">
|
|
156
|
+
<input type="hidden" name="client_id" value="${esc(client_id)}">
|
|
156
157
|
<label for="k">API Key</label>
|
|
157
158
|
<input type="password" id="k" name="api_key" placeholder="pb_sk_\u2026" required autofocus>
|
|
158
159
|
<p class="err" id="e">Key must start with pb_sk_</p>
|
|
@@ -168,11 +169,26 @@ app.post(
|
|
|
168
169
|
"/authorize",
|
|
169
170
|
express.urlencoded({ extended: false }),
|
|
170
171
|
(req, res) => {
|
|
171
|
-
const { api_key, redirect_uri, code_challenge, state } = req.body;
|
|
172
|
+
const { api_key, redirect_uri, code_challenge, state, client_id } = req.body;
|
|
172
173
|
if (!api_key?.startsWith("pb_sk_")) {
|
|
173
174
|
res.status(400).send("Invalid API key");
|
|
174
175
|
return;
|
|
175
176
|
}
|
|
177
|
+
if (!client_id || !registeredClients.has(client_id)) {
|
|
178
|
+
res.status(400).json({
|
|
179
|
+
error: "invalid_request",
|
|
180
|
+
error_description: "Unknown or missing client_id"
|
|
181
|
+
});
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
const client = registeredClients.get(client_id);
|
|
185
|
+
if (!client.redirect_uris.includes(redirect_uri)) {
|
|
186
|
+
res.status(400).json({
|
|
187
|
+
error: "invalid_request",
|
|
188
|
+
error_description: "redirect_uri does not match any registered redirect for this client"
|
|
189
|
+
});
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
176
192
|
const code = randomUUID();
|
|
177
193
|
pendingCodes.set(code, {
|
|
178
194
|
apiKey: api_key,
|
package/dist/http.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/http.ts"],"sourcesContent":["/**\n * HTTP transport entry point for Product Brain MCP.\n *\n * Serves the MCP protocol over Streamable HTTP for web clients\n * (Claude web app, API consumers) that can't spawn local processes.\n *\n * Implements the full MCP OAuth 2.1 spec (Nov 2025):\n * 1. Protected Resource Metadata (/.well-known/oauth-protected-resource)\n * 2. Authorization Server Metadata (/.well-known/oauth-authorization-server)\n * 3. Dynamic Client Registration (POST /register)\n * 4. Authorization Code + PKCE (GET/POST /authorize)\n * 5. Token Exchange (POST /oauth/token)\n *\n * Env:\n * CONVEX_SITE_URL — Convex deployment URL (defaults to cloud)\n * PORT / MCP_PORT — Listen port (default 3000)\n * CORS_ORIGINS — Comma-separated allowed origins (default: all)\n * PB_MODULES — Comma-separated modules (default: core,gitchain,arch)\n */\n\nimport { createHash, randomUUID } from \"node:crypto\";\nimport express from \"express\";\nimport { StreamableHTTPServerTransport } from \"@modelcontextprotocol/sdk/server/streamableHttp.js\";\nimport { isInitializeRequest } from \"@modelcontextprotocol/sdk/types.js\";\nimport rateLimit from \"express-rate-limit\";\n\nimport { bootstrapHttp } from \"./client.js\";\nimport { runWithAuth } from \"./auth.js\";\nimport { createProductBrainServer, SERVER_VERSION } from \"./server.js\";\nimport { initAnalytics, shutdownAnalytics, getPostHogClient } from \"./analytics.js\";\nimport { initFeatureFlags } from \"./featureFlags.js\";\n\n// ── Bootstrap ───────────────────────────────────────────────────────────\n\nbootstrapHttp();\ninitAnalytics();\ninitFeatureFlags(getPostHogClient());\n\nconst PORT = parseInt(process.env.PORT ?? process.env.MCP_PORT ?? \"3002\", 10);\n\nfunction baseUrl(req: any): string {\n const proto = req.headers[\"x-forwarded-proto\"] ?? req.protocol ?? \"http\";\n const host = req.headers.host ?? `localhost:${PORT}`;\n return `${proto}://${host}`;\n}\n\n// ── Express App ─────────────────────────────────────────────────────────\n\nconst app = express();\n// Required when behind a reverse proxy (e.g. Railway): rate limiter uses X-Forwarded-For\n// and throws ERR_ERL_UNEXPECTED_X_FORWARDED_FOR if trust proxy is false.\napp.set(\"trust proxy\", 1);\napp.use(express.json());\n\n// CORS — fail-closed; requires CORS_ORIGINS to be explicitly configured.\nconst ALLOWED_ORIGINS = process.env.CORS_ORIGINS\n ?.split(\",\")\n .map((o) => o.trim())\n .filter(Boolean);\n\napp.use((_req: any, res: any, next: any) => {\n const origin = _req.headers.origin;\n if (ALLOWED_ORIGINS && origin && ALLOWED_ORIGINS.includes(origin)) {\n res.setHeader(\"Access-Control-Allow-Origin\", origin);\n }\n res.setHeader(\"Access-Control-Allow-Methods\", \"GET, POST, DELETE, OPTIONS\");\n res.setHeader(\n \"Access-Control-Allow-Headers\",\n \"Content-Type, Authorization, Mcp-Session-Id, Last-Event-Id\",\n );\n res.setHeader(\"Access-Control-Expose-Headers\", \"Mcp-Session-Id\");\n if (_req.method === \"OPTIONS\") {\n res.status(204).end();\n return;\n }\n next();\n});\n\n// ── OAuth: Protected Resource Metadata (RFC 9728) ────────────────────────\n// Step 1 of MCP auth: Claude fetches this to discover the authorization server.\n\napp.get(\"/.well-known/oauth-protected-resource\", (req: any, res: any) => {\n const base = baseUrl(req);\n res.json({\n resource: base,\n authorization_servers: [base],\n scopes_supported: [\"mcp:tools\", \"mcp:resources\"],\n bearer_methods_supported: [\"header\"],\n });\n});\n\n// ── OAuth: Authorization Server Metadata (RFC 8414) ──────────────────────\n// Step 2: Claude fetches this to discover authorize, token, and register endpoints.\n\napp.get(\"/.well-known/oauth-authorization-server\", (req: any, res: any) => {\n const base = baseUrl(req);\n res.json({\n issuer: base,\n authorization_endpoint: `${base}/authorize`,\n token_endpoint: `${base}/oauth/token`,\n registration_endpoint: `${base}/register`,\n response_types_supported: [\"code\"],\n grant_types_supported: [\"authorization_code\", \"refresh_token\"],\n code_challenge_methods_supported: [\"S256\"],\n token_endpoint_auth_methods_supported: [\"none\"],\n scopes_supported: [\"mcp:tools\", \"mcp:resources\"],\n });\n});\n\n// ── OAuth: Dynamic Client Registration (RFC 7591) ────────────────────────\n// Step 3: Claude registers itself as a client before starting the auth flow.\n\ninterface RegisteredClient {\n client_id: string;\n redirect_uris: string[];\n client_name?: string;\n registeredAt: number;\n}\n\nconst registeredClients = new Map<string, RegisteredClient>();\n\napp.post(\n \"/register\",\n express.json(),\n (req: any, res: any) => {\n const { redirect_uris, client_name } = req.body;\n\n if (!Array.isArray(redirect_uris) || redirect_uris.length === 0) {\n res.status(400).json({\n error: \"invalid_client_metadata\",\n error_description: \"redirect_uris is required\",\n });\n return;\n }\n\n const clientId = `pb_client_${randomUUID()}`;\n const client: RegisteredClient = {\n client_id: clientId,\n redirect_uris,\n client_name,\n registeredAt: Date.now(),\n };\n registeredClients.set(clientId, client);\n\n res.status(201).json({\n client_id: clientId,\n client_name: client_name ?? \"MCP Client\",\n redirect_uris,\n grant_types: [\"authorization_code\"],\n response_types: [\"code\"],\n token_endpoint_auth_method: \"none\",\n });\n },\n);\n\n// ── OAuth: Authorization Code + PKCE ─────────────────────────────────────\n// Step 4: User enters their pb_sk_* key, server generates a one-time code.\n\ninterface PendingAuth {\n apiKey: string;\n codeChallenge: string;\n redirectUri: string;\n expiresAt: number;\n}\n\nconst pendingCodes = new Map<string, PendingAuth>();\n\n// Refresh token store — declared here so the cleanup interval can reference it.\nconst ACCESS_TOKEN_TTL = 3600; // 1 hour\nconst REFRESH_TOKEN_TTL_MS = 90 * 24 * 60 * 60_000; // 90 days\n\ninterface RefreshEntry {\n apiKey: string;\n createdAt: number;\n}\n\nconst refreshTokens = new Map<string, RefreshEntry>();\n\nsetInterval(() => {\n const now = Date.now();\n for (const [code, auth] of pendingCodes) {\n if (now > auth.expiresAt) pendingCodes.delete(code);\n }\n for (const [id, client] of registeredClients) {\n if (now - client.registeredAt > 24 * 60 * 60_000) registeredClients.delete(id);\n }\n for (const [token, entry] of refreshTokens) {\n if (now - entry.createdAt > REFRESH_TOKEN_TTL_MS) refreshTokens.delete(token);\n }\n}, 60_000);\n\nfunction esc(s: unknown): string {\n return String(s ?? \"\").replace(/[&\"<>]/g, (c) =>\n ({ \"&\": \"&\", '\"': \""\", \"<\": \"<\", \">\": \">\" })[c]!,\n );\n}\n\napp.get(\"/authorize\", (req: any, res: any) => {\n const { redirect_uri, code_challenge, code_challenge_method, state } =\n req.query;\n res.type(\"html\").send(`<!DOCTYPE html>\n<html lang=\"en\"><head>\n<meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n<title>Authorize — Product Brain</title>\n<style>\n*{margin:0;padding:0;box-sizing:border-box}\nbody{font-family:-apple-system,system-ui,sans-serif;background:#0a0a0a;color:#e5e5e5;\n display:flex;align-items:center;justify-content:center;min-height:100vh;padding:1rem}\n.card{background:#1a1a1a;border:1px solid #333;border-radius:12px;padding:2rem;max-width:400px;width:100%}\nh1{font-size:1.2rem;margin-bottom:.25rem}\n.sub{color:#999;font-size:.85rem;margin-bottom:1.5rem}\nlabel{display:block;font-size:.85rem;margin-bottom:.4rem;color:#ccc}\ninput[type=password]{width:100%;padding:.6rem .75rem;background:#111;border:1px solid #444;\n border-radius:8px;color:#e5e5e5;font:.85rem/1.4 monospace}\ninput:focus{outline:none;border-color:#7c3aed}\nbutton{width:100%;padding:.6rem;background:#7c3aed;color:#fff;border:none;\n border-radius:8px;font-size:.85rem;cursor:pointer;margin-top:1rem}\nbutton:hover{background:#6d28d9}\n.err{color:#ef4444;font-size:.8rem;margin-top:.5rem;display:none}\n</style></head><body>\n<div class=\"card\">\n<h1>Product Brain</h1>\n<p class=\"sub\">Enter your API key to connect Claude to your workspace.</p>\n<form method=\"POST\" action=\"/authorize\">\n <input type=\"hidden\" name=\"redirect_uri\" value=\"${esc(redirect_uri)}\">\n <input type=\"hidden\" name=\"code_challenge\" value=\"${esc(code_challenge)}\">\n <input type=\"hidden\" name=\"code_challenge_method\" value=\"${esc(code_challenge_method)}\">\n <input type=\"hidden\" name=\"state\" value=\"${esc(state)}\">\n <label for=\"k\">API Key</label>\n <input type=\"password\" id=\"k\" name=\"api_key\" placeholder=\"pb_sk_…\" required autofocus>\n <p class=\"err\" id=\"e\">Key must start with pb_sk_</p>\n <button type=\"submit\">Authorize</button>\n</form>\n</div>\n<script>document.querySelector(\"form\").onsubmit=function(e){\nif(!document.getElementById(\"k\").value.startsWith(\"pb_sk_\")){\ne.preventDefault();document.getElementById(\"e\").style.display=\"block\"}}</script>\n</body></html>`);\n});\n\napp.post(\n \"/authorize\",\n express.urlencoded({ extended: false }),\n (req: any, res: any) => {\n const { api_key, redirect_uri, code_challenge, state } = req.body;\n\n if (!api_key?.startsWith(\"pb_sk_\")) {\n res.status(400).send(\"Invalid API key\");\n return;\n }\n\n const code = randomUUID();\n pendingCodes.set(code, {\n apiKey: api_key,\n codeChallenge: code_challenge,\n redirectUri: redirect_uri,\n expiresAt: Date.now() + 5 * 60_000,\n });\n\n const url = new URL(redirect_uri);\n url.searchParams.set(\"code\", code);\n if (state) url.searchParams.set(\"state\", state);\n res.redirect(302, url.toString());\n },\n);\n\n// ── OAuth: Token Exchange ────────────────────────────────────────────────\n// Step 5: Claude exchanges the authorization code (with PKCE verifier) for a token.\n// Supports both authorization_code and refresh_token grants.\n\nfunction issueTokens(apiKey: string): object {\n const refreshToken = `pb_rt_${randomUUID()}`;\n refreshTokens.set(refreshToken, { apiKey, createdAt: Date.now() });\n return {\n access_token: apiKey,\n token_type: \"Bearer\",\n expires_in: ACCESS_TOKEN_TTL,\n refresh_token: refreshToken,\n };\n}\n\napp.post(\n \"/oauth/token\",\n express.urlencoded({ extended: false }),\n express.json(),\n (req: any, res: any) => {\n const { grant_type, code, code_verifier, redirect_uri, refresh_token } =\n req.body;\n\n if (grant_type === \"refresh_token\") {\n const entry = refreshTokens.get(refresh_token);\n if (!entry) {\n res.status(400).json({ error: \"invalid_grant\", error_description: \"Invalid refresh token\" });\n return;\n }\n if (Date.now() - entry.createdAt > REFRESH_TOKEN_TTL_MS) {\n refreshTokens.delete(refresh_token);\n res.status(400).json({ error: \"invalid_grant\", error_description: \"Refresh token expired\" });\n return;\n }\n // Rotate: revoke old, issue new pair\n const apiKey = entry.apiKey;\n refreshTokens.delete(refresh_token);\n res.json(issueTokens(apiKey));\n return;\n }\n\n if (grant_type !== \"authorization_code\") {\n res.status(400).json({ error: \"unsupported_grant_type\" });\n return;\n }\n\n const pending = pendingCodes.get(code);\n if (!pending || pending.redirectUri !== redirect_uri) {\n res.status(400).json({ error: \"invalid_grant\" });\n return;\n }\n\n // PKCE S256 validation\n const challenge = createHash(\"sha256\")\n .update(code_verifier ?? \"\")\n .digest(\"base64url\");\n if (challenge !== pending.codeChallenge) {\n pendingCodes.delete(code);\n res.status(400).json({\n error: \"invalid_grant\",\n error_description: \"PKCE verification failed\",\n });\n return;\n }\n\n pendingCodes.delete(code);\n res.json(issueTokens(pending.apiKey));\n },\n);\n\n// ── Rate Limiting ────────────────────────────────────────────────────────\n\nconst mcpLimiter = rateLimit({\n windowMs: 60_000,\n max: 120,\n standardHeaders: true,\n legacyHeaders: false,\n message: { error: \"Too many requests. Try again later.\" },\n});\n\n// ── Health Check ─────────────────────────────────────────────────────────\n\napp.get(\"/health\", (_req: any, res: any) => {\n res.json({ status: \"ok\", version: SERVER_VERSION, transport: \"http\" });\n});\n\n// ── Session Management ──────────────────────────────────────────────────\n\ninterface SessionEntry {\n transport: StreamableHTTPServerTransport;\n lastAccess: number;\n}\n\nconst sessions = new Map<string, SessionEntry>();\nconst SESSION_TTL_MS = 30 * 60 * 1000;\nconst MAX_SESSIONS = 200;\n\nfunction evictStaleSessions(): void {\n const now = Date.now();\n for (const [id, entry] of sessions) {\n if (now - entry.lastAccess > SESSION_TTL_MS) {\n logSessionLifecycle(\"session_deleted\", id, \"ttl\");\n entry.transport.close().catch(() => {});\n sessions.delete(id);\n }\n }\n if (sessions.size > MAX_SESSIONS) {\n const sorted = [...sessions.entries()].sort(\n (a, b) => a[1].lastAccess - b[1].lastAccess,\n );\n for (let i = 0; i < sorted.length - MAX_SESSIONS; i++) {\n logSessionLifecycle(\"session_deleted\", sorted[i][0], \"eviction\");\n sorted[i][1].transport.close().catch(() => {});\n sessions.delete(sorted[i][0]);\n }\n }\n}\n\nsetInterval(evictStaleSessions, 60_000);\n\n// ── Auth Helpers ─────────────────────────────────────────────────────────\n\nfunction extractBearerKey(req: any): string | null {\n const header = req.headers?.authorization;\n if (typeof header !== \"string\" || !header.startsWith(\"Bearer \")) return null;\n const token = header.slice(7).trim();\n return token.startsWith(\"pb_sk_\") ? token : null;\n}\n\nfunction send401(req: any, res: any): void {\n const base = baseUrl(req);\n res\n .status(401)\n .set(\n \"WWW-Authenticate\",\n `Bearer resource_metadata=\"${base}/.well-known/oauth-protected-resource\"`,\n )\n .json({ error: \"unauthorized\" });\n}\n\nfunction logRequest(\n method: string,\n outcome: \"ok\" | \"auth_fail\" | \"error\",\n sessionId?: string,\n durationMs?: number,\n): void {\n const ts = new Date().toISOString();\n const sid = sessionId ? ` session=${sessionId}` : \"\";\n const dur = durationMs != null ? ` duration=${durationMs}ms` : \"\";\n process.stderr.write(`[HTTP] ${ts} ${method} ${outcome}${sid}${dur}\\n`);\n}\n\nfunction logSessionLifecycle(\n event: \"session_created\" | \"session_deleted\",\n sessionId: string,\n reason?: \"ttl\" | \"eviction\" | \"onclose\",\n): void {\n const ts = new Date().toISOString();\n const r = reason ? ` reason=${reason}` : \"\";\n process.stderr.write(`[HTTP] ${ts} ${event} session=${sessionId}${r}\\n`);\n}\n\n// ── MCP Handlers ────────────────────────────────────────────────────────\n\napp.post(\"/mcp\", mcpLimiter, async (req: any, res: any) => {\n const apiKey = extractBearerKey(req);\n if (!apiKey) {\n logRequest(\"POST\", \"auth_fail\");\n send401(req, res);\n return;\n }\n\n const sessionId = req.headers[\"mcp-session-id\"] as string | undefined;\n const reqStart = Date.now();\n\n try {\n await runWithAuth({ apiKey }, async () => {\n if (sessionId && sessions.has(sessionId)) {\n const entry = sessions.get(sessionId)!;\n entry.lastAccess = Date.now();\n await entry.transport.handleRequest(req, res, req.body);\n logRequest(\"POST\", \"ok\", sessionId, Date.now() - reqStart);\n } else if (!sessionId && isInitializeRequest(req.body)) {\n const transport = new StreamableHTTPServerTransport({\n sessionIdGenerator: () => randomUUID(),\n onsessioninitialized: (sid: string) => {\n sessions.set(sid, { transport, lastAccess: Date.now() });\n logSessionLifecycle(\"session_created\", sid);\n },\n });\n\n transport.onclose = () => {\n const sid = transport.sessionId;\n if (sid) {\n logSessionLifecycle(\"session_deleted\", sid, \"onclose\");\n sessions.delete(sid);\n }\n };\n\n const server = createProductBrainServer();\n await server.connect(transport);\n await transport.handleRequest(req, res, req.body);\n logRequest(\"POST\", \"ok\", transport.sessionId ?? undefined, Date.now() - reqStart);\n } else {\n process.stderr.write(\n `[HTTP] ${new Date().toISOString()} session_invalid no valid session ID (client may have omitted Mcp-Session-Id)\\n`,\n );\n res.status(400).json({\n jsonrpc: \"2.0\",\n error: { code: -32000, message: \"Bad Request: no valid session ID provided\" },\n id: null,\n });\n }\n });\n } catch (err: any) {\n logRequest(\"POST\", \"error\", sessionId, Date.now() - reqStart);\n if (!res.headersSent) {\n res.status(500).json({\n jsonrpc: \"2.0\",\n error: { code: -32603, message: \"Internal server error\" },\n id: null,\n });\n }\n }\n});\n\napp.get(\"/mcp\", mcpLimiter, async (req: any, res: any) => {\n const apiKey = extractBearerKey(req);\n if (!apiKey) {\n logRequest(\"GET\", \"auth_fail\");\n send401(req, res);\n return;\n }\n\n const sessionId = req.headers[\"mcp-session-id\"] as string | undefined;\n if (!sessionId || !sessions.has(sessionId)) {\n res.status(400).send(\"Invalid or missing session ID\");\n return;\n }\n\n try {\n await runWithAuth({ apiKey }, async () => {\n const entry = sessions.get(sessionId)!;\n entry.lastAccess = Date.now();\n await entry.transport.handleRequest(req, res);\n logRequest(\"GET\", \"ok\", sessionId);\n });\n } catch {\n logRequest(\"GET\", \"error\", sessionId);\n }\n});\n\napp.delete(\"/mcp\", mcpLimiter, async (req: any, res: any) => {\n const apiKey = extractBearerKey(req);\n if (!apiKey) {\n logRequest(\"DELETE\", \"auth_fail\");\n send401(req, res);\n return;\n }\n\n const sessionId = req.headers[\"mcp-session-id\"] as string | undefined;\n if (!sessionId || !sessions.has(sessionId)) {\n res.status(400).send(\"Invalid or missing session ID\");\n return;\n }\n\n try {\n await runWithAuth({ apiKey }, async () => {\n const entry = sessions.get(sessionId)!;\n await entry.transport.handleRequest(req, res);\n logRequest(\"DELETE\", \"ok\", sessionId);\n });\n } catch {\n logRequest(\"DELETE\", \"error\", sessionId);\n }\n});\n\n// ── Start ───────────────────────────────────────────────────────────────\n\nprocess.on(\"unhandledRejection\", (reason) => {\n const msg = reason instanceof Error ? reason.message : String(reason);\n console.error(`[MCP HTTP] Unhandled rejection: ${msg}`);\n});\n\nprocess.on(\"uncaughtException\", (err) => {\n console.error(`[MCP HTTP] Uncaught exception: ${err.stack ?? err.message}`);\n gracefulShutdown();\n});\n\nlet shuttingDown = false;\nasync function gracefulShutdown() {\n if (shuttingDown) return;\n shuttingDown = true;\n setTimeout(() => process.exit(1), 3_000).unref();\n console.log(\"Shutting down...\");\n for (const [, entry] of sessions) {\n await entry.transport.close().catch(() => {});\n }\n try {\n await shutdownAnalytics();\n } catch {\n /* best-effort */\n }\n process.exit(0);\n}\n\n// Bind all interfaces — Railway/Cloudflare reach the container on its non-loopback IP.\n// Loopback-only (127.0.0.1) causes edge 502: the proxy never connects to localhost inside the pod.\nconst LISTEN_HOST = \"0.0.0.0\";\nconst httpServer = app.listen(PORT, LISTEN_HOST, () => {\n console.log(\n `Product Brain MCP HTTP server v${SERVER_VERSION} listening on ${LISTEN_HOST}:${PORT}`,\n );\n});\nhttpServer.on(\"error\", (err) => {\n console.error(`[MCP HTTP] Server error: ${err.message}`);\n process.exit(1);\n});\n\nprocess.on(\"SIGINT\", gracefulShutdown);\nprocess.on(\"SIGTERM\", gracefulShutdown);\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/http.ts"],"sourcesContent":["/**\n * HTTP transport entry point for Product Brain MCP.\n *\n * Serves the MCP protocol over Streamable HTTP for web clients\n * (Claude web app, API consumers) that can't spawn local processes.\n *\n * Implements the full MCP OAuth 2.1 spec (Nov 2025):\n * 1. Protected Resource Metadata (/.well-known/oauth-protected-resource)\n * 2. Authorization Server Metadata (/.well-known/oauth-authorization-server)\n * 3. Dynamic Client Registration (POST /register)\n * 4. Authorization Code + PKCE (GET/POST /authorize)\n * 5. Token Exchange (POST /oauth/token)\n *\n * Env:\n * CONVEX_SITE_URL — Convex deployment URL (defaults to cloud)\n * PORT / MCP_PORT — Listen port (default 3000)\n * CORS_ORIGINS — Comma-separated allowed origins (default: all)\n * PB_MODULES — Comma-separated modules (default: core,gitchain,arch)\n */\n\nimport { createHash, randomUUID } from \"node:crypto\";\nimport express from \"express\";\nimport { StreamableHTTPServerTransport } from \"@modelcontextprotocol/sdk/server/streamableHttp.js\";\nimport { isInitializeRequest } from \"@modelcontextprotocol/sdk/types.js\";\nimport rateLimit from \"express-rate-limit\";\n\nimport { bootstrapHttp } from \"./client.js\";\nimport { runWithAuth } from \"./auth.js\";\nimport { createProductBrainServer, SERVER_VERSION } from \"./server.js\";\nimport { initAnalytics, shutdownAnalytics, getPostHogClient } from \"./analytics.js\";\nimport { initFeatureFlags } from \"./featureFlags.js\";\n\n// ── Bootstrap ───────────────────────────────────────────────────────────\n\nbootstrapHttp();\ninitAnalytics();\ninitFeatureFlags(getPostHogClient());\n\nconst PORT = parseInt(process.env.PORT ?? process.env.MCP_PORT ?? \"3002\", 10);\n\nfunction baseUrl(req: any): string {\n const proto = req.headers[\"x-forwarded-proto\"] ?? req.protocol ?? \"http\";\n const host = req.headers.host ?? `localhost:${PORT}`;\n return `${proto}://${host}`;\n}\n\n// ── Express App ─────────────────────────────────────────────────────────\n\nconst app = express();\n// Required when behind a reverse proxy (e.g. Railway): rate limiter uses X-Forwarded-For\n// and throws ERR_ERL_UNEXPECTED_X_FORWARDED_FOR if trust proxy is false.\napp.set(\"trust proxy\", 1);\napp.use(express.json());\n\n// CORS — fail-closed; requires CORS_ORIGINS to be explicitly configured.\nconst ALLOWED_ORIGINS = process.env.CORS_ORIGINS\n ?.split(\",\")\n .map((o) => o.trim())\n .filter(Boolean);\n\napp.use((_req: any, res: any, next: any) => {\n const origin = _req.headers.origin;\n if (ALLOWED_ORIGINS && origin && ALLOWED_ORIGINS.includes(origin)) {\n res.setHeader(\"Access-Control-Allow-Origin\", origin);\n }\n res.setHeader(\"Access-Control-Allow-Methods\", \"GET, POST, DELETE, OPTIONS\");\n res.setHeader(\n \"Access-Control-Allow-Headers\",\n \"Content-Type, Authorization, Mcp-Session-Id, Last-Event-Id\",\n );\n res.setHeader(\"Access-Control-Expose-Headers\", \"Mcp-Session-Id\");\n if (_req.method === \"OPTIONS\") {\n res.status(204).end();\n return;\n }\n next();\n});\n\n// ── OAuth: Protected Resource Metadata (RFC 9728) ────────────────────────\n// Step 1 of MCP auth: Claude fetches this to discover the authorization server.\n\napp.get(\"/.well-known/oauth-protected-resource\", (req: any, res: any) => {\n const base = baseUrl(req);\n res.json({\n resource: base,\n authorization_servers: [base],\n scopes_supported: [\"mcp:tools\", \"mcp:resources\"],\n bearer_methods_supported: [\"header\"],\n });\n});\n\n// ── OAuth: Authorization Server Metadata (RFC 8414) ──────────────────────\n// Step 2: Claude fetches this to discover authorize, token, and register endpoints.\n\napp.get(\"/.well-known/oauth-authorization-server\", (req: any, res: any) => {\n const base = baseUrl(req);\n res.json({\n issuer: base,\n authorization_endpoint: `${base}/authorize`,\n token_endpoint: `${base}/oauth/token`,\n registration_endpoint: `${base}/register`,\n response_types_supported: [\"code\"],\n grant_types_supported: [\"authorization_code\", \"refresh_token\"],\n code_challenge_methods_supported: [\"S256\"],\n token_endpoint_auth_methods_supported: [\"none\"],\n scopes_supported: [\"mcp:tools\", \"mcp:resources\"],\n });\n});\n\n// ── OAuth: Dynamic Client Registration (RFC 7591) ────────────────────────\n// Step 3: Claude registers itself as a client before starting the auth flow.\n\ninterface RegisteredClient {\n client_id: string;\n redirect_uris: string[];\n client_name?: string;\n registeredAt: number;\n}\n\nconst registeredClients = new Map<string, RegisteredClient>();\n\napp.post(\n \"/register\",\n express.json(),\n (req: any, res: any) => {\n const { redirect_uris, client_name } = req.body;\n\n if (!Array.isArray(redirect_uris) || redirect_uris.length === 0) {\n res.status(400).json({\n error: \"invalid_client_metadata\",\n error_description: \"redirect_uris is required\",\n });\n return;\n }\n\n const clientId = `pb_client_${randomUUID()}`;\n const client: RegisteredClient = {\n client_id: clientId,\n redirect_uris,\n client_name,\n registeredAt: Date.now(),\n };\n registeredClients.set(clientId, client);\n\n res.status(201).json({\n client_id: clientId,\n client_name: client_name ?? \"MCP Client\",\n redirect_uris,\n grant_types: [\"authorization_code\"],\n response_types: [\"code\"],\n token_endpoint_auth_method: \"none\",\n });\n },\n);\n\n// ── OAuth: Authorization Code + PKCE ─────────────────────────────────────\n// Step 4: User enters their pb_sk_* key, server generates a one-time code.\n\ninterface PendingAuth {\n apiKey: string;\n codeChallenge: string;\n redirectUri: string;\n expiresAt: number;\n}\n\nconst pendingCodes = new Map<string, PendingAuth>();\n\n// Refresh token store — declared here so the cleanup interval can reference it.\nconst ACCESS_TOKEN_TTL = 3600; // 1 hour\nconst REFRESH_TOKEN_TTL_MS = 90 * 24 * 60 * 60_000; // 90 days\n\ninterface RefreshEntry {\n apiKey: string;\n createdAt: number;\n}\n\nconst refreshTokens = new Map<string, RefreshEntry>();\n\nsetInterval(() => {\n const now = Date.now();\n for (const [code, auth] of pendingCodes) {\n if (now > auth.expiresAt) pendingCodes.delete(code);\n }\n for (const [id, client] of registeredClients) {\n if (now - client.registeredAt > 24 * 60 * 60_000) registeredClients.delete(id);\n }\n for (const [token, entry] of refreshTokens) {\n if (now - entry.createdAt > REFRESH_TOKEN_TTL_MS) refreshTokens.delete(token);\n }\n}, 60_000);\n\nfunction esc(s: unknown): string {\n return String(s ?? \"\").replace(/[&\"<>]/g, (c) =>\n ({ \"&\": \"&\", '\"': \""\", \"<\": \"<\", \">\": \">\" })[c]!,\n );\n}\n\napp.get(\"/authorize\", (req: any, res: any) => {\n const { redirect_uri, code_challenge, code_challenge_method, state, client_id } =\n req.query;\n res.type(\"html\").send(`<!DOCTYPE html>\n<html lang=\"en\"><head>\n<meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n<title>Authorize — Product Brain</title>\n<style>\n*{margin:0;padding:0;box-sizing:border-box}\nbody{font-family:-apple-system,system-ui,sans-serif;background:#0a0a0a;color:#e5e5e5;\n display:flex;align-items:center;justify-content:center;min-height:100vh;padding:1rem}\n.card{background:#1a1a1a;border:1px solid #333;border-radius:12px;padding:2rem;max-width:400px;width:100%}\nh1{font-size:1.2rem;margin-bottom:.25rem}\n.sub{color:#999;font-size:.85rem;margin-bottom:1.5rem}\nlabel{display:block;font-size:.85rem;margin-bottom:.4rem;color:#ccc}\ninput[type=password]{width:100%;padding:.6rem .75rem;background:#111;border:1px solid #444;\n border-radius:8px;color:#e5e5e5;font:.85rem/1.4 monospace}\ninput:focus{outline:none;border-color:#7c3aed}\nbutton{width:100%;padding:.6rem;background:#7c3aed;color:#fff;border:none;\n border-radius:8px;font-size:.85rem;cursor:pointer;margin-top:1rem}\nbutton:hover{background:#6d28d9}\n.err{color:#ef4444;font-size:.8rem;margin-top:.5rem;display:none}\n</style></head><body>\n<div class=\"card\">\n<h1>Product Brain</h1>\n<p class=\"sub\">Enter your API key to connect Claude to your workspace.</p>\n<form method=\"POST\" action=\"/authorize\">\n <input type=\"hidden\" name=\"redirect_uri\" value=\"${esc(redirect_uri)}\">\n <input type=\"hidden\" name=\"code_challenge\" value=\"${esc(code_challenge)}\">\n <input type=\"hidden\" name=\"code_challenge_method\" value=\"${esc(code_challenge_method)}\">\n <input type=\"hidden\" name=\"state\" value=\"${esc(state)}\">\n <input type=\"hidden\" name=\"client_id\" value=\"${esc(client_id)}\">\n <label for=\"k\">API Key</label>\n <input type=\"password\" id=\"k\" name=\"api_key\" placeholder=\"pb_sk_…\" required autofocus>\n <p class=\"err\" id=\"e\">Key must start with pb_sk_</p>\n <button type=\"submit\">Authorize</button>\n</form>\n</div>\n<script>document.querySelector(\"form\").onsubmit=function(e){\nif(!document.getElementById(\"k\").value.startsWith(\"pb_sk_\")){\ne.preventDefault();document.getElementById(\"e\").style.display=\"block\"}}</script>\n</body></html>`);\n});\n\napp.post(\n \"/authorize\",\n express.urlencoded({ extended: false }),\n (req: any, res: any) => {\n const { api_key, redirect_uri, code_challenge, state, client_id } = req.body;\n\n if (!api_key?.startsWith(\"pb_sk_\")) {\n res.status(400).send(\"Invalid API key\");\n return;\n }\n\n // Validate redirect_uri against the registered client's allowed redirects.\n // Open redirect prevention: never trust a request-supplied redirect_uri without\n // checking it was pre-registered during dynamic client registration (RFC 7591).\n if (!client_id || !registeredClients.has(client_id)) {\n res.status(400).json({\n error: \"invalid_request\",\n error_description: \"Unknown or missing client_id\",\n });\n return;\n }\n\n const client = registeredClients.get(client_id)!;\n if (!client.redirect_uris.includes(redirect_uri)) {\n res.status(400).json({\n error: \"invalid_request\",\n error_description: \"redirect_uri does not match any registered redirect for this client\",\n });\n return;\n }\n\n const code = randomUUID();\n pendingCodes.set(code, {\n apiKey: api_key,\n codeChallenge: code_challenge,\n redirectUri: redirect_uri,\n expiresAt: Date.now() + 5 * 60_000,\n });\n\n const url = new URL(redirect_uri);\n url.searchParams.set(\"code\", code);\n if (state) url.searchParams.set(\"state\", state);\n res.redirect(302, url.toString());\n },\n);\n\n// ── OAuth: Token Exchange ────────────────────────────────────────────────\n// Step 5: Claude exchanges the authorization code (with PKCE verifier) for a token.\n// Supports both authorization_code and refresh_token grants.\n\nfunction issueTokens(apiKey: string): object {\n const refreshToken = `pb_rt_${randomUUID()}`;\n refreshTokens.set(refreshToken, { apiKey, createdAt: Date.now() });\n return {\n access_token: apiKey,\n token_type: \"Bearer\",\n expires_in: ACCESS_TOKEN_TTL,\n refresh_token: refreshToken,\n };\n}\n\napp.post(\n \"/oauth/token\",\n express.urlencoded({ extended: false }),\n express.json(),\n (req: any, res: any) => {\n const { grant_type, code, code_verifier, redirect_uri, refresh_token } =\n req.body;\n\n if (grant_type === \"refresh_token\") {\n const entry = refreshTokens.get(refresh_token);\n if (!entry) {\n res.status(400).json({ error: \"invalid_grant\", error_description: \"Invalid refresh token\" });\n return;\n }\n if (Date.now() - entry.createdAt > REFRESH_TOKEN_TTL_MS) {\n refreshTokens.delete(refresh_token);\n res.status(400).json({ error: \"invalid_grant\", error_description: \"Refresh token expired\" });\n return;\n }\n // Rotate: revoke old, issue new pair\n const apiKey = entry.apiKey;\n refreshTokens.delete(refresh_token);\n res.json(issueTokens(apiKey));\n return;\n }\n\n if (grant_type !== \"authorization_code\") {\n res.status(400).json({ error: \"unsupported_grant_type\" });\n return;\n }\n\n const pending = pendingCodes.get(code);\n if (!pending || pending.redirectUri !== redirect_uri) {\n res.status(400).json({ error: \"invalid_grant\" });\n return;\n }\n\n // PKCE S256 validation\n const challenge = createHash(\"sha256\")\n .update(code_verifier ?? \"\")\n .digest(\"base64url\");\n if (challenge !== pending.codeChallenge) {\n pendingCodes.delete(code);\n res.status(400).json({\n error: \"invalid_grant\",\n error_description: \"PKCE verification failed\",\n });\n return;\n }\n\n pendingCodes.delete(code);\n res.json(issueTokens(pending.apiKey));\n },\n);\n\n// ── Rate Limiting ────────────────────────────────────────────────────────\n\nconst mcpLimiter = rateLimit({\n windowMs: 60_000,\n max: 120,\n standardHeaders: true,\n legacyHeaders: false,\n message: { error: \"Too many requests. Try again later.\" },\n});\n\n// ── Health Check ─────────────────────────────────────────────────────────\n\napp.get(\"/health\", (_req: any, res: any) => {\n res.json({ status: \"ok\", version: SERVER_VERSION, transport: \"http\" });\n});\n\n// ── Session Management ──────────────────────────────────────────────────\n\ninterface SessionEntry {\n transport: StreamableHTTPServerTransport;\n lastAccess: number;\n}\n\nconst sessions = new Map<string, SessionEntry>();\nconst SESSION_TTL_MS = 30 * 60 * 1000;\nconst MAX_SESSIONS = 200;\n\nfunction evictStaleSessions(): void {\n const now = Date.now();\n for (const [id, entry] of sessions) {\n if (now - entry.lastAccess > SESSION_TTL_MS) {\n logSessionLifecycle(\"session_deleted\", id, \"ttl\");\n entry.transport.close().catch(() => {});\n sessions.delete(id);\n }\n }\n if (sessions.size > MAX_SESSIONS) {\n const sorted = [...sessions.entries()].sort(\n (a, b) => a[1].lastAccess - b[1].lastAccess,\n );\n for (let i = 0; i < sorted.length - MAX_SESSIONS; i++) {\n logSessionLifecycle(\"session_deleted\", sorted[i][0], \"eviction\");\n sorted[i][1].transport.close().catch(() => {});\n sessions.delete(sorted[i][0]);\n }\n }\n}\n\nsetInterval(evictStaleSessions, 60_000);\n\n// ── Auth Helpers ─────────────────────────────────────────────────────────\n\nfunction extractBearerKey(req: any): string | null {\n const header = req.headers?.authorization;\n if (typeof header !== \"string\" || !header.startsWith(\"Bearer \")) return null;\n const token = header.slice(7).trim();\n return token.startsWith(\"pb_sk_\") ? token : null;\n}\n\nfunction send401(req: any, res: any): void {\n const base = baseUrl(req);\n res\n .status(401)\n .set(\n \"WWW-Authenticate\",\n `Bearer resource_metadata=\"${base}/.well-known/oauth-protected-resource\"`,\n )\n .json({ error: \"unauthorized\" });\n}\n\nfunction logRequest(\n method: string,\n outcome: \"ok\" | \"auth_fail\" | \"error\",\n sessionId?: string,\n durationMs?: number,\n): void {\n const ts = new Date().toISOString();\n const sid = sessionId ? ` session=${sessionId}` : \"\";\n const dur = durationMs != null ? ` duration=${durationMs}ms` : \"\";\n process.stderr.write(`[HTTP] ${ts} ${method} ${outcome}${sid}${dur}\\n`);\n}\n\nfunction logSessionLifecycle(\n event: \"session_created\" | \"session_deleted\",\n sessionId: string,\n reason?: \"ttl\" | \"eviction\" | \"onclose\",\n): void {\n const ts = new Date().toISOString();\n const r = reason ? ` reason=${reason}` : \"\";\n process.stderr.write(`[HTTP] ${ts} ${event} session=${sessionId}${r}\\n`);\n}\n\n// ── MCP Handlers ────────────────────────────────────────────────────────\n\napp.post(\"/mcp\", mcpLimiter, async (req: any, res: any) => {\n const apiKey = extractBearerKey(req);\n if (!apiKey) {\n logRequest(\"POST\", \"auth_fail\");\n send401(req, res);\n return;\n }\n\n const sessionId = req.headers[\"mcp-session-id\"] as string | undefined;\n const reqStart = Date.now();\n\n try {\n await runWithAuth({ apiKey }, async () => {\n if (sessionId && sessions.has(sessionId)) {\n const entry = sessions.get(sessionId)!;\n entry.lastAccess = Date.now();\n await entry.transport.handleRequest(req, res, req.body);\n logRequest(\"POST\", \"ok\", sessionId, Date.now() - reqStart);\n } else if (!sessionId && isInitializeRequest(req.body)) {\n const transport = new StreamableHTTPServerTransport({\n sessionIdGenerator: () => randomUUID(),\n onsessioninitialized: (sid: string) => {\n sessions.set(sid, { transport, lastAccess: Date.now() });\n logSessionLifecycle(\"session_created\", sid);\n },\n });\n\n transport.onclose = () => {\n const sid = transport.sessionId;\n if (sid) {\n logSessionLifecycle(\"session_deleted\", sid, \"onclose\");\n sessions.delete(sid);\n }\n };\n\n const server = createProductBrainServer();\n await server.connect(transport);\n await transport.handleRequest(req, res, req.body);\n logRequest(\"POST\", \"ok\", transport.sessionId ?? undefined, Date.now() - reqStart);\n } else {\n process.stderr.write(\n `[HTTP] ${new Date().toISOString()} session_invalid no valid session ID (client may have omitted Mcp-Session-Id)\\n`,\n );\n res.status(400).json({\n jsonrpc: \"2.0\",\n error: { code: -32000, message: \"Bad Request: no valid session ID provided\" },\n id: null,\n });\n }\n });\n } catch (err: any) {\n logRequest(\"POST\", \"error\", sessionId, Date.now() - reqStart);\n if (!res.headersSent) {\n res.status(500).json({\n jsonrpc: \"2.0\",\n error: { code: -32603, message: \"Internal server error\" },\n id: null,\n });\n }\n }\n});\n\napp.get(\"/mcp\", mcpLimiter, async (req: any, res: any) => {\n const apiKey = extractBearerKey(req);\n if (!apiKey) {\n logRequest(\"GET\", \"auth_fail\");\n send401(req, res);\n return;\n }\n\n const sessionId = req.headers[\"mcp-session-id\"] as string | undefined;\n if (!sessionId || !sessions.has(sessionId)) {\n res.status(400).send(\"Invalid or missing session ID\");\n return;\n }\n\n try {\n await runWithAuth({ apiKey }, async () => {\n const entry = sessions.get(sessionId)!;\n entry.lastAccess = Date.now();\n await entry.transport.handleRequest(req, res);\n logRequest(\"GET\", \"ok\", sessionId);\n });\n } catch {\n logRequest(\"GET\", \"error\", sessionId);\n }\n});\n\napp.delete(\"/mcp\", mcpLimiter, async (req: any, res: any) => {\n const apiKey = extractBearerKey(req);\n if (!apiKey) {\n logRequest(\"DELETE\", \"auth_fail\");\n send401(req, res);\n return;\n }\n\n const sessionId = req.headers[\"mcp-session-id\"] as string | undefined;\n if (!sessionId || !sessions.has(sessionId)) {\n res.status(400).send(\"Invalid or missing session ID\");\n return;\n }\n\n try {\n await runWithAuth({ apiKey }, async () => {\n const entry = sessions.get(sessionId)!;\n await entry.transport.handleRequest(req, res);\n logRequest(\"DELETE\", \"ok\", sessionId);\n });\n } catch {\n logRequest(\"DELETE\", \"error\", sessionId);\n }\n});\n\n// ── Start ───────────────────────────────────────────────────────────────\n\nprocess.on(\"unhandledRejection\", (reason) => {\n const msg = reason instanceof Error ? reason.message : String(reason);\n console.error(`[MCP HTTP] Unhandled rejection: ${msg}`);\n});\n\nprocess.on(\"uncaughtException\", (err) => {\n console.error(`[MCP HTTP] Uncaught exception: ${err.stack ?? err.message}`);\n gracefulShutdown();\n});\n\nlet shuttingDown = false;\nasync function gracefulShutdown() {\n if (shuttingDown) return;\n shuttingDown = true;\n setTimeout(() => process.exit(1), 3_000).unref();\n console.log(\"Shutting down...\");\n for (const [, entry] of sessions) {\n await entry.transport.close().catch(() => {});\n }\n try {\n await shutdownAnalytics();\n } catch {\n /* best-effort */\n }\n process.exit(0);\n}\n\n// Bind all interfaces — Railway/Cloudflare reach the container on its non-loopback IP.\n// Loopback-only (127.0.0.1) causes edge 502: the proxy never connects to localhost inside the pod.\nconst LISTEN_HOST = \"0.0.0.0\";\nconst httpServer = app.listen(PORT, LISTEN_HOST, () => {\n console.log(\n `Product Brain MCP HTTP server v${SERVER_VERSION} listening on ${LISTEN_HOST}:${PORT}`,\n );\n});\nhttpServer.on(\"error\", (err) => {\n console.error(`[MCP HTTP] Server error: ${err.message}`);\n process.exit(1);\n});\n\nprocess.on(\"SIGINT\", gracefulShutdown);\nprocess.on(\"SIGTERM\", gracefulShutdown);\n"],"mappings":"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","names":[]}
|
package/dist/index.js
CHANGED
|
@@ -9,13 +9,13 @@ import {
|
|
|
9
9
|
orphanAgentSession,
|
|
10
10
|
recoverSessionState,
|
|
11
11
|
startAgentSession
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-UUUJXF2W.js";
|
|
13
13
|
import {
|
|
14
14
|
getPostHogClient,
|
|
15
15
|
initAnalytics,
|
|
16
16
|
shutdownAnalytics,
|
|
17
17
|
trackSessionStarted
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-MOPOQUJP.js";
|
|
19
19
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
import { readFileSync } from "fs";
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
trackSetupCompleted,
|
|
8
8
|
trackSetupStarted,
|
|
9
9
|
writeClientConfig
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-MOPOQUJP.js";
|
|
11
11
|
|
|
12
12
|
// src/cli/setup.ts
|
|
13
13
|
import { execSync } from "child_process";
|
|
@@ -294,4 +294,4 @@ function printClaudeSnippet() {
|
|
|
294
294
|
export {
|
|
295
295
|
runSetup
|
|
296
296
|
};
|
|
297
|
-
//# sourceMappingURL=setup-
|
|
297
|
+
//# sourceMappingURL=setup-YYADLH22.js.map
|
|
@@ -244,7 +244,7 @@ Boolean requesting whether a visible border and background is provided by the ho
|
|
|
244
244
|
- omitted: host decides border`)});y.object({method:y.literal("ui/request-display-mode"),params:y.object({mode:Or.describe("The display mode being requested.")})});y.object({mode:Or.describe("The display mode that was actually set. May differ from requested if not supported.")}).passthrough();var L8=y.union([y.literal("model"),y.literal("app")]).describe("Tool visibility scope - who can access the tool.");y.object({resourceUri:y.string().optional(),visibility:y.array(L8).optional().describe(`Who can access this tool. Default: ["model", "app"]
|
|
245
245
|
- "model": Tool visible to and callable by the agent
|
|
246
246
|
- "app": Tool callable by the app from this server only`)});y.object({mimeTypes:y.array(y.string()).optional().describe('Array of supported MIME types for UI resources.\nMust include `"text/html;profile=mcp-app"` for MCP Apps support.')});y.object({method:y.literal("ui/download-file"),params:y.object({contents:y.array(y.union([hc,vc])).describe("Resource contents to download — embedded (inline data) or linked (host fetches). Uses standard MCP resource types.")})});y.object({method:y.literal("ui/message"),params:y.object({role:y.literal("user").describe('Message role, currently only "user" is supported.'),content:y.array(Xn).describe("Message content blocks (text, image, etc.).")})});y.object({method:y.literal("ui/notifications/sandbox-resource-ready"),params:y.object({html:y.string().describe("HTML content to load into the inner iframe."),sandbox:y.string().optional().describe("Optional override for the inner iframe's sandbox attribute."),csp:cp.optional().describe("CSP configuration from resource metadata."),permissions:dp.optional().describe("Sandbox permissions from resource metadata.")})});y.object({method:y.literal("ui/notifications/tool-result"),params:Rr.describe("Standard MCP tool execution result.")});var r4=y.object({toolInfo:y.object({id:Zi.optional().describe("JSON-RPC id of the tools/call request."),tool:Xo.describe("Tool definition including name, inputSchema, etc.")}).optional().describe("Metadata of the tool call that instantiated this App."),theme:Z8.optional().describe("Current color theme preference."),styles:C8.optional().describe("Style configuration for theming the app."),displayMode:Or.optional().describe("How the UI is currently displayed."),availableDisplayModes:y.array(Or).optional().describe("Display modes the host supports."),containerDimensions:y.union([y.object({height:y.number().describe("Fixed container height in pixels.")}),y.object({maxHeight:y.union([y.number(),y.undefined()]).optional().describe("Maximum container height in pixels.")})]).and(y.union([y.object({width:y.number().describe("Fixed container width in pixels.")}),y.object({maxWidth:y.union([y.number(),y.undefined()]).optional().describe("Maximum container width in pixels.")})])).optional().describe(`Container dimensions. Represents the dimensions of the iframe or other
|
|
247
|
-
container holding the app. Specify either width or maxWidth, and either height or maxHeight.`),locale:y.string().optional().describe("User's language and region preference in BCP 47 format."),timeZone:y.string().optional().describe("User's timezone in IANA format."),userAgent:y.string().optional().describe("Host application identifier."),platform:y.union([y.literal("web"),y.literal("desktop"),y.literal("mobile")]).optional().describe("Platform type for responsive design decisions."),deviceCapabilities:y.object({touch:y.boolean().optional().describe("Whether the device supports touch input."),hover:y.boolean().optional().describe("Whether the device supports hover interactions.")}).optional().describe("Device input capabilities."),safeAreaInsets:y.object({top:y.number().describe("Top safe area inset in pixels."),right:y.number().describe("Right safe area inset in pixels."),bottom:y.number().describe("Bottom safe area inset in pixels."),left:y.number().describe("Left safe area inset in pixels.")}).optional().describe("Mobile safe area boundaries in pixels.")}).passthrough();y.object({method:y.literal("ui/notifications/host-context-changed"),params:r4.describe("Partial context update containing only changed fields.")});y.object({method:y.literal("ui/update-model-context"),params:y.object({content:y.array(Xn).optional().describe("Context content blocks (text, image, etc.)."),structuredContent:y.record(y.string(),y.unknown().describe("Structured content for machine-readable context data.")).optional().describe("Structured content for machine-readable context data.")})});y.object({method:y.literal("ui/initialize"),params:y.object({appInfo:Ei.describe("App identification (name and version)."),appCapabilities:M8.describe("Features and capabilities this app provides."),protocolVersion:y.string().describe("Protocol version this app supports.")})});y.object({protocolVersion:y.string().describe('Negotiated protocol version string (e.g., "2025-11-21").'),hostInfo:Ei.describe("Host application identification and version."),hostCapabilities:R8.describe("Features and capabilities provided by the host."),hostContext:r4.describe("Rich context about the host environment.")}).passthrough();class F8{constructor(n=window.parent,r){Pe(this,"eventTarget");Pe(this,"eventSource");Pe(this,"messageListener");Pe(this,"onclose");Pe(this,"onerror");Pe(this,"onmessage");Pe(this,"sessionId");Pe(this,"setProtocolVersion");this.eventTarget=n,this.eventSource=r,this.messageListener=a=>{var o,s,u;if(r&&a.source!==this.eventSource){console.debug("Ignoring message from unknown source",a);return}let i=b_.safeParse(a.data);i.success?(console.debug("Parsed message",i.data),(o=this.onmessage)==null||o.call(this,i.data)):((s=a.data)==null?void 0:s.jsonrpc)!=="2.0"?console.debug("Ignoring non-JSON-RPC message",i.error.message,a):(console.error("Failed to parse message",i.error.message,a),(u=this.onerror)==null||u.call(this,Error("Invalid JSON-RPC message received: "+i.error.message)))}}async start(){window.addEventListener("message",this.messageListener)}async send(n,r){console.debug("Sending message",n),this.eventTarget.postMessage(n,"*")}async close(){var n;window.removeEventListener("message",this.messageListener),(n=this.onclose)==null||n.call(this)}}const fp=new t9({name:"GraphConstellation",version:"1.0.0"},{});let Xi=null;const
|
|
247
|
+
container holding the app. Specify either width or maxWidth, and either height or maxHeight.`),locale:y.string().optional().describe("User's language and region preference in BCP 47 format."),timeZone:y.string().optional().describe("User's timezone in IANA format."),userAgent:y.string().optional().describe("Host application identifier."),platform:y.union([y.literal("web"),y.literal("desktop"),y.literal("mobile")]).optional().describe("Platform type for responsive design decisions."),deviceCapabilities:y.object({touch:y.boolean().optional().describe("Whether the device supports touch input."),hover:y.boolean().optional().describe("Whether the device supports hover interactions.")}).optional().describe("Device input capabilities."),safeAreaInsets:y.object({top:y.number().describe("Top safe area inset in pixels."),right:y.number().describe("Right safe area inset in pixels."),bottom:y.number().describe("Bottom safe area inset in pixels."),left:y.number().describe("Left safe area inset in pixels.")}).optional().describe("Mobile safe area boundaries in pixels.")}).passthrough();y.object({method:y.literal("ui/notifications/host-context-changed"),params:r4.describe("Partial context update containing only changed fields.")});y.object({method:y.literal("ui/update-model-context"),params:y.object({content:y.array(Xn).optional().describe("Context content blocks (text, image, etc.)."),structuredContent:y.record(y.string(),y.unknown().describe("Structured content for machine-readable context data.")).optional().describe("Structured content for machine-readable context data.")})});y.object({method:y.literal("ui/initialize"),params:y.object({appInfo:Ei.describe("App identification (name and version)."),appCapabilities:M8.describe("Features and capabilities this app provides."),protocolVersion:y.string().describe("Protocol version this app supports.")})});y.object({protocolVersion:y.string().describe('Negotiated protocol version string (e.g., "2025-11-21").'),hostInfo:Ei.describe("Host application identification and version."),hostCapabilities:R8.describe("Features and capabilities provided by the host."),hostContext:r4.describe("Rich context about the host environment.")}).passthrough();class F8{constructor(n=window.parent,r){Pe(this,"eventTarget");Pe(this,"eventSource");Pe(this,"messageListener");Pe(this,"onclose");Pe(this,"onerror");Pe(this,"onmessage");Pe(this,"sessionId");Pe(this,"setProtocolVersion");this.eventTarget=n,this.eventSource=r,this.messageListener=a=>{var o,s,u;if(r&&a.source!==this.eventSource){console.debug("Ignoring message from unknown source",a);return}let i=b_.safeParse(a.data);i.success?(console.debug("Parsed message",i.data),(o=this.onmessage)==null||o.call(this,i.data)):((s=a.data)==null?void 0:s.jsonrpc)!=="2.0"?console.debug("Ignoring non-JSON-RPC message",i.error.message,a):(console.error("Failed to parse message",i.error.message,a),(u=this.onerror)==null||u.call(this,Error("Invalid JSON-RPC message received: "+i.error.message)))}}async start(){window.addEventListener("message",this.messageListener)}async send(n,r){console.debug("Sending message",n),this.eventTarget.postMessage(n,"*")}async close(){var n;window.removeEventListener("message",this.messageListener),(n=this.onclose)==null||n.call(this)}}const fp=new t9({name:"GraphConstellation",version:"1.0.0"},{});let Xi=null;function J8(e){return e.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")}const q8={governs:"#6366f1",informs:"#8b5cf6",part_of:"#a855f7",constrains:"#d946ef",depends_on:"#ec4899",related_to:"#f43f5e",suggested:"#94a3b8",related:"#64748b"};function V8(e){return q8[e.type??""]??"#94a3b8"}function no(e,n){const r=document.getElementById("root");if(!r)return;if(e.length===0){r.innerHTML='<div class="empty">No graph data to display</div>',Xi&&Xi.graphData({nodes:[],links:[]});return}let a=document.getElementById("graph");a||(r.innerHTML='<div class="view-title">Constellation</div><div id="graph"></div>',a=document.getElementById("graph"));const i=new Map(e.map(u=>[u.id,{...u}])),o=n.filter(u=>i.has(u.source)&&i.has(u.target)).map(u=>({source:u.source,target:u.target,type:u.type??"related"})),s={nodes:Array.from(i.values()),links:o};Xi||(Xi=zj()(a).nodeId("id").nodeLabel(u=>J8(`${u.name??u.id}${u.collectionName?` [${u.collectionName}]`:""}`)).linkColor(u=>V8(u)).linkDirectionalArrowLength(4).linkDirectionalArrowRelPos(1).linkCurvature(.15)),Xi.graphData(s)}fp.ontoolresult=e=>{var r,a;const n=e.structuredContent;if((r=n==null?void 0:n.nodes)!=null&&r.length){const i=n.edges??[];no(n.nodes,i)}else if((a=n==null?void 0:n.entries)!=null&&a.length){const i=n.entries,o=i.map(u=>({id:u.entryId??String(Math.random()),name:u.name,collectionName:u.collectionName})),s=o.length>1?i.slice(1).map(u=>({source:i[0].entryId??"",target:u.entryId??"",type:"related"})):[];no(o,s)}else no([],[])};fp.ontoolinput=()=>{no([],[])};const W8=new F8(window.parent,window.parent);fp.connect(W8);</script>
|
|
248
248
|
</head>
|
|
249
249
|
<body>
|
|
250
250
|
<div id="root">
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/analytics.ts","../src/cli/config-writer.ts"],"sourcesContent":["/**\n * PostHog analytics for SynergyOS maintainers — tracks MCP usage (sessions, tool calls).\n * Not user-facing. Key is injected at build time via SYNERGYOS_POSTHOG_KEY.\n * Override with POSTHOG_MCP_KEY for self-hosted deployments.\n */\n\nimport { userInfo } from \"node:os\";\nimport { PostHog } from \"posthog-node\";\n\nlet client: PostHog | null = null;\nlet distinctId = \"anonymous\";\n\nconst POSTHOG_HOST = \"https://eu.i.posthog.com\";\n\n/** Injected at build time: SYNERGYOS_POSTHOG_KEY env when running `npm run build`/publish. */\ndeclare const __SYNERGYOS_POSTHOG_KEY__: string;\n\n/** Only write to stderr when MCP_DEBUG=1 for quieter default DX. */\nfunction log(msg: string): void {\n if (process.env.MCP_DEBUG === \"1\") {\n process.stderr.write(msg);\n }\n}\n\nfunction getBuildTimeKey(): string {\n try {\n return __SYNERGYOS_POSTHOG_KEY__;\n } catch {\n // Not replaced by bundler (e.g. running via tsx in tests) — treat as absent.\n return \"\";\n }\n}\n\nexport function initAnalytics(): void {\n const apiKey = process.env.POSTHOG_MCP_KEY || getBuildTimeKey();\n if (!apiKey) {\n log(\"[MCP-ANALYTICS] No PostHog key — tracking disabled (set SYNERGYOS_POSTHOG_KEY at build time for publish)\\n\");\n return;\n }\n\n client = new PostHog(apiKey, {\n host: POSTHOG_HOST,\n flushAt: 1,\n flushInterval: 5000,\n featureFlagsPollingInterval: 30_000,\n });\n distinctId = process.env.MCP_USER_ID || fallbackDistinctId();\n\n log(`[MCP-ANALYTICS] Initialized — host=${POSTHOG_HOST} distinctId=${distinctId}\\n`);\n}\n\nfunction fallbackDistinctId(): string {\n try {\n return userInfo().username;\n } catch {\n return `os-${process.pid}`;\n }\n}\n\nexport function trackSessionStarted(\n workspaceId: string,\n serverVersion: string,\n): void {\n if (!client) return;\n client.capture({\n distinctId,\n event: \"mcp_session_started\",\n properties: {\n workspace_id: workspaceId,\n server_version: serverVersion,\n source: \"mcp-server\",\n $groups: { workspace: workspaceId },\n },\n });\n}\n\nexport function trackToolCall(\n fn: string,\n status: \"ok\" | \"error\",\n durationMs: number,\n workspaceId: string,\n errorMsg?: string,\n): void {\n const properties: Record<string, unknown> = {\n tool: fn,\n status,\n duration_ms: durationMs,\n workspace_id: workspaceId,\n source: \"mcp-server\",\n $groups: { workspace: workspaceId },\n };\n if (errorMsg) properties.error = errorMsg;\n\n if (!client) return;\n client.capture({\n distinctId,\n event: \"mcp_tool_called\",\n properties,\n });\n}\n\nexport function trackSetupStarted(): void {\n if (!client) return;\n client.capture({\n distinctId,\n event: \"mcp_setup_started\",\n properties: {\n source: \"mcp-server\",\n platform: process.platform,\n },\n });\n}\n\nexport function trackSetupCompleted(\n chosenClient: string,\n outcome: \"config_written\" | \"config_existed\" | \"snippet_shown\" | \"write_error\",\n): void {\n if (!client) return;\n client.capture({\n distinctId,\n event: \"mcp_setup_completed\",\n properties: {\n client: chosenClient,\n outcome,\n source: \"mcp-server\",\n platform: process.platform,\n },\n });\n}\n\nexport function trackQualityVerdict(\n workspaceId: string,\n props: {\n entry_id: string;\n entry_type: string;\n tier: string;\n context: string;\n passed: boolean;\n source: string;\n criteria_total: number;\n criteria_failed: number;\n llm_scheduled: boolean;\n },\n): void {\n if (!client) return;\n client.capture({\n distinctId,\n event: \"quality_verdict_generated\",\n properties: {\n ...props,\n workspace_id: workspaceId,\n source_system: \"mcp-server\",\n $groups: { workspace: workspaceId },\n },\n });\n}\n\nexport function trackQualityCheck(\n workspaceId: string,\n props: {\n entry_id: string;\n entry_type: string;\n tier: string;\n passed: boolean;\n source: string;\n llm_status?: string;\n llm_duration_ms?: number;\n llm_error?: string;\n has_roger_martin: boolean;\n },\n): void {\n if (!client) return;\n client.capture({\n distinctId,\n event: \"quality_verdict_checked\",\n properties: {\n ...props,\n workspace_id: workspaceId,\n source_system: \"mcp-server\",\n $groups: { workspace: workspaceId },\n },\n });\n}\n\nexport type ClassifierReasonCategory =\n | \"auto-routed\"\n | \"low-confidence\"\n | \"ambiguous\"\n | \"non-provisioned\";\n\ntype CaptureClassifierTelemetryProps = {\n predicted_collection: string;\n confidence: number;\n auto_routed: boolean;\n reason_category: ClassifierReasonCategory;\n explicit_collection_provided: boolean;\n};\n\nfunction trackCaptureClassifierEvent(\n event: \"mcp_capture_classifier_evaluated\" | \"mcp_capture_classifier_auto_routed\" | \"mcp_capture_classifier_fallback\",\n workspaceId: string,\n props: CaptureClassifierTelemetryProps,\n): void {\n if (!client) return;\n try {\n client.capture({\n distinctId,\n event,\n properties: {\n ...props,\n workspace_id: workspaceId,\n source_system: \"mcp-server\",\n $groups: { workspace: workspaceId },\n },\n });\n } catch {\n // Analytics are advisory and must never break capture flow.\n }\n}\n\nexport function trackCaptureClassifierEvaluated(\n workspaceId: string,\n props: CaptureClassifierTelemetryProps,\n): void {\n trackCaptureClassifierEvent(\"mcp_capture_classifier_evaluated\", workspaceId, props);\n}\n\nexport function trackCaptureClassifierAutoRouted(\n workspaceId: string,\n props: CaptureClassifierTelemetryProps,\n): void {\n trackCaptureClassifierEvent(\"mcp_capture_classifier_auto_routed\", workspaceId, props);\n}\n\nexport function trackCaptureClassifierFallback(\n workspaceId: string,\n props: CaptureClassifierTelemetryProps,\n): void {\n trackCaptureClassifierEvent(\"mcp_capture_classifier_fallback\", workspaceId, props);\n}\n\n/** GLO-26 / TEN-156: every SSOT commit for PostHog funnels (split auto vs manual). */\nexport function trackChainEntryCommitted(\n workspaceId: string,\n props: {\n entry_id: string;\n collection?: string;\n commit_method: \"auto\" | \"manual\";\n surface:\n | \"mcp_commit_tool\"\n | \"mcp_capture\"\n | \"mcp_wrapup\";\n },\n): void {\n if (!client) return;\n try {\n client.capture({\n distinctId,\n event: \"chain_entry_committed\",\n properties: {\n workspace_id: workspaceId,\n source_system: \"mcp-server\",\n $groups: { workspace: workspaceId },\n ...props,\n },\n });\n } catch {\n // Analytics must never break the tool path.\n }\n}\n\nexport function trackKnowledgeGap(\n workspaceId: string,\n props: {\n query: string;\n tool: string;\n action: string;\n gap_type: \"search_zero\" | \"context_task_empty\" | \"context_entry_isolated\" | \"context_graph_empty\";\n collection_scope?: string;\n },\n): void {\n if (!client) return;\n try {\n client.capture({\n distinctId,\n event: \"knowledge_gap_detected\",\n properties: {\n ...props,\n query: props.query.slice(0, 200),\n workspace_id: workspaceId,\n source_system: \"mcp-server\",\n $groups: { workspace: workspaceId },\n },\n });\n } catch {\n // Analytics must never break the tool response path.\n }\n}\n\nexport function getPostHogClient(): PostHog | null {\n return client;\n}\n\nexport async function shutdownAnalytics(): Promise<void> {\n await client?.shutdown();\n}\n","/**\n * Multi-client MCP config detection and writer.\n *\n * Supports:\n * - Cursor: .cursor/mcp.json in cwd (project-level)\n * - Claude Desktop: ~/Library/Application Support/Claude/claude_desktop_config.json (macOS)\n * %APPDATA%/Claude/claude_desktop_config.json (Windows)\n *\n * The writer reads existing config, merges the new server entry (never\n * overwrites existing entries), and writes back. Falls back to printing\n * a snippet for unsupported OS or unknown formats.\n */\n\nimport { existsSync, readFileSync, writeFileSync, mkdirSync } from \"node:fs\";\nimport { join, dirname } from \"node:path\";\nimport { homedir, platform } from \"node:os\";\n\nexport interface McpClientInfo {\n name: string;\n configPath: string;\n}\n\nconst SERVER_ENTRY_KEY = \"Product Brain\";\nconst LEGACY_ENTRY_KEY = \"productbrain\";\n\n/**\n * Canonical npx package specifier. Update here when exiting beta.\n * Frontend mirror: src/lib/constants/mcp.ts\n * Business rule: BR-84 (Chain)\n */\nexport const MCP_NPX_PACKAGE = \"@productbrain/mcp@beta\";\n\nfunction buildServerEntry(apiKey: string) {\n return {\n command: \"npx\",\n args: [\"-y\", MCP_NPX_PACKAGE],\n env: { PRODUCTBRAIN_API_KEY: apiKey },\n };\n}\n\n// ── Detection ───────────────────────────────────────────────────────────\n\nfunction getCursorConfigPath(): string {\n return join(process.cwd(), \".cursor\", \"mcp.json\");\n}\n\nfunction getClaudeDesktopConfigPath(): string | null {\n const os = platform();\n if (os === \"darwin\") {\n return join(\n homedir(),\n \"Library\",\n \"Application Support\",\n \"Claude\",\n \"claude_desktop_config.json\",\n );\n }\n if (os === \"win32\") {\n const appData = process.env.APPDATA ?? join(homedir(), \"AppData\", \"Roaming\");\n return join(appData, \"Claude\", \"claude_desktop_config.json\");\n }\n // Linux: no official Claude Desktop location yet\n return null;\n}\n\nexport function resolveClient(name: \"Cursor\" | \"Claude Desktop\"): McpClientInfo | null {\n if (name === \"Cursor\") {\n return { name, configPath: getCursorConfigPath() };\n }\n const configPath = getClaudeDesktopConfigPath();\n return configPath ? { name, configPath } : null;\n}\n\n// ── Writing ─────────────────────────────────────────────────────────────\n\nfunction readJsonSafe(path: string): Record<string, any> {\n if (!existsSync(path)) return {};\n try {\n return JSON.parse(readFileSync(path, \"utf-8\"));\n } catch {\n return {};\n }\n}\n\n/**\n * Write or merge the Product Brain server entry into a client config file.\n * Migrates legacy \"productbrain\" key to \"Product Brain\" when present.\n * Returns true if the config was written, false if already present.\n */\nexport async function writeClientConfig(\n client: McpClientInfo,\n apiKey: string,\n): Promise<boolean> {\n const config = readJsonSafe(client.configPath);\n\n const serversKey = \"mcpServers\";\n if (!config[serversKey]) config[serversKey] = {};\n\n // Migrate legacy \"productbrain\" key or update existing Product Brain with new API key\n if (config[serversKey][LEGACY_ENTRY_KEY]) {\n const legacy = config[serversKey][LEGACY_ENTRY_KEY];\n config[serversKey][SERVER_ENTRY_KEY] = {\n ...buildServerEntry(apiKey),\n env: { ...legacy.env, PRODUCTBRAIN_API_KEY: legacy.env?.PRODUCTBRAIN_API_KEY ?? apiKey },\n };\n delete config[serversKey][LEGACY_ENTRY_KEY];\n } else {\n const existing = config[serversKey][SERVER_ENTRY_KEY];\n config[serversKey][SERVER_ENTRY_KEY] = existing\n ? { ...existing, env: { ...existing.env, PRODUCTBRAIN_API_KEY: apiKey } }\n : buildServerEntry(apiKey);\n }\n\n const dir = dirname(client.configPath);\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n\n writeFileSync(client.configPath, JSON.stringify(config, null, 2) + \"\\n\", \"utf-8\");\n return true;\n}\n"],"mappings":"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","names":["client"]}
|