/** * Streamable HTTP Transport for MCP * * Implements the MCP Streamable HTTP transport specification (2025-03-26). * This allows standard MCP clients (like Claude Desktop) to connect to Beam. * * Endpoint: /mcp * - POST: Client sends JSON-RPC requests, server responds with JSON or SSE * - GET: Opens SSE stream for server-initiated messages * * Configuration Schema (SEP-1596 inspired): * - Returns configurationSchema in initialize response * - Uses JSON Schema for rich UI generation (dropdowns, file pickers, etc.) * - beam/configure tool for submitting configuration * - beam/browse tool for server filesystem browsing * * @see https://modelcontextprotocol.io/specification/2025-03-26/basic/transports */ import type { IncomingHttpHeaders, IncomingMessage, ServerResponse } from 'http'; import type { JSONRPCRequest, AnyPhotonInfo, PhotonInstance, ExternalMCPInfo } from './types.js'; interface MCPSession { id: string; initialized: boolean; createdAt: Date; lastActivity: Date; sseResponse?: ServerResponse; sseOpenedAt?: Date; remoteAddress?: string; userAgent?: string; isBeam?: boolean; clientInfo?: { name: string; version: string; }; clientCapabilities?: Record; clientProfile?: ClientProfile; /** Tracked instance name for daemon drift recovery */ instanceName?: string; /** * If this session presented a valid `Mcp-Claim-Code` on initialize, * photons outside this directory are filtered out of tools/list. When * unset the session has unscoped (full) access — the default. * See src/daemon/claims.ts for the full claim-code story. */ claimScopeDir?: string; } export type MCPProtocolMode = 'legacy-sessionful' | 'stateless'; export interface ClientProfile { protocolVersion: string; clientName?: string; clientVersion?: string; mode: MCPProtocolMode; capabilities: { tools: boolean; prompts: boolean; resources: boolean; sampling: boolean; mcpApps: boolean; tasks: 'none' | 'legacy-core' | 'extension'; cacheMetadata: boolean; }; quirks: { unnamespacedToolNames: boolean; prefersOpenAIAppMetadata: boolean; requiresLegacyInitializeConfigSchema: boolean; }; } export interface PhotonRequestContext { requestId?: string | number; protocolVersion: string; transport: 'streamable-http'; client: ClientProfile; caller?: CallerInfo; traceparent?: string; legacyTransportSessionId?: string; appSessionId?: string; appSessionSource: 'explicit-meta' | 'explicit-argument' | 'header' | 'legacy-mcp-session' | 'caller-default' | 'anonymous-default'; scopeDir?: string; } declare function resolveClientProfile(request: JSONRPCRequest | undefined, session: MCPSession, headers: IncomingHttpHeaders): ClientProfile; declare function resolvePhotonRequestContext(input: { request: JSONRPCRequest; session: MCPSession; headers: IncomingHttpHeaders; caller?: CallerInfo; }): PhotonRequestContext; export declare const __streamableHttpTransportInternals: { resolveClientProfile: typeof resolveClientProfile; resolvePhotonRequestContext: typeof resolvePhotonRequestContext; }; /** * Wire a PhotonLoader's `notifyResourceUpdated` channel into the streamable-HTTP * subscription registry. Call once at server start. */ export declare function attachLoaderForResourceUpdates(loader: { setResourceUpdateNotifier: (fn: (uri: string) => void | Promise) => void; }): void; export declare function stopSessionCleanup(): void; interface CallerInfo { id: string; name?: string; anonymous: boolean; scope?: string; claims?: Record; } export interface StreamableHTTPOptions { photons: AnyPhotonInfo[]; photonMCPs: Map; externalMCPs?: ExternalMCPInfo[]; externalMCPClients?: Map; externalMCPSDKClients?: Map; reconnectExternalMCP?: (name: string) => Promise<{ success: boolean; error?: string; }>; loadUIAsset: (photonName: string, uiId: string) => Promise<{ content: string; isPhotonTemplate: boolean; compiled?: import('../tsx-compiler.js').CompiledTsx; } | null>; /** Working directory override (base dir for state/config/cache) */ workingDir?: string; configurePhoton?: (photonName: string, config: Record) => Promise<{ success: boolean; error?: string; }>; reloadPhoton?: (photonName: string) => Promise<{ success: boolean; photon?: any; error?: string; }>; schedulePhotonReload?: (photonName: string) => Promise<{ success: boolean; photon?: any; error?: string; }>; removePhoton?: (photonName: string) => Promise<{ success: boolean; error?: string; }>; updateMetadata?: (photonName: string, methodName: string | null, metadata: Record) => Promise<{ success: boolean; error?: string; }>; generatePhotonHelp?: (photonName: string) => Promise; loader?: { executeTool: (mcp: any, toolName: string, args: any, options?: any) => Promise; }; broadcast?: (message: object) => void; subscriptionManager?: { onClientViewingBoard: (sessionId: string, photon: string, board: string, lastTimestamp?: number) => void; onClientDisconnect: (sessionId: string) => void; }; } /** * Handle MCP Streamable HTTP requests */ export declare function handleStreamableHTTP(req: IncomingMessage, res: ServerResponse, options: StreamableHTTPOptions): Promise; /** * Send a notification to all connected SSE clients * @param method - The notification method name * @param params - Optional parameters for the notification * @param beamOnly - If true, only send to Beam clients (clientInfo.name === "beam") */ export declare function broadcastNotification(method: string, params?: Record, beamOnly?: boolean): void; /** * Send a notification to Beam clients only */ export declare function broadcastToBeam(method: string, params?: Record): void; /** * Get count of active sessions (for debugging) */ export declare function getActiveSessionCount(): { total: number; beam: number; }; /** * Send a notification to a specific session by ID * Used for replaying missed events on reconnect */ export declare function sendToSession(sessionId: string, method: string, params?: Record): boolean; /** * Request elicitation from the frontend for an external MCP. * This is used when external MCP servers send elicitation/create requests. * * @param mcpName - Name of the external MCP requesting elicitation * @param request - The elicitation request params from the MCP server * @returns Promise resolving to the user's response */ export declare function requestExternalElicitation(mcpName: string, request: { mode: 'form' | 'url'; message: string; requestedSchema?: any; url?: string; }): Promise<{ action: 'accept' | 'decline' | 'cancel'; content?: any; }>; export {}; //# sourceMappingURL=streamable-http-transport.d.ts.map