@agimon-ai/doompi-core 0.0.1-alpha.82 → 0.0.1-alpha.83
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/src/server/sessionMcpRoutes.cjs +23 -5
- package/dist/src/server/sessionMcpRoutes.cjs.map +1 -1
- package/dist/src/server/sessionMcpRoutes.mjs +23 -5
- package/dist/src/server/sessionMcpRoutes.mjs.map +1 -1
- package/dist/src/services/sessionMcpAuthorization/index.cjs +20 -6
- package/dist/src/services/sessionMcpAuthorization/index.cjs.map +1 -1
- package/dist/src/services/sessionMcpAuthorization/index.d.mts +8 -3
- package/dist/src/services/sessionMcpAuthorization/index.d.mts.map +1 -1
- package/dist/src/services/sessionMcpAuthorization/index.mjs +20 -6
- package/dist/src/services/sessionMcpAuthorization/index.mjs.map +1 -1
- package/dist/src/services/sessionMcpRegistrationStore/index.cjs +2 -2
- package/dist/src/services/sessionMcpRegistrationStore/index.cjs.map +1 -1
- package/dist/src/services/sessionMcpRegistrationStore/index.mjs +2 -2
- package/dist/src/services/sessionMcpRegistrationStore/index.mjs.map +1 -1
- package/package.json +1 -1
|
@@ -309,6 +309,7 @@ function createSessionMcpRoutes(options) {
|
|
|
309
309
|
if (options.registrationStore === void 0) return;
|
|
310
310
|
const active = target(workspaceId, sessionId);
|
|
311
311
|
if (active === void 0 || grant.sessionGeneration !== active.generation || grant.clientId === "") throw new Error("The session grant is no longer active.");
|
|
312
|
+
if (authorization.readClient(grant.clientId)?.tokenEndpointAuthMethod === "api_key") return;
|
|
312
313
|
const registration = authorization.persistentRegistration(grant.clientId, workspaceId, Date.now());
|
|
313
314
|
if (registration === void 0 || !options.registrationStore.save(registration)) throw new Error("The verified Session MCP client could not be saved.");
|
|
314
315
|
},
|
|
@@ -353,7 +354,7 @@ function createSessionMcpRoutes(options) {
|
|
|
353
354
|
const client = authorization.readClient(clientId);
|
|
354
355
|
const binding = authorization.readAuthorizationBinding(clientId);
|
|
355
356
|
const redirectUri = url.searchParams.get("redirect_uri") ?? "";
|
|
356
|
-
if (client === void 0 || binding === void 0 || redirectUri !== client.redirectUri) return oauthError(new require_index$1.SessionMcpOAuthError("invalid_client", "Unknown client or callback."));
|
|
357
|
+
if (client === void 0 || client.tokenEndpointAuthMethod !== "client_secret_post" || binding === void 0 || redirectUri !== client.redirectUri) return oauthError(new require_index$1.SessionMcpOAuthError("invalid_client", "Unknown client or callback."));
|
|
357
358
|
const callback = new URL(client.redirectUri);
|
|
358
359
|
const state = url.searchParams.get("state");
|
|
359
360
|
try {
|
|
@@ -507,7 +508,10 @@ function createSessionMcpRoutes(options) {
|
|
|
507
508
|
const hasSkills = Object.hasOwn(input, "skills");
|
|
508
509
|
const tools = parseStringArray(input.tools);
|
|
509
510
|
const skills = parseStringArray(input.skills);
|
|
510
|
-
|
|
511
|
+
const apiKey = input.authMethod === "api_key";
|
|
512
|
+
if (input.authMethod !== void 0 && input.authMethod !== "oauth" && !apiKey) return json(400, { error: "Authentication method is not supported." });
|
|
513
|
+
if (apiKey && (scope !== "session" || Object.hasOwn(input, "redirectUri"))) return json(400, { error: "API keys require session scope and no redirectUri." });
|
|
514
|
+
if (!apiKey && typeof input.redirectUri !== "string") return json(400, { error: "A redirectUri is required." });
|
|
511
515
|
if (scope === "session" && (hasTools || hasSkills)) return json(400, { error: "Session scope cannot include capability grants." });
|
|
512
516
|
if (scope === "restricted" && (typeof input.name !== "string" || !tools || !skills)) return json(400, { error: "Restricted scope requires a name, tools, and skills." });
|
|
513
517
|
if (scope === "restricted") {
|
|
@@ -522,13 +526,16 @@ function createSessionMcpRoutes(options) {
|
|
|
522
526
|
if (tools.some((name) => !activeTools.has(name)) || skills.some((name) => !activeSkills.has(name))) return json(400, { error: "Every requested grant must be active in the session." });
|
|
523
527
|
}
|
|
524
528
|
try {
|
|
525
|
-
const client = authorization.createClient({
|
|
529
|
+
const client = authorization.createClient(apiKey ? {
|
|
530
|
+
name: typeof input.name === "string" ? input.name : "API key",
|
|
531
|
+
authMethod: "api_key"
|
|
532
|
+
} : {
|
|
526
533
|
name: scope === "session" ? defaultClientName(configuredOrigin) : input.name,
|
|
527
534
|
redirectUri: input.redirectUri
|
|
528
535
|
});
|
|
529
536
|
const audience = `${configuredOrigin}${sessionPath(workspaceId, sessionId)}`;
|
|
530
537
|
try {
|
|
531
|
-
|
|
538
|
+
const binding = scope === "session" ? authorization.createAuthorizationBinding({
|
|
532
539
|
clientId: client.clientId,
|
|
533
540
|
sessionId,
|
|
534
541
|
sessionGeneration: resolved.generation,
|
|
@@ -544,7 +551,18 @@ function createSessionMcpRoutes(options) {
|
|
|
544
551
|
routing,
|
|
545
552
|
tools: [...new Set(tools)],
|
|
546
553
|
skills: [...new Set(skills)]
|
|
547
|
-
})
|
|
554
|
+
});
|
|
555
|
+
if (apiKey) {
|
|
556
|
+
const registration = authorization.persistentRegistration(client.clientId, workspaceId, Date.now());
|
|
557
|
+
if (registration === void 0 || !options.registrationStore.save(registration)) {
|
|
558
|
+
authorization.revokeClient(client.clientId);
|
|
559
|
+
return json(500, { error: "API key could not be saved." });
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
return json(201, { client: {
|
|
563
|
+
...publicClient(client, binding),
|
|
564
|
+
clientSecret: client.clientSecret
|
|
565
|
+
} });
|
|
548
566
|
} catch (error) {
|
|
549
567
|
authorization.revokeClient(client.clientId);
|
|
550
568
|
throw error;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sessionMcpRoutes.cjs","names":["SessionMcpOAuthError","sessionMcpRouting","path","SessionMcpConversationError","fs","createSessionMcpAuthorizationService","createSessionMcpHttpHandler"],"sources":["../../../src/server/sessionMcpRoutes.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\n\nimport type { DoomHubSessionReservations, DoomHubSessionScope } from '../schemas/hubChannel';\nimport {\n createSessionMcpAuthorizationService,\n SessionMcpOAuthError,\n sessionMcpRouting,\n type SessionMcpAuthorizationBinding,\n type SessionMcpAuthorizationService,\n type SessionMcpClient,\n} from '../services/sessionMcpAuthorization';\nimport { SessionMcpConversationError, type SessionMcpConversationStore } from '../services/sessionMcpConversations';\nimport type { SessionMcpRegistrationStore } from '../services/sessionMcpRegistrationStore';\nimport type { HeadlessHub, HeadlessHubSession } from './headlessHub';\nimport { createSessionMcpHttpHandler, type SessionMcpHttpHandler } from './sessionMcpHandler';\n\nconst AUTHORIZATION_SERVER_DISCOVERY = '/.well-known/oauth-authorization-server';\nconst AUTHORIZE_ROUTE = '/oauth/authorize';\nconst TOKEN_ROUTE = '/oauth/token';\nconst SESSION_MCP_PATTERN = /^\\/api\\/workspaces\\/([^/]+)\\/sessions\\/([^/]+)\\/mcp$/u;\nconst SESSION_MCP_CONFIG_PATTERN = /^\\/api\\/workspaces\\/([^/]+)\\/sessions\\/([^/]+)\\/mcp\\/config$/u;\nconst SESSION_MCP_CLIENTS_PATTERN = /^\\/api\\/workspaces\\/([^/]+)\\/sessions\\/([^/]+)\\/mcp\\/clients(?:\\/([^/]+))?$/u;\nconst SESSION_MCP_CONVERSATIONS_PATTERN =\n /^\\/api\\/workspaces\\/([^/]+)\\/sessions\\/([^/]+)\\/mcp\\/conversations(?:\\/([^/]+))?$/u;\nconst PROTECTED_RESOURCE_PATTERN =\n /^\\/\\.well-known\\/oauth-protected-resource(\\/api\\/workspaces\\/[^/]+\\/sessions\\/[^/]+\\/mcp)$/u;\n\nexport interface SessionMcpRoutesOptions {\n readonly headlessHub: HeadlessHub;\n readonly publicOrigin: () => string | undefined;\n readonly publicOriginRevision?: () => number;\n readonly authorization?: SessionMcpAuthorizationService;\n readonly registrationStore?: SessionMcpRegistrationStore;\n readonly conversationStore?: SessionMcpConversationStore;\n readonly isSessionPersisted?: (sessionId: string, cwd: string) => boolean;\n readonly onNotice?: (message: string) => void;\n}\n\nexport interface SessionMcpRoutes {\n handlePublic(request: Request): Promise<Response | undefined>;\n handleHost(request: Request): Promise<Response | undefined>;\n readonly reservations: DoomHubSessionReservations;\n closeSessionBinding(sessionId: string): void;\n close(): void;\n}\n\nfunction json(status: number, value: unknown): Response {\n return Response.json(value, { status, headers: { 'cache-control': 'no-store' } });\n}\n\nfunction oauthError(error: unknown): Response {\n if (error instanceof SessionMcpOAuthError)\n return json(error.code === 'invalid_client' ? 401 : 400, {\n error: error.code,\n error_description: error.message,\n });\n return json(400, { error: 'invalid_request', error_description: 'The OAuth request was invalid.' });\n}\n\nfunction trustedOrigin(value: string | undefined): string | undefined {\n if (value === undefined) return undefined;\n try {\n const url = new URL(value);\n return url.protocol === 'https:' && url.pathname === '/' && url.search === '' && url.hash === ''\n ? url.origin\n : undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction sessionPath(workspaceId: string, sessionId: string): string {\n return `/api/workspaces/${encodeURIComponent(workspaceId)}/sessions/${encodeURIComponent(sessionId)}/mcp`;\n}\n\nfunction parseStringArray(value: unknown): readonly string[] | undefined {\n return Array.isArray(value) && value.every((entry) => typeof entry === 'string') ? value : undefined;\n}\n\nfunction defaultClientName(origin: string): string {\n return `ChatGPT · ${new URL(origin).hostname}`;\n}\n\nfunction formString(form: FormData, name: string): string {\n const value = form.get(name);\n return typeof value === 'string' ? value : '';\n}\n\nfunction publicClient(client: SessionMcpClient, binding: SessionMcpAuthorizationBinding): Record<string, unknown> {\n return {\n ...client,\n scope: binding.scope,\n routing: sessionMcpRouting(binding.routing),\n tools: binding.tools,\n skills: binding.skills,\n audience: binding.audience,\n };\n}\n\n/** Canonicalizes an intended directory without creating it or accepting symlink aliases. */\nfunction executionDirectory(value: string): string {\n if (!path.isAbsolute(value) || value.includes('\\0') || value.length > 4096)\n throw new SessionMcpConversationError('INVALID_EXECUTION_DIRECTORY', 'Choose an absolute execution directory.');\n let ancestor = path.resolve(value);\n const suffix: string[] = [];\n for (;;) {\n try {\n return path.join(fs.realpathSync(ancestor), ...suffix);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT' || path.dirname(ancestor) === ancestor)\n throw new SessionMcpConversationError(\n 'INVALID_EXECUTION_DIRECTORY',\n 'The execution directory cannot be resolved.',\n );\n suffix.unshift(path.basename(ancestor));\n ancestor = path.dirname(ancestor);\n }\n }\n}\n\nfunction overlappingDirectories(left: string, right: string): boolean {\n const contained = (root: string, target: string): boolean => {\n const relative = path.relative(root, target);\n return (\n relative === '' || (!path.isAbsolute(relative) && relative !== '..' && !relative.startsWith(`..${path.sep}`))\n );\n };\n return contained(left, right) || contained(right, left);\n}\n\n/** Routes the process-local session MCP authority and revokes every grant with its live incarnation. */\nexport function createSessionMcpRoutes(options: SessionMcpRoutesOptions): SessionMcpRoutes {\n const authorization = options.authorization ?? createSessionMcpAuthorizationService();\n const incarnations = new Map<\n string,\n { host: HeadlessHubSession['host']; generation: number; handlers: Map<string, SessionMcpHttpHandler> }\n >();\n let nextGeneration = 1;\n let publishPending = (): void => {};\n\n const revokeIncarnation = (sessionId: string, generation: number): void => {\n authorization.revokeSessionGeneration(sessionId, generation);\n };\n const register = (session: HeadlessHubSession): void => {\n const current = incarnations.get(session.id);\n if (current?.host === session.host) return;\n if (current !== undefined) revokeIncarnation(session.id, current.generation);\n incarnations.set(session.id, { host: session.host, generation: nextGeneration++, handlers: new Map() });\n };\n for (const session of options.headlessHub.snapshot()) register(session);\n const unsubscribe = options.headlessHub.onEvent((event) => {\n if (event.kind === 'upsert') {\n register(event.session);\n publishPending();\n }\n if (event.kind === 'removed') {\n const current = incarnations.get(event.sessionId);\n if (current !== undefined) revokeIncarnation(event.sessionId, current.generation);\n incarnations.delete(event.sessionId);\n }\n });\n\n let observedOrigin: string | undefined;\n let observedOriginRevision: number | undefined;\n let originObserved = false;\n const origin = (): string | undefined => {\n const current = trustedOrigin(options.publicOrigin());\n const revision = options.publicOriginRevision?.();\n if (\n (originObserved && current !== observedOrigin) ||\n (revision !== undefined && observedOriginRevision !== undefined && revision !== observedOriginRevision)\n ) {\n for (const client of authorization.listClients()) {\n authorization.revokeClient(client.clientId);\n options.registrationStore?.remove(client.clientId);\n }\n for (const incarnation of incarnations.values()) incarnation.handlers.clear();\n }\n observedOrigin = current;\n observedOriginRevision = revision;\n originObserved = true;\n restorePersistedRegistrations(current);\n return current;\n };\n const target = (workspaceId: string, sessionId: string) => {\n const session = options.headlessHub.session(sessionId);\n const incarnation = incarnations.get(sessionId);\n return session !== undefined && session.workspaceId === workspaceId && incarnation?.host === session.host\n ? { session, generation: incarnation.generation }\n : undefined;\n };\n const restorePersistedRegistrations = (configuredOrigin: string | undefined): void => {\n if (configuredOrigin === undefined) return;\n for (const registration of options.registrationStore?.registrations() ?? []) {\n const audience = `${configuredOrigin}${sessionPath(registration.workspaceId, registration.binding.sessionId)}`;\n if (registration.binding.audience !== audience) {\n options.registrationStore?.remove(registration.client.clientId);\n continue;\n }\n const resolved = target(registration.workspaceId, registration.binding.sessionId);\n if (resolved !== undefined) authorization.restorePersistentRegistration(registration, resolved.generation);\n }\n };\n\n const requireStore = (): SessionMcpConversationStore => {\n if (!options.conversationStore)\n throw new SessionMcpConversationError(\n 'CONVERSATION_ROUTING_UNAVAILABLE',\n 'Persistent conversation routing is unavailable.',\n );\n return options.conversationStore;\n };\n publishPending = () => {\n if (!options.conversationStore) return;\n try {\n const records = options.conversationStore.list();\n for (const parent of options.headlessHub.snapshot())\n options.headlessHub.setPendingSessionSetups?.(\n parent.id,\n records\n .filter((record) => record.parentSessionId === parent.id && record.state === 'pending')\n .map((record) => ({\n id: record.id,\n name: `conversation ${record.id.slice(0, 8)}`,\n createdAt: record.createdAt,\n ...(record.cwd === undefined ? {} : { cwd: record.cwd }),\n })),\n );\n } catch (error) {\n options.onNotice?.(error instanceof Error ? error.message : 'Conversation bindings are unavailable.');\n }\n };\n const readReservation = (id: string, parentSessionId: string) => {\n origin();\n const record = requireStore().get(id, parentSessionId);\n const binding = authorization.readAuthorizationBinding(record.clientId);\n const parent = target(record.parentWorkspaceId, parentSessionId);\n if (\n !parent ||\n binding?.routing !== 'conversation' ||\n binding.sessionId !== parentSessionId ||\n binding.sessionGeneration !== parent.generation\n )\n throw new SessionMcpConversationError(\n 'SESSION_UNAVAILABLE',\n 'The connection that owns this setup is no longer active.',\n id,\n );\n return { record, parent };\n };\n const reservations: DoomHubSessionReservations = {\n read(id, parentSessionId) {\n const { record } = readReservation(id, parentSessionId);\n return { sessionId: record.id, ...(record.cwd === undefined ? {} : { cwd: record.cwd }) };\n },\n async prepare(id, parentSessionId, suppliedDirectory) {\n const { record, parent } = readReservation(id, parentSessionId);\n const cwd = executionDirectory(suppliedDirectory);\n const parentRoot =\n options.headlessHub.workspaces().find((workspace) => workspace.id === parent.session.workspaceId)?.root ??\n parent.session.cwd;\n const occupied = [\n parentRoot,\n ...options.headlessHub\n .snapshot()\n .filter((session) => session.id !== id)\n .map((session) => session.cwd),\n ...requireStore()\n .list()\n .filter((entry) => entry.id !== id && entry.state !== 'closed' && entry.cwd !== undefined)\n .map((entry) => entry.cwd!),\n ];\n if (occupied.some((directory) => overlappingDirectories(cwd, executionDirectory(directory))))\n throw new SessionMcpConversationError(\n 'EXECUTION_DIRECTORY_SHARED',\n 'Choose a separate checkout, not a directory shared with another session.',\n id,\n );\n if (record.state === 'bound' && record.cwd !== cwd)\n throw new SessionMcpConversationError(\n 'SESSION_TARGET_FIXED',\n 'An initialized session cannot change its directory.',\n id,\n );\n requireStore().prepare(id, parentSessionId, cwd);\n publishPending();\n return { sessionId: id, cwd };\n },\n async complete(id, parentSessionId): Promise<DoomHubSessionScope> {\n const { record } = readReservation(id, parentSessionId);\n const child = options.headlessHub.session(id);\n if (\n !record.cwd ||\n !child?.workspaceId ||\n child.parentSessionId !== parentSessionId ||\n executionDirectory(child.cwd) !== record.cwd ||\n options.isSessionPersisted?.(id, child.cwd) === false\n )\n throw new SessionMcpConversationError(\n 'SESSION_SETUP_INCOMPLETE',\n 'The reserved session is not durably ready. Recover the existing setup instead of creating another checkout.',\n id,\n );\n requireStore().bind(id, parentSessionId, child.workspaceId);\n publishPending();\n return { sessionId: id, workspaceId: child.workspaceId, cwd: child.cwd };\n },\n };\n const provision = new Map<string, Promise<DoomHubSessionScope>>();\n const setupDirectory = async (\n id: string,\n parentSessionId: string,\n directory: string,\n ): Promise<DoomHubSessionScope> => {\n // A mistyped existing-directory choice must not permanently reserve a nonexistent path.\n const canonical = executionDirectory(directory);\n if (!fs.statSync(canonical).isDirectory()) throw new Error('The execution directory is not a directory.');\n const prepared = await reservations.prepare(id, parentSessionId, canonical);\n const pending = provision.get(id);\n if (pending) return pending;\n const setup = (async () => {\n if (!options.headlessHub.session(id)) {\n const record = requireStore().get(id, parentSessionId);\n if (record.state === 'bound' || options.isSessionPersisted?.(id, prepared.cwd))\n throw new SessionMcpConversationError(\n 'SESSION_UNAVAILABLE',\n 'Resume the existing session in DoomPi. It will not be recreated automatically.',\n id,\n );\n if (!fs.statSync(prepared.cwd).isDirectory()) throw new Error('The execution directory is not a directory.');\n await options.headlessHub.sessionService.create({\n cwd: prepared.cwd,\n name: `conversation ${id.slice(0, 8)}`,\n parentSessionId,\n sessionProvenance: 'remote-conversation',\n reservationId: id,\n });\n }\n return reservations.complete(id, parentSessionId);\n })();\n provision.set(id, setup);\n try {\n return await setup;\n } finally {\n if (provision.get(id) === setup) provision.delete(id);\n }\n };\n const provisionConversationWorktree = async (\n record: ReturnType<SessionMcpConversationStore['reserve']>,\n signal?: AbortSignal,\n ): Promise<DoomHubSessionScope> => {\n const pending = provision.get(record.id);\n if (pending) return pending;\n const setup = (async () => {\n try {\n const child = await options.headlessHub.sessionService.provisionReservedWorktree?.({\n reservationId: record.id,\n parentSessionId: record.parentSessionId,\n signal,\n });\n if (child === undefined || child.sessionId !== record.id)\n throw new Error('Automatic worktree provisioning did not create the reserved session.');\n return child;\n } catch (error) {\n if (error instanceof SessionMcpConversationError) throw error;\n throw new SessionMcpConversationError(\n 'SESSION_WORKTREE_PROVISION_FAILED',\n 'An automatic worktree could not be created for this conversation. Resolve the host Git setup, then retry.',\n record.id,\n );\n }\n })();\n provision.set(record.id, setup);\n try {\n return await setup;\n } finally {\n if (provision.get(record.id) === setup) provision.delete(record.id);\n }\n };\n const publicRoutes = async (request: Request): Promise<Response | undefined> => {\n const url = new URL(request.url);\n const configuredOrigin = origin();\n const mcpMatch = SESSION_MCP_PATTERN.exec(url.pathname);\n if (mcpMatch !== null) {\n if (configuredOrigin === undefined) return json(503, { error: 'Session MCP public origin is unavailable.' });\n const workspaceId = decodeURIComponent(mcpMatch[1]);\n const sessionId = decodeURIComponent(mcpMatch[2]);\n const exactPath = sessionPath(workspaceId, sessionId);\n if (url.pathname !== exactPath || url.search !== '') return json(404, { error: 'Not found.' });\n const audience = `${configuredOrigin}${exactPath}`;\n const handlers = target(workspaceId, sessionId) === undefined ? undefined : incarnations.get(sessionId)?.handlers;\n const handler =\n handlers?.get(audience) ??\n createSessionMcpHttpHandler({\n audience,\n authorization,\n onNotice: options.onNotice,\n resolveConversation: async (grant, digest, reserve, signal) => {\n const store = requireStore();\n let record = reserve\n ? store.reserve(grant.clientId, sessionId, workspaceId, digest)\n : store.find(grant.clientId, sessionId, digest);\n if (!record || record.state === 'closed')\n throw new SessionMcpConversationError(\n 'SESSION_UNAVAILABLE',\n 'This conversation binding is unavailable. It will not be recreated automatically.',\n );\n if (record.state === 'pending') {\n publishPending();\n await provisionConversationWorktree(record, signal);\n record = store.find(grant.clientId, sessionId, digest);\n if (!record || record.state !== 'bound')\n throw new SessionMcpConversationError(\n 'SESSION_WORKTREE_PROVISION_FAILED',\n 'The automatic worktree setup did not complete. Retry after resolving the host Git setup.',\n );\n }\n const child = record.workspaceId === undefined ? undefined : target(record.workspaceId, record.id);\n if (\n !child ||\n child.session.parentSessionId !== sessionId ||\n executionDirectory(child.session.cwd) !== record.cwd ||\n !fs.existsSync(child.session.cwd)\n )\n throw new SessionMcpConversationError(\n 'SESSION_UNAVAILABLE',\n 'The bound session is unavailable. Resume it in DoomPi; no other session will be used.',\n record.id,\n );\n return { sessionId: record.id, generation: child.generation, toolSurface: child.session.host.mcpSurface };\n },\n onVerified: (grant) => {\n if (options.registrationStore === undefined) return;\n const active = target(workspaceId, sessionId);\n if (active === undefined || grant.sessionGeneration !== active.generation || grant.clientId === '') {\n throw new Error('The session grant is no longer active.');\n }\n const registration = authorization.persistentRegistration(grant.clientId, workspaceId, Date.now());\n if (registration === undefined || !options.registrationStore.save(registration)) {\n throw new Error('The verified Session MCP client could not be saved.');\n }\n },\n resourceMetadataUrl: `${configuredOrigin}/.well-known/oauth-protected-resource${exactPath}`,\n resolveSession: (grantedSessionId) => {\n if (grantedSessionId !== sessionId) return undefined;\n const resolved = target(workspaceId, sessionId);\n return resolved === undefined\n ? undefined\n : { sessionId, generation: resolved.generation, toolSurface: resolved.session.host.mcpSurface };\n },\n });\n handlers?.set(audience, handler);\n return handler(new Request(audience, request));\n }\n\n const protectedMatch = PROTECTED_RESOURCE_PATTERN.exec(url.pathname);\n if (protectedMatch !== null && request.method === 'GET') {\n if (configuredOrigin === undefined) return json(503, { error: 'Session MCP public origin is unavailable.' });\n return json(200, {\n resource: `${configuredOrigin}${protectedMatch[1]}`,\n authorization_servers: [configuredOrigin],\n bearer_methods_supported: ['header'],\n });\n }\n if (url.pathname === AUTHORIZATION_SERVER_DISCOVERY && request.method === 'GET') {\n if (configuredOrigin === undefined) return json(503, { error: 'Session MCP public origin is unavailable.' });\n return json(200, {\n issuer: configuredOrigin,\n authorization_endpoint: `${configuredOrigin}${AUTHORIZE_ROUTE}`,\n token_endpoint: `${configuredOrigin}${TOKEN_ROUTE}`,\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: ['client_secret_post'],\n });\n }\n if (url.pathname === AUTHORIZE_ROUTE && request.method === 'GET') {\n if (configuredOrigin === undefined) return json(503, { error: 'Session MCP public origin is unavailable.' });\n const clientId = url.searchParams.get('client_id') ?? '';\n const client = authorization.readClient(clientId);\n const binding = authorization.readAuthorizationBinding(clientId);\n const redirectUri = url.searchParams.get('redirect_uri') ?? '';\n if (client === undefined || binding === undefined || redirectUri !== client.redirectUri) {\n return oauthError(new SessionMcpOAuthError('invalid_client', 'Unknown client or callback.'));\n }\n const callback = new URL(client.redirectUri);\n const state = url.searchParams.get('state');\n try {\n const boundSession = options.headlessHub.session(binding.sessionId);\n const boundIncarnation = incarnations.get(binding.sessionId);\n const expectedAudience =\n boundSession?.workspaceId === undefined\n ? undefined\n : `${configuredOrigin}${sessionPath(boundSession.workspaceId, binding.sessionId)}`;\n if (\n expectedAudience === undefined ||\n binding.audience !== expectedAudience ||\n boundIncarnation?.host !== boundSession?.host ||\n binding.sessionGeneration !== boundIncarnation?.generation\n ) {\n authorization.revokeClient(clientId);\n throw new SessionMcpOAuthError('invalid_grant', 'The preauthorized session is no longer active.');\n }\n if (url.searchParams.get('response_type') !== 'code')\n throw new SessionMcpOAuthError('invalid_request', 'Only the code response type is supported.');\n if (url.searchParams.get('resource') !== null && url.searchParams.get('resource') !== binding.audience)\n throw new SessionMcpOAuthError('invalid_request', 'The requested resource is not preauthorized.');\n const code = authorization.issueAuthorizationCode({\n clientId,\n redirectUri,\n codeChallenge: url.searchParams.get('code_challenge') ?? '',\n codeChallengeMethod: url.searchParams.get('code_challenge_method') as 'S256',\n });\n callback.searchParams.set('code', code.code);\n } catch (error) {\n callback.searchParams.set('error', error instanceof SessionMcpOAuthError ? error.code : 'invalid_request');\n callback.searchParams.set(\n 'error_description',\n error instanceof Error ? error.message : 'The OAuth request was invalid.',\n );\n }\n if (state !== null) callback.searchParams.set('state', state);\n return new Response(null, { status: 302, headers: { location: callback.href, 'cache-control': 'no-store' } });\n }\n if (url.pathname === TOKEN_ROUTE && request.method === 'POST') {\n try {\n const form = await request.formData();\n const grantType = formString(form, 'grant_type');\n const clientId = formString(form, 'client_id');\n const clientSecret = formString(form, 'client_secret');\n const result =\n grantType === 'authorization_code'\n ? authorization.exchangeToken({\n grantType,\n clientId,\n clientSecret,\n code: formString(form, 'code'),\n redirectUri: formString(form, 'redirect_uri'),\n codeVerifier: formString(form, 'code_verifier'),\n })\n : grantType === 'refresh_token'\n ? authorization.exchangeToken({\n grantType,\n clientId,\n clientSecret,\n refreshToken: formString(form, 'refresh_token'),\n })\n : (() => {\n throw new SessionMcpOAuthError('unsupported_grant_type', 'The grant type is not supported.');\n })();\n return json(200, {\n access_token: result.accessToken,\n refresh_token: result.refreshToken,\n token_type: result.tokenType,\n expires_in: result.expiresIn,\n });\n } catch (error) {\n return oauthError(error);\n }\n }\n return undefined;\n };\n\n publishPending();\n return {\n reservations,\n closeSessionBinding(sessionId) {\n options.conversationStore?.closeSession(sessionId);\n publishPending();\n },\n handlePublic: publicRoutes,\n async handleHost(request) {\n const url = new URL(request.url);\n origin();\n const configMatch = SESSION_MCP_CONFIG_PATTERN.exec(url.pathname);\n const match = SESSION_MCP_CLIENTS_PATTERN.exec(url.pathname);\n const conversationMatch = SESSION_MCP_CONVERSATIONS_PATTERN.exec(url.pathname);\n if (configMatch === null && match === null && conversationMatch === null) return undefined;\n const routeMatch = configMatch ?? match ?? conversationMatch!;\n const workspaceId = decodeURIComponent(routeMatch[1]);\n const sessionId = decodeURIComponent(routeMatch[2]);\n const clientId = match?.[3] === undefined ? undefined : decodeURIComponent(match[3]);\n const resolved = target(workspaceId, sessionId);\n if (resolved === undefined) return json(404, { error: 'Session not found.' });\n if (conversationMatch !== null) {\n const id = conversationMatch[3] === undefined ? undefined : decodeURIComponent(conversationMatch[3]);\n try {\n if (request.method === 'GET' && id === undefined)\n return json(200, {\n conversations: requireStore()\n .list()\n .filter((record) => record.parentSessionId === sessionId && record.state !== 'closed')\n .map(({ conversationDigest: _digest, clientId: _clientId, ...record }) => record),\n });\n if (id !== undefined && request.method === 'DELETE') {\n const record = requireStore().get(id, sessionId);\n if (record.state !== 'pending' || options.headlessHub.session(id))\n return json(409, { error: 'Remove the existing session instead.' });\n requireStore().closeSession(id);\n publishPending();\n return json(200, { ok: true });\n }\n if (id !== undefined && request.method === 'POST') {\n const body: unknown = await request.json();\n if (typeof body !== 'object' || body === null || !('cwd' in body) || typeof body.cwd !== 'string')\n return json(400, { error: 'An execution directory is required.' });\n return json(200, { session: await setupDirectory(id, sessionId, body.cwd) });\n }\n return json(405, { error: 'Method not allowed.' });\n } catch (error) {\n return json(409, {\n error:\n error instanceof SessionMcpConversationError\n ? error.message\n : 'Session setup failed. Recover the existing setup before retrying.',\n ...(error instanceof SessionMcpConversationError ? { code: error.code } : {}),\n });\n }\n }\n if (configMatch !== null) {\n if (request.method !== 'GET') return json(405, { error: 'Method not allowed.' });\n const configuredOrigin = origin();\n if (configuredOrigin === undefined) return json(503, { error: 'Session MCP public origin is unavailable.' });\n let surface;\n try {\n surface = resolved.session.host.mcpSurface.readSurface();\n } catch {\n return json(409, { error: 'The session capability surface is not ready.' });\n }\n return json(200, {\n audience: `${configuredOrigin}${sessionPath(workspaceId, sessionId)}`,\n authorizationEndpoint: `${configuredOrigin}${AUTHORIZE_ROUTE}`,\n tokenEndpoint: `${configuredOrigin}${TOKEN_ROUTE}`,\n tools: surface.tools.map(({ name, label, description }) => ({ name, label, description })),\n skills: surface.skills,\n });\n }\n if (clientId === undefined && request.method === 'GET') {\n return json(200, {\n clients: authorization.listClients().flatMap((client) => {\n const binding = authorization.readAuthorizationBinding(client.clientId);\n return binding?.sessionId === sessionId ? [publicClient(client, binding)] : [];\n }),\n });\n }\n if (clientId === undefined && request.method === 'POST') {\n const configuredOrigin = origin();\n if (configuredOrigin === undefined) return json(503, { error: 'Session MCP public origin is unavailable.' });\n let body: unknown;\n try {\n body = await request.json();\n } catch {\n return json(400, { error: 'Invalid client request.' });\n }\n if (typeof body !== 'object' || body === null) return json(400, { error: 'Invalid client request.' });\n const input = body as Record<string, unknown>;\n if (!options.conversationStore || !options.registrationStore)\n return json(503, { error: 'Mandatory conversation routing requires persistent host state.' });\n if (input.routing !== undefined && input.routing !== 'session' && input.routing !== 'conversation')\n return json(400, { error: 'Session MCP routing is not supported.' });\n const routing = sessionMcpRouting(input.routing);\n const requestedScope = input.scope;\n if (requestedScope !== undefined && requestedScope !== 'session' && requestedScope !== 'restricted') {\n return json(400, { error: 'Authorization scope is not supported.' });\n }\n const scope = requestedScope === 'session' ? 'session' : 'restricted';\n const hasTools = Object.hasOwn(input, 'tools');\n const hasSkills = Object.hasOwn(input, 'skills');\n const tools = parseStringArray(input.tools);\n const skills = parseStringArray(input.skills);\n if (typeof input.redirectUri !== 'string') return json(400, { error: 'A redirectUri is required.' });\n if (scope === 'session' && (hasTools || hasSkills)) {\n return json(400, { error: 'Session scope cannot include capability grants.' });\n }\n if (scope === 'restricted' && (typeof input.name !== 'string' || !tools || !skills)) {\n return json(400, { error: 'Restricted scope requires a name, tools, and skills.' });\n }\n if (scope === 'restricted') {\n let surface;\n try {\n surface = resolved.session.host.mcpSurface.readSurface();\n } catch {\n return json(409, { error: 'The session capability surface is not ready.' });\n }\n const activeTools = new Set(surface.tools.map((tool) => tool.name));\n const activeSkills = new Set(surface.skills.map((skill) => skill.name));\n if (tools!.some((name) => !activeTools.has(name)) || skills!.some((name) => !activeSkills.has(name)))\n return json(400, { error: 'Every requested grant must be active in the session.' });\n }\n try {\n const client = authorization.createClient({\n name: scope === 'session' ? defaultClientName(configuredOrigin) : (input.name as string),\n redirectUri: input.redirectUri,\n });\n const audience = `${configuredOrigin}${sessionPath(workspaceId, sessionId)}`;\n try {\n const binding =\n scope === 'session'\n ? authorization.createAuthorizationBinding({\n clientId: client.clientId,\n sessionId,\n sessionGeneration: resolved.generation,\n audience,\n scope: 'session',\n routing,\n })\n : authorization.createAuthorizationBinding({\n clientId: client.clientId,\n sessionId,\n sessionGeneration: resolved.generation,\n audience,\n scope: 'restricted',\n routing,\n tools: [...new Set(tools!)],\n skills: [...new Set(skills!)],\n });\n return json(201, { client: publicClient(client, binding) });\n } catch (error) {\n authorization.revokeClient(client.clientId);\n throw error;\n }\n } catch (error) {\n return oauthError(error);\n }\n }\n if (clientId !== undefined && request.method === 'DELETE') {\n const binding = authorization.readAuthorizationBinding(clientId);\n if (binding?.sessionId !== sessionId) return json(404, { error: 'Client not found.' });\n if (options.registrationStore !== undefined && !options.registrationStore.remove(clientId)) {\n return json(500, { error: 'Client revocation could not be saved.' });\n }\n authorization.revokeClient(clientId);\n options.conversationStore?.closeClient(clientId);\n publishPending();\n return json(200, { ok: true });\n }\n return json(405, { error: 'Method not allowed.' });\n },\n close() {\n unsubscribe();\n for (const [sessionId, incarnation] of incarnations) revokeIncarnation(sessionId, incarnation.generation);\n incarnations.clear();\n },\n };\n}\n\nexport function isPublicSessionMcpRoute(method: string, pathname: string): boolean {\n return (\n SESSION_MCP_PATTERN.test(pathname) ||\n (method === 'GET' && PROTECTED_RESOURCE_PATTERN.test(pathname)) ||\n (method === 'GET' && (pathname === AUTHORIZATION_SERVER_DISCOVERY || pathname === AUTHORIZE_ROUTE)) ||\n (method === 'POST' && pathname === TOKEN_ROUTE)\n );\n}\n\nexport function isSessionMcpHostRoute(pathname: string): boolean {\n return (\n SESSION_MCP_CONFIG_PATTERN.test(pathname) ||\n SESSION_MCP_CLIENTS_PATTERN.test(pathname) ||\n SESSION_MCP_CONVERSATIONS_PATTERN.test(pathname)\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"sessionMcpRoutes.cjs","names":["SessionMcpOAuthError","sessionMcpRouting","path","SessionMcpConversationError","fs","createSessionMcpAuthorizationService","createSessionMcpHttpHandler"],"sources":["../../../src/server/sessionMcpRoutes.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\n\nimport type { DoomHubSessionReservations, DoomHubSessionScope } from '../schemas/hubChannel';\nimport {\n createSessionMcpAuthorizationService,\n SessionMcpOAuthError,\n sessionMcpRouting,\n type SessionMcpAuthorizationBinding,\n type SessionMcpAuthorizationService,\n type SessionMcpClient,\n} from '../services/sessionMcpAuthorization';\nimport { SessionMcpConversationError, type SessionMcpConversationStore } from '../services/sessionMcpConversations';\nimport type { SessionMcpRegistrationStore } from '../services/sessionMcpRegistrationStore';\nimport type { HeadlessHub, HeadlessHubSession } from './headlessHub';\nimport { createSessionMcpHttpHandler, type SessionMcpHttpHandler } from './sessionMcpHandler';\n\nconst AUTHORIZATION_SERVER_DISCOVERY = '/.well-known/oauth-authorization-server';\nconst AUTHORIZE_ROUTE = '/oauth/authorize';\nconst TOKEN_ROUTE = '/oauth/token';\nconst SESSION_MCP_PATTERN = /^\\/api\\/workspaces\\/([^/]+)\\/sessions\\/([^/]+)\\/mcp$/u;\nconst SESSION_MCP_CONFIG_PATTERN = /^\\/api\\/workspaces\\/([^/]+)\\/sessions\\/([^/]+)\\/mcp\\/config$/u;\nconst SESSION_MCP_CLIENTS_PATTERN = /^\\/api\\/workspaces\\/([^/]+)\\/sessions\\/([^/]+)\\/mcp\\/clients(?:\\/([^/]+))?$/u;\nconst SESSION_MCP_CONVERSATIONS_PATTERN =\n /^\\/api\\/workspaces\\/([^/]+)\\/sessions\\/([^/]+)\\/mcp\\/conversations(?:\\/([^/]+))?$/u;\nconst PROTECTED_RESOURCE_PATTERN =\n /^\\/\\.well-known\\/oauth-protected-resource(\\/api\\/workspaces\\/[^/]+\\/sessions\\/[^/]+\\/mcp)$/u;\n\nexport interface SessionMcpRoutesOptions {\n readonly headlessHub: HeadlessHub;\n readonly publicOrigin: () => string | undefined;\n readonly publicOriginRevision?: () => number;\n readonly authorization?: SessionMcpAuthorizationService;\n readonly registrationStore?: SessionMcpRegistrationStore;\n readonly conversationStore?: SessionMcpConversationStore;\n readonly isSessionPersisted?: (sessionId: string, cwd: string) => boolean;\n readonly onNotice?: (message: string) => void;\n}\n\nexport interface SessionMcpRoutes {\n handlePublic(request: Request): Promise<Response | undefined>;\n handleHost(request: Request): Promise<Response | undefined>;\n readonly reservations: DoomHubSessionReservations;\n closeSessionBinding(sessionId: string): void;\n close(): void;\n}\n\nfunction json(status: number, value: unknown): Response {\n return Response.json(value, { status, headers: { 'cache-control': 'no-store' } });\n}\n\nfunction oauthError(error: unknown): Response {\n if (error instanceof SessionMcpOAuthError)\n return json(error.code === 'invalid_client' ? 401 : 400, {\n error: error.code,\n error_description: error.message,\n });\n return json(400, { error: 'invalid_request', error_description: 'The OAuth request was invalid.' });\n}\n\nfunction trustedOrigin(value: string | undefined): string | undefined {\n if (value === undefined) return undefined;\n try {\n const url = new URL(value);\n return url.protocol === 'https:' && url.pathname === '/' && url.search === '' && url.hash === ''\n ? url.origin\n : undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction sessionPath(workspaceId: string, sessionId: string): string {\n return `/api/workspaces/${encodeURIComponent(workspaceId)}/sessions/${encodeURIComponent(sessionId)}/mcp`;\n}\n\nfunction parseStringArray(value: unknown): readonly string[] | undefined {\n return Array.isArray(value) && value.every((entry) => typeof entry === 'string') ? value : undefined;\n}\n\nfunction defaultClientName(origin: string): string {\n return `ChatGPT · ${new URL(origin).hostname}`;\n}\n\nfunction formString(form: FormData, name: string): string {\n const value = form.get(name);\n return typeof value === 'string' ? value : '';\n}\n\nfunction publicClient(client: SessionMcpClient, binding: SessionMcpAuthorizationBinding): Record<string, unknown> {\n return {\n ...client,\n scope: binding.scope,\n routing: sessionMcpRouting(binding.routing),\n tools: binding.tools,\n skills: binding.skills,\n audience: binding.audience,\n };\n}\n\n/** Canonicalizes an intended directory without creating it or accepting symlink aliases. */\nfunction executionDirectory(value: string): string {\n if (!path.isAbsolute(value) || value.includes('\\0') || value.length > 4096)\n throw new SessionMcpConversationError('INVALID_EXECUTION_DIRECTORY', 'Choose an absolute execution directory.');\n let ancestor = path.resolve(value);\n const suffix: string[] = [];\n for (;;) {\n try {\n return path.join(fs.realpathSync(ancestor), ...suffix);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT' || path.dirname(ancestor) === ancestor)\n throw new SessionMcpConversationError(\n 'INVALID_EXECUTION_DIRECTORY',\n 'The execution directory cannot be resolved.',\n );\n suffix.unshift(path.basename(ancestor));\n ancestor = path.dirname(ancestor);\n }\n }\n}\n\nfunction overlappingDirectories(left: string, right: string): boolean {\n const contained = (root: string, target: string): boolean => {\n const relative = path.relative(root, target);\n return (\n relative === '' || (!path.isAbsolute(relative) && relative !== '..' && !relative.startsWith(`..${path.sep}`))\n );\n };\n return contained(left, right) || contained(right, left);\n}\n\n/** Routes the process-local session MCP authority and revokes every grant with its live incarnation. */\nexport function createSessionMcpRoutes(options: SessionMcpRoutesOptions): SessionMcpRoutes {\n const authorization = options.authorization ?? createSessionMcpAuthorizationService();\n const incarnations = new Map<\n string,\n { host: HeadlessHubSession['host']; generation: number; handlers: Map<string, SessionMcpHttpHandler> }\n >();\n let nextGeneration = 1;\n let publishPending = (): void => {};\n\n const revokeIncarnation = (sessionId: string, generation: number): void => {\n authorization.revokeSessionGeneration(sessionId, generation);\n };\n const register = (session: HeadlessHubSession): void => {\n const current = incarnations.get(session.id);\n if (current?.host === session.host) return;\n if (current !== undefined) revokeIncarnation(session.id, current.generation);\n incarnations.set(session.id, { host: session.host, generation: nextGeneration++, handlers: new Map() });\n };\n for (const session of options.headlessHub.snapshot()) register(session);\n const unsubscribe = options.headlessHub.onEvent((event) => {\n if (event.kind === 'upsert') {\n register(event.session);\n publishPending();\n }\n if (event.kind === 'removed') {\n const current = incarnations.get(event.sessionId);\n if (current !== undefined) revokeIncarnation(event.sessionId, current.generation);\n incarnations.delete(event.sessionId);\n }\n });\n\n let observedOrigin: string | undefined;\n let observedOriginRevision: number | undefined;\n let originObserved = false;\n const origin = (): string | undefined => {\n const current = trustedOrigin(options.publicOrigin());\n const revision = options.publicOriginRevision?.();\n if (\n (originObserved && current !== observedOrigin) ||\n (revision !== undefined && observedOriginRevision !== undefined && revision !== observedOriginRevision)\n ) {\n for (const client of authorization.listClients()) {\n authorization.revokeClient(client.clientId);\n options.registrationStore?.remove(client.clientId);\n }\n for (const incarnation of incarnations.values()) incarnation.handlers.clear();\n }\n observedOrigin = current;\n observedOriginRevision = revision;\n originObserved = true;\n restorePersistedRegistrations(current);\n return current;\n };\n const target = (workspaceId: string, sessionId: string) => {\n const session = options.headlessHub.session(sessionId);\n const incarnation = incarnations.get(sessionId);\n return session !== undefined && session.workspaceId === workspaceId && incarnation?.host === session.host\n ? { session, generation: incarnation.generation }\n : undefined;\n };\n const restorePersistedRegistrations = (configuredOrigin: string | undefined): void => {\n if (configuredOrigin === undefined) return;\n for (const registration of options.registrationStore?.registrations() ?? []) {\n const audience = `${configuredOrigin}${sessionPath(registration.workspaceId, registration.binding.sessionId)}`;\n if (registration.binding.audience !== audience) {\n options.registrationStore?.remove(registration.client.clientId);\n continue;\n }\n const resolved = target(registration.workspaceId, registration.binding.sessionId);\n if (resolved !== undefined) authorization.restorePersistentRegistration(registration, resolved.generation);\n }\n };\n\n const requireStore = (): SessionMcpConversationStore => {\n if (!options.conversationStore)\n throw new SessionMcpConversationError(\n 'CONVERSATION_ROUTING_UNAVAILABLE',\n 'Persistent conversation routing is unavailable.',\n );\n return options.conversationStore;\n };\n publishPending = () => {\n if (!options.conversationStore) return;\n try {\n const records = options.conversationStore.list();\n for (const parent of options.headlessHub.snapshot())\n options.headlessHub.setPendingSessionSetups?.(\n parent.id,\n records\n .filter((record) => record.parentSessionId === parent.id && record.state === 'pending')\n .map((record) => ({\n id: record.id,\n name: `conversation ${record.id.slice(0, 8)}`,\n createdAt: record.createdAt,\n ...(record.cwd === undefined ? {} : { cwd: record.cwd }),\n })),\n );\n } catch (error) {\n options.onNotice?.(error instanceof Error ? error.message : 'Conversation bindings are unavailable.');\n }\n };\n const readReservation = (id: string, parentSessionId: string) => {\n origin();\n const record = requireStore().get(id, parentSessionId);\n const binding = authorization.readAuthorizationBinding(record.clientId);\n const parent = target(record.parentWorkspaceId, parentSessionId);\n if (\n !parent ||\n binding?.routing !== 'conversation' ||\n binding.sessionId !== parentSessionId ||\n binding.sessionGeneration !== parent.generation\n )\n throw new SessionMcpConversationError(\n 'SESSION_UNAVAILABLE',\n 'The connection that owns this setup is no longer active.',\n id,\n );\n return { record, parent };\n };\n const reservations: DoomHubSessionReservations = {\n read(id, parentSessionId) {\n const { record } = readReservation(id, parentSessionId);\n return { sessionId: record.id, ...(record.cwd === undefined ? {} : { cwd: record.cwd }) };\n },\n async prepare(id, parentSessionId, suppliedDirectory) {\n const { record, parent } = readReservation(id, parentSessionId);\n const cwd = executionDirectory(suppliedDirectory);\n const parentRoot =\n options.headlessHub.workspaces().find((workspace) => workspace.id === parent.session.workspaceId)?.root ??\n parent.session.cwd;\n const occupied = [\n parentRoot,\n ...options.headlessHub\n .snapshot()\n .filter((session) => session.id !== id)\n .map((session) => session.cwd),\n ...requireStore()\n .list()\n .filter((entry) => entry.id !== id && entry.state !== 'closed' && entry.cwd !== undefined)\n .map((entry) => entry.cwd!),\n ];\n if (occupied.some((directory) => overlappingDirectories(cwd, executionDirectory(directory))))\n throw new SessionMcpConversationError(\n 'EXECUTION_DIRECTORY_SHARED',\n 'Choose a separate checkout, not a directory shared with another session.',\n id,\n );\n if (record.state === 'bound' && record.cwd !== cwd)\n throw new SessionMcpConversationError(\n 'SESSION_TARGET_FIXED',\n 'An initialized session cannot change its directory.',\n id,\n );\n requireStore().prepare(id, parentSessionId, cwd);\n publishPending();\n return { sessionId: id, cwd };\n },\n async complete(id, parentSessionId): Promise<DoomHubSessionScope> {\n const { record } = readReservation(id, parentSessionId);\n const child = options.headlessHub.session(id);\n if (\n !record.cwd ||\n !child?.workspaceId ||\n child.parentSessionId !== parentSessionId ||\n executionDirectory(child.cwd) !== record.cwd ||\n options.isSessionPersisted?.(id, child.cwd) === false\n )\n throw new SessionMcpConversationError(\n 'SESSION_SETUP_INCOMPLETE',\n 'The reserved session is not durably ready. Recover the existing setup instead of creating another checkout.',\n id,\n );\n requireStore().bind(id, parentSessionId, child.workspaceId);\n publishPending();\n return { sessionId: id, workspaceId: child.workspaceId, cwd: child.cwd };\n },\n };\n const provision = new Map<string, Promise<DoomHubSessionScope>>();\n const setupDirectory = async (\n id: string,\n parentSessionId: string,\n directory: string,\n ): Promise<DoomHubSessionScope> => {\n // A mistyped existing-directory choice must not permanently reserve a nonexistent path.\n const canonical = executionDirectory(directory);\n if (!fs.statSync(canonical).isDirectory()) throw new Error('The execution directory is not a directory.');\n const prepared = await reservations.prepare(id, parentSessionId, canonical);\n const pending = provision.get(id);\n if (pending) return pending;\n const setup = (async () => {\n if (!options.headlessHub.session(id)) {\n const record = requireStore().get(id, parentSessionId);\n if (record.state === 'bound' || options.isSessionPersisted?.(id, prepared.cwd))\n throw new SessionMcpConversationError(\n 'SESSION_UNAVAILABLE',\n 'Resume the existing session in DoomPi. It will not be recreated automatically.',\n id,\n );\n if (!fs.statSync(prepared.cwd).isDirectory()) throw new Error('The execution directory is not a directory.');\n await options.headlessHub.sessionService.create({\n cwd: prepared.cwd,\n name: `conversation ${id.slice(0, 8)}`,\n parentSessionId,\n sessionProvenance: 'remote-conversation',\n reservationId: id,\n });\n }\n return reservations.complete(id, parentSessionId);\n })();\n provision.set(id, setup);\n try {\n return await setup;\n } finally {\n if (provision.get(id) === setup) provision.delete(id);\n }\n };\n const provisionConversationWorktree = async (\n record: ReturnType<SessionMcpConversationStore['reserve']>,\n signal?: AbortSignal,\n ): Promise<DoomHubSessionScope> => {\n const pending = provision.get(record.id);\n if (pending) return pending;\n const setup = (async () => {\n try {\n const child = await options.headlessHub.sessionService.provisionReservedWorktree?.({\n reservationId: record.id,\n parentSessionId: record.parentSessionId,\n signal,\n });\n if (child === undefined || child.sessionId !== record.id)\n throw new Error('Automatic worktree provisioning did not create the reserved session.');\n return child;\n } catch (error) {\n if (error instanceof SessionMcpConversationError) throw error;\n throw new SessionMcpConversationError(\n 'SESSION_WORKTREE_PROVISION_FAILED',\n 'An automatic worktree could not be created for this conversation. Resolve the host Git setup, then retry.',\n record.id,\n );\n }\n })();\n provision.set(record.id, setup);\n try {\n return await setup;\n } finally {\n if (provision.get(record.id) === setup) provision.delete(record.id);\n }\n };\n const publicRoutes = async (request: Request): Promise<Response | undefined> => {\n const url = new URL(request.url);\n const configuredOrigin = origin();\n const mcpMatch = SESSION_MCP_PATTERN.exec(url.pathname);\n if (mcpMatch !== null) {\n if (configuredOrigin === undefined) return json(503, { error: 'Session MCP public origin is unavailable.' });\n const workspaceId = decodeURIComponent(mcpMatch[1]);\n const sessionId = decodeURIComponent(mcpMatch[2]);\n const exactPath = sessionPath(workspaceId, sessionId);\n if (url.pathname !== exactPath || url.search !== '') return json(404, { error: 'Not found.' });\n const audience = `${configuredOrigin}${exactPath}`;\n const handlers = target(workspaceId, sessionId) === undefined ? undefined : incarnations.get(sessionId)?.handlers;\n const handler =\n handlers?.get(audience) ??\n createSessionMcpHttpHandler({\n audience,\n authorization,\n onNotice: options.onNotice,\n resolveConversation: async (grant, digest, reserve, signal) => {\n const store = requireStore();\n let record = reserve\n ? store.reserve(grant.clientId, sessionId, workspaceId, digest)\n : store.find(grant.clientId, sessionId, digest);\n if (!record || record.state === 'closed')\n throw new SessionMcpConversationError(\n 'SESSION_UNAVAILABLE',\n 'This conversation binding is unavailable. It will not be recreated automatically.',\n );\n if (record.state === 'pending') {\n publishPending();\n await provisionConversationWorktree(record, signal);\n record = store.find(grant.clientId, sessionId, digest);\n if (!record || record.state !== 'bound')\n throw new SessionMcpConversationError(\n 'SESSION_WORKTREE_PROVISION_FAILED',\n 'The automatic worktree setup did not complete. Retry after resolving the host Git setup.',\n );\n }\n const child = record.workspaceId === undefined ? undefined : target(record.workspaceId, record.id);\n if (\n !child ||\n child.session.parentSessionId !== sessionId ||\n executionDirectory(child.session.cwd) !== record.cwd ||\n !fs.existsSync(child.session.cwd)\n )\n throw new SessionMcpConversationError(\n 'SESSION_UNAVAILABLE',\n 'The bound session is unavailable. Resume it in DoomPi; no other session will be used.',\n record.id,\n );\n return { sessionId: record.id, generation: child.generation, toolSurface: child.session.host.mcpSurface };\n },\n onVerified: (grant) => {\n if (options.registrationStore === undefined) return;\n const active = target(workspaceId, sessionId);\n if (active === undefined || grant.sessionGeneration !== active.generation || grant.clientId === '') {\n throw new Error('The session grant is no longer active.');\n }\n if (authorization.readClient(grant.clientId)?.tokenEndpointAuthMethod === 'api_key') return;\n const registration = authorization.persistentRegistration(grant.clientId, workspaceId, Date.now());\n if (registration === undefined || !options.registrationStore.save(registration)) {\n throw new Error('The verified Session MCP client could not be saved.');\n }\n },\n resourceMetadataUrl: `${configuredOrigin}/.well-known/oauth-protected-resource${exactPath}`,\n resolveSession: (grantedSessionId) => {\n if (grantedSessionId !== sessionId) return undefined;\n const resolved = target(workspaceId, sessionId);\n return resolved === undefined\n ? undefined\n : { sessionId, generation: resolved.generation, toolSurface: resolved.session.host.mcpSurface };\n },\n });\n handlers?.set(audience, handler);\n return handler(new Request(audience, request));\n }\n\n const protectedMatch = PROTECTED_RESOURCE_PATTERN.exec(url.pathname);\n if (protectedMatch !== null && request.method === 'GET') {\n if (configuredOrigin === undefined) return json(503, { error: 'Session MCP public origin is unavailable.' });\n return json(200, {\n resource: `${configuredOrigin}${protectedMatch[1]}`,\n authorization_servers: [configuredOrigin],\n bearer_methods_supported: ['header'],\n });\n }\n if (url.pathname === AUTHORIZATION_SERVER_DISCOVERY && request.method === 'GET') {\n if (configuredOrigin === undefined) return json(503, { error: 'Session MCP public origin is unavailable.' });\n return json(200, {\n issuer: configuredOrigin,\n authorization_endpoint: `${configuredOrigin}${AUTHORIZE_ROUTE}`,\n token_endpoint: `${configuredOrigin}${TOKEN_ROUTE}`,\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: ['client_secret_post'],\n });\n }\n if (url.pathname === AUTHORIZE_ROUTE && request.method === 'GET') {\n if (configuredOrigin === undefined) return json(503, { error: 'Session MCP public origin is unavailable.' });\n const clientId = url.searchParams.get('client_id') ?? '';\n const client = authorization.readClient(clientId);\n const binding = authorization.readAuthorizationBinding(clientId);\n const redirectUri = url.searchParams.get('redirect_uri') ?? '';\n if (\n client === undefined ||\n client.tokenEndpointAuthMethod !== 'client_secret_post' ||\n binding === undefined ||\n redirectUri !== client.redirectUri\n ) {\n return oauthError(new SessionMcpOAuthError('invalid_client', 'Unknown client or callback.'));\n }\n const callback = new URL(client.redirectUri);\n const state = url.searchParams.get('state');\n try {\n const boundSession = options.headlessHub.session(binding.sessionId);\n const boundIncarnation = incarnations.get(binding.sessionId);\n const expectedAudience =\n boundSession?.workspaceId === undefined\n ? undefined\n : `${configuredOrigin}${sessionPath(boundSession.workspaceId, binding.sessionId)}`;\n if (\n expectedAudience === undefined ||\n binding.audience !== expectedAudience ||\n boundIncarnation?.host !== boundSession?.host ||\n binding.sessionGeneration !== boundIncarnation?.generation\n ) {\n authorization.revokeClient(clientId);\n throw new SessionMcpOAuthError('invalid_grant', 'The preauthorized session is no longer active.');\n }\n if (url.searchParams.get('response_type') !== 'code')\n throw new SessionMcpOAuthError('invalid_request', 'Only the code response type is supported.');\n if (url.searchParams.get('resource') !== null && url.searchParams.get('resource') !== binding.audience)\n throw new SessionMcpOAuthError('invalid_request', 'The requested resource is not preauthorized.');\n const code = authorization.issueAuthorizationCode({\n clientId,\n redirectUri,\n codeChallenge: url.searchParams.get('code_challenge') ?? '',\n codeChallengeMethod: url.searchParams.get('code_challenge_method') as 'S256',\n });\n callback.searchParams.set('code', code.code);\n } catch (error) {\n callback.searchParams.set('error', error instanceof SessionMcpOAuthError ? error.code : 'invalid_request');\n callback.searchParams.set(\n 'error_description',\n error instanceof Error ? error.message : 'The OAuth request was invalid.',\n );\n }\n if (state !== null) callback.searchParams.set('state', state);\n return new Response(null, { status: 302, headers: { location: callback.href, 'cache-control': 'no-store' } });\n }\n if (url.pathname === TOKEN_ROUTE && request.method === 'POST') {\n try {\n const form = await request.formData();\n const grantType = formString(form, 'grant_type');\n const clientId = formString(form, 'client_id');\n const clientSecret = formString(form, 'client_secret');\n const result =\n grantType === 'authorization_code'\n ? authorization.exchangeToken({\n grantType,\n clientId,\n clientSecret,\n code: formString(form, 'code'),\n redirectUri: formString(form, 'redirect_uri'),\n codeVerifier: formString(form, 'code_verifier'),\n })\n : grantType === 'refresh_token'\n ? authorization.exchangeToken({\n grantType,\n clientId,\n clientSecret,\n refreshToken: formString(form, 'refresh_token'),\n })\n : (() => {\n throw new SessionMcpOAuthError('unsupported_grant_type', 'The grant type is not supported.');\n })();\n return json(200, {\n access_token: result.accessToken,\n refresh_token: result.refreshToken,\n token_type: result.tokenType,\n expires_in: result.expiresIn,\n });\n } catch (error) {\n return oauthError(error);\n }\n }\n return undefined;\n };\n\n publishPending();\n return {\n reservations,\n closeSessionBinding(sessionId) {\n options.conversationStore?.closeSession(sessionId);\n publishPending();\n },\n handlePublic: publicRoutes,\n async handleHost(request) {\n const url = new URL(request.url);\n origin();\n const configMatch = SESSION_MCP_CONFIG_PATTERN.exec(url.pathname);\n const match = SESSION_MCP_CLIENTS_PATTERN.exec(url.pathname);\n const conversationMatch = SESSION_MCP_CONVERSATIONS_PATTERN.exec(url.pathname);\n if (configMatch === null && match === null && conversationMatch === null) return undefined;\n const routeMatch = configMatch ?? match ?? conversationMatch!;\n const workspaceId = decodeURIComponent(routeMatch[1]);\n const sessionId = decodeURIComponent(routeMatch[2]);\n const clientId = match?.[3] === undefined ? undefined : decodeURIComponent(match[3]);\n const resolved = target(workspaceId, sessionId);\n if (resolved === undefined) return json(404, { error: 'Session not found.' });\n if (conversationMatch !== null) {\n const id = conversationMatch[3] === undefined ? undefined : decodeURIComponent(conversationMatch[3]);\n try {\n if (request.method === 'GET' && id === undefined)\n return json(200, {\n conversations: requireStore()\n .list()\n .filter((record) => record.parentSessionId === sessionId && record.state !== 'closed')\n .map(({ conversationDigest: _digest, clientId: _clientId, ...record }) => record),\n });\n if (id !== undefined && request.method === 'DELETE') {\n const record = requireStore().get(id, sessionId);\n if (record.state !== 'pending' || options.headlessHub.session(id))\n return json(409, { error: 'Remove the existing session instead.' });\n requireStore().closeSession(id);\n publishPending();\n return json(200, { ok: true });\n }\n if (id !== undefined && request.method === 'POST') {\n const body: unknown = await request.json();\n if (typeof body !== 'object' || body === null || !('cwd' in body) || typeof body.cwd !== 'string')\n return json(400, { error: 'An execution directory is required.' });\n return json(200, { session: await setupDirectory(id, sessionId, body.cwd) });\n }\n return json(405, { error: 'Method not allowed.' });\n } catch (error) {\n return json(409, {\n error:\n error instanceof SessionMcpConversationError\n ? error.message\n : 'Session setup failed. Recover the existing setup before retrying.',\n ...(error instanceof SessionMcpConversationError ? { code: error.code } : {}),\n });\n }\n }\n if (configMatch !== null) {\n if (request.method !== 'GET') return json(405, { error: 'Method not allowed.' });\n const configuredOrigin = origin();\n if (configuredOrigin === undefined) return json(503, { error: 'Session MCP public origin is unavailable.' });\n let surface;\n try {\n surface = resolved.session.host.mcpSurface.readSurface();\n } catch {\n return json(409, { error: 'The session capability surface is not ready.' });\n }\n return json(200, {\n audience: `${configuredOrigin}${sessionPath(workspaceId, sessionId)}`,\n authorizationEndpoint: `${configuredOrigin}${AUTHORIZE_ROUTE}`,\n tokenEndpoint: `${configuredOrigin}${TOKEN_ROUTE}`,\n tools: surface.tools.map(({ name, label, description }) => ({ name, label, description })),\n skills: surface.skills,\n });\n }\n if (clientId === undefined && request.method === 'GET') {\n return json(200, {\n clients: authorization.listClients().flatMap((client) => {\n const binding = authorization.readAuthorizationBinding(client.clientId);\n return binding?.sessionId === sessionId ? [publicClient(client, binding)] : [];\n }),\n });\n }\n if (clientId === undefined && request.method === 'POST') {\n const configuredOrigin = origin();\n if (configuredOrigin === undefined) return json(503, { error: 'Session MCP public origin is unavailable.' });\n let body: unknown;\n try {\n body = await request.json();\n } catch {\n return json(400, { error: 'Invalid client request.' });\n }\n if (typeof body !== 'object' || body === null) return json(400, { error: 'Invalid client request.' });\n const input = body as Record<string, unknown>;\n if (!options.conversationStore || !options.registrationStore)\n return json(503, { error: 'Mandatory conversation routing requires persistent host state.' });\n if (input.routing !== undefined && input.routing !== 'session' && input.routing !== 'conversation')\n return json(400, { error: 'Session MCP routing is not supported.' });\n const routing = sessionMcpRouting(input.routing);\n const requestedScope = input.scope;\n if (requestedScope !== undefined && requestedScope !== 'session' && requestedScope !== 'restricted') {\n return json(400, { error: 'Authorization scope is not supported.' });\n }\n const scope = requestedScope === 'session' ? 'session' : 'restricted';\n const hasTools = Object.hasOwn(input, 'tools');\n const hasSkills = Object.hasOwn(input, 'skills');\n const tools = parseStringArray(input.tools);\n const skills = parseStringArray(input.skills);\n const apiKey = input.authMethod === 'api_key';\n if (input.authMethod !== undefined && input.authMethod !== 'oauth' && !apiKey)\n return json(400, { error: 'Authentication method is not supported.' });\n if (apiKey && (scope !== 'session' || Object.hasOwn(input, 'redirectUri')))\n return json(400, { error: 'API keys require session scope and no redirectUri.' });\n if (!apiKey && typeof input.redirectUri !== 'string') return json(400, { error: 'A redirectUri is required.' });\n if (scope === 'session' && (hasTools || hasSkills)) {\n return json(400, { error: 'Session scope cannot include capability grants.' });\n }\n if (scope === 'restricted' && (typeof input.name !== 'string' || !tools || !skills)) {\n return json(400, { error: 'Restricted scope requires a name, tools, and skills.' });\n }\n if (scope === 'restricted') {\n let surface;\n try {\n surface = resolved.session.host.mcpSurface.readSurface();\n } catch {\n return json(409, { error: 'The session capability surface is not ready.' });\n }\n const activeTools = new Set(surface.tools.map((tool) => tool.name));\n const activeSkills = new Set(surface.skills.map((skill) => skill.name));\n if (tools!.some((name) => !activeTools.has(name)) || skills!.some((name) => !activeSkills.has(name)))\n return json(400, { error: 'Every requested grant must be active in the session.' });\n }\n try {\n const client = authorization.createClient(\n apiKey\n ? { name: typeof input.name === 'string' ? input.name : 'API key', authMethod: 'api_key' }\n : {\n name: scope === 'session' ? defaultClientName(configuredOrigin) : (input.name as string),\n redirectUri: input.redirectUri as string,\n },\n );\n const audience = `${configuredOrigin}${sessionPath(workspaceId, sessionId)}`;\n try {\n const binding =\n scope === 'session'\n ? authorization.createAuthorizationBinding({\n clientId: client.clientId,\n sessionId,\n sessionGeneration: resolved.generation,\n audience,\n scope: 'session',\n routing,\n })\n : authorization.createAuthorizationBinding({\n clientId: client.clientId,\n sessionId,\n sessionGeneration: resolved.generation,\n audience,\n scope: 'restricted',\n routing,\n tools: [...new Set(tools!)],\n skills: [...new Set(skills!)],\n });\n if (apiKey) {\n const registration = authorization.persistentRegistration(client.clientId, workspaceId, Date.now());\n if (registration === undefined || !options.registrationStore.save(registration)) {\n authorization.revokeClient(client.clientId);\n return json(500, { error: 'API key could not be saved.' });\n }\n }\n return json(201, { client: { ...publicClient(client, binding), clientSecret: client.clientSecret } });\n } catch (error) {\n authorization.revokeClient(client.clientId);\n throw error;\n }\n } catch (error) {\n return oauthError(error);\n }\n }\n if (clientId !== undefined && request.method === 'DELETE') {\n const binding = authorization.readAuthorizationBinding(clientId);\n if (binding?.sessionId !== sessionId) return json(404, { error: 'Client not found.' });\n if (options.registrationStore !== undefined && !options.registrationStore.remove(clientId)) {\n return json(500, { error: 'Client revocation could not be saved.' });\n }\n authorization.revokeClient(clientId);\n options.conversationStore?.closeClient(clientId);\n publishPending();\n return json(200, { ok: true });\n }\n return json(405, { error: 'Method not allowed.' });\n },\n close() {\n unsubscribe();\n for (const [sessionId, incarnation] of incarnations) revokeIncarnation(sessionId, incarnation.generation);\n incarnations.clear();\n },\n };\n}\n\nexport function isPublicSessionMcpRoute(method: string, pathname: string): boolean {\n return (\n SESSION_MCP_PATTERN.test(pathname) ||\n (method === 'GET' && PROTECTED_RESOURCE_PATTERN.test(pathname)) ||\n (method === 'GET' && (pathname === AUTHORIZATION_SERVER_DISCOVERY || pathname === AUTHORIZE_ROUTE)) ||\n (method === 'POST' && pathname === TOKEN_ROUTE)\n );\n}\n\nexport function isSessionMcpHostRoute(pathname: string): boolean {\n return (\n SESSION_MCP_CONFIG_PATTERN.test(pathname) ||\n SESSION_MCP_CLIENTS_PATTERN.test(pathname) ||\n SESSION_MCP_CONVERSATIONS_PATTERN.test(pathname)\n );\n}\n"],"mappings":"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"}
|
|
@@ -306,6 +306,7 @@ function createSessionMcpRoutes(options) {
|
|
|
306
306
|
if (options.registrationStore === void 0) return;
|
|
307
307
|
const active = target(workspaceId, sessionId);
|
|
308
308
|
if (active === void 0 || grant.sessionGeneration !== active.generation || grant.clientId === "") throw new Error("The session grant is no longer active.");
|
|
309
|
+
if (authorization.readClient(grant.clientId)?.tokenEndpointAuthMethod === "api_key") return;
|
|
309
310
|
const registration = authorization.persistentRegistration(grant.clientId, workspaceId, Date.now());
|
|
310
311
|
if (registration === void 0 || !options.registrationStore.save(registration)) throw new Error("The verified Session MCP client could not be saved.");
|
|
311
312
|
},
|
|
@@ -350,7 +351,7 @@ function createSessionMcpRoutes(options) {
|
|
|
350
351
|
const client = authorization.readClient(clientId);
|
|
351
352
|
const binding = authorization.readAuthorizationBinding(clientId);
|
|
352
353
|
const redirectUri = url.searchParams.get("redirect_uri") ?? "";
|
|
353
|
-
if (client === void 0 || binding === void 0 || redirectUri !== client.redirectUri) return oauthError(new SessionMcpOAuthError("invalid_client", "Unknown client or callback."));
|
|
354
|
+
if (client === void 0 || client.tokenEndpointAuthMethod !== "client_secret_post" || binding === void 0 || redirectUri !== client.redirectUri) return oauthError(new SessionMcpOAuthError("invalid_client", "Unknown client or callback."));
|
|
354
355
|
const callback = new URL(client.redirectUri);
|
|
355
356
|
const state = url.searchParams.get("state");
|
|
356
357
|
try {
|
|
@@ -504,7 +505,10 @@ function createSessionMcpRoutes(options) {
|
|
|
504
505
|
const hasSkills = Object.hasOwn(input, "skills");
|
|
505
506
|
const tools = parseStringArray(input.tools);
|
|
506
507
|
const skills = parseStringArray(input.skills);
|
|
507
|
-
|
|
508
|
+
const apiKey = input.authMethod === "api_key";
|
|
509
|
+
if (input.authMethod !== void 0 && input.authMethod !== "oauth" && !apiKey) return json(400, { error: "Authentication method is not supported." });
|
|
510
|
+
if (apiKey && (scope !== "session" || Object.hasOwn(input, "redirectUri"))) return json(400, { error: "API keys require session scope and no redirectUri." });
|
|
511
|
+
if (!apiKey && typeof input.redirectUri !== "string") return json(400, { error: "A redirectUri is required." });
|
|
508
512
|
if (scope === "session" && (hasTools || hasSkills)) return json(400, { error: "Session scope cannot include capability grants." });
|
|
509
513
|
if (scope === "restricted" && (typeof input.name !== "string" || !tools || !skills)) return json(400, { error: "Restricted scope requires a name, tools, and skills." });
|
|
510
514
|
if (scope === "restricted") {
|
|
@@ -519,13 +523,16 @@ function createSessionMcpRoutes(options) {
|
|
|
519
523
|
if (tools.some((name) => !activeTools.has(name)) || skills.some((name) => !activeSkills.has(name))) return json(400, { error: "Every requested grant must be active in the session." });
|
|
520
524
|
}
|
|
521
525
|
try {
|
|
522
|
-
const client = authorization.createClient({
|
|
526
|
+
const client = authorization.createClient(apiKey ? {
|
|
527
|
+
name: typeof input.name === "string" ? input.name : "API key",
|
|
528
|
+
authMethod: "api_key"
|
|
529
|
+
} : {
|
|
523
530
|
name: scope === "session" ? defaultClientName(configuredOrigin) : input.name,
|
|
524
531
|
redirectUri: input.redirectUri
|
|
525
532
|
});
|
|
526
533
|
const audience = `${configuredOrigin}${sessionPath(workspaceId, sessionId)}`;
|
|
527
534
|
try {
|
|
528
|
-
|
|
535
|
+
const binding = scope === "session" ? authorization.createAuthorizationBinding({
|
|
529
536
|
clientId: client.clientId,
|
|
530
537
|
sessionId,
|
|
531
538
|
sessionGeneration: resolved.generation,
|
|
@@ -541,7 +548,18 @@ function createSessionMcpRoutes(options) {
|
|
|
541
548
|
routing,
|
|
542
549
|
tools: [...new Set(tools)],
|
|
543
550
|
skills: [...new Set(skills)]
|
|
544
|
-
})
|
|
551
|
+
});
|
|
552
|
+
if (apiKey) {
|
|
553
|
+
const registration = authorization.persistentRegistration(client.clientId, workspaceId, Date.now());
|
|
554
|
+
if (registration === void 0 || !options.registrationStore.save(registration)) {
|
|
555
|
+
authorization.revokeClient(client.clientId);
|
|
556
|
+
return json(500, { error: "API key could not be saved." });
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
return json(201, { client: {
|
|
560
|
+
...publicClient(client, binding),
|
|
561
|
+
clientSecret: client.clientSecret
|
|
562
|
+
} });
|
|
545
563
|
} catch (error) {
|
|
546
564
|
authorization.revokeClient(client.clientId);
|
|
547
565
|
throw error;
|