import http from "node:http"; import http2 from "node:http2"; import { z } from "zod"; import type { AuthContext } from "@grackle-ai/auth"; import type { ToolDefinition } from "./tool-registry.js"; /** * Pushes an MCP Apps widget render event into a session's stream. Injected by the * server (wraps `@grackle-ai/core`'s `publishWidgetEvent`). Declared structurally * to avoid widening this package's runtime dependencies onto core. */ export type PublishWidgetEvent = (sessionId: string, payload: { resourceUri: string; toolName: string; html: string; rendererKind?: string; csp?: unknown; toolInput?: Record; toolResult?: unknown; widgetId?: string; version?: number; }) => void; /** * Emits a `document.show` domain event for a session so the UI opens a read-only * live view of a file (#1396 live docs v0). Injected by the server (wraps * `@grackle-ai/core`'s `publishDocumentShow`). Declared structurally to avoid * widening this package's runtime dependencies onto core. */ export type PublishDocumentShow = (sessionId: string, payload: { uri: string; }) => void; /** Options for creating an MCP server. */ export interface McpServerOptions { /** Host address to bind the MCP server to. */ bindHost: string; /** Port for the MCP server to listen on. */ mcpPort: number; /** Port of the co-located gRPC server for backend calls. */ grpcPort: number; /** API key used for authenticating both inbound MCP and outbound gRPC requests. */ apiKey: string; /** Base URL of the OAuth authorization server (web server). When set, enables OAuth discovery. */ authorizationServerUrl?: string; /** * Explicit browser-facing MCP origin (GRACKLE_MCP_ORIGIN), e.g. * `https://mcp.example.com`. Used as the trusted asset/CSP origin for * broker-captured widgets so it never depends on the (spoofable) request * `Host` header. When unset, the broker derives it from `bindHost` + `mcpPort`. */ mcpOrigin?: string; /** Optional plugin-contributed tool groups to register alongside built-in tools. */ toolGroups?: ToolDefinition[][]; /** Push MCP Apps widget render events into session streams (in-process, from the server). */ publishWidgetEvent?: PublishWidgetEvent; /** Emit `document.show` domain events when `show_file` runs (in-process, from the server). */ publishDocumentShow?: PublishDocumentShow; /** * Subscribe to "a workspace's promoted-component set changed" signals so the * server can push `tools/list_changed` to that workspace's sessions (#1297). * Called once with a `notify(workspaceId)` callback; returns an unsubscribe. * Broker-mode only (the central server wires it to the domain-event bus); * standalone omits it. */ onComponentChangeSubscribe?: (notify: (workspaceId: string) => void) => () => void; /** * Optional native-TLS material (#1373). When set, the MCP listener is built * with `http2.createSecureServer({ allowHTTP1: true })`, the in-process gRPC * client connects to the co-located server over TLS (trusting `cert` as the * loopback chain anchor, no skip-verify), and the local effective scheme * flips to https for the broker asset origin + the OAuth resource URL * fallback. Reverse-proxy TLS termination is signaled separately via * `GRACKLE_PUBLIC_URL` / `GRACKLE_MCP_ORIGIN` (#1371). */ secureContext?: { cert: Buffer; key: Buffer; }; } /** Create a low-level MCP Server instance with tool handlers wired to the ConnectRPC backend. */ /** * Build the JSON Schema for a dynamic `render_` tool's input from a stored * component `propsSchema` (#1272). Round-trips through zod so the result is always * a valid, normalized JSON Schema. A component with no `propsSchema` (the DB * default `""`) or an unparseable one falls back to a *passthrough* object that * allows arbitrary keys (`additionalProperties: true`) — the call path doesn't * validate props when there's no schema, so the advertised input must be equally * permissive or the tool would look uncallable. */ export declare function dynamicRenderInputSchema(propsSchema: string): ReturnType>; /** * Session ids whose scoped auth context is bound to `workspaceId`. Pure helper so * the promoted-tool `tools/list_changed` fan-out (#1297) is unit-testable without * spinning an http server. */ export declare function sessionIdsForWorkspace(authContexts: ReadonlyMap, workspaceId: string): string[]; /** * Create an HTTP server that serves the MCP Streamable HTTP protocol on `/mcp`. * * The server manages stateful sessions — each MCP client gets its own transport * and Server instance, tracked by session ID. */ export declare function createMcpServer(options: McpServerOptions): http.Server | http2.Http2SecureServer; /** * Resolve the public origin the client reached us on, for embedding into widget * asset URLs. Honors `X-Forwarded-Proto` (reverse proxies) and TLS for the * scheme, and normalizes the Host header through `URL` so a hostile value cannot * inject markup when interpolated into the widget HTML's `