/** * MCP SSE/HTTP Transport — Phase 6 Part 1 * * Makes the MCP bridge network-accessible over HTTP + Server-Sent Events so * any MCP-compatible AI (Claude Desktop, Cursor, Cline, etc.) can connect * to Network-AI from outside the process. * * Architecture: * * External AI agent * │ POST /mcp (JSON-RPC 2.0 request) * ▼ * McpSseServer (HTTP, port 3001) * │ handleRPC() * ▼ * McpCombinedBridge * ├── blackboard tools (read/write/list/delete/exists) * ├── extended tools (budget/token/audit) * └── control tools (config/agent/fsm) * * External AI agent * │ GET /sse (SSE connection — server pushes events) * ▼ * McpSseServer broadcasts events to all connected clients * * Zero external dependencies — uses Node.js built-in `node:http` only. * * @module mcp-transport-sse * @version 1.0.0 */ import type { McpTransport, McpJsonRpcRequest, McpJsonRpcResponse } from './mcp-bridge'; import { McpBlackboardBridge } from './mcp-bridge'; import type { MCPToolDefinition, BlackboardToolResult } from './mcp-blackboard-tools'; /** * Any object that provides MCP tools can implement this interface and be * registered with `McpCombinedBridge`. */ export interface McpToolProvider { getDefinitions(): MCPToolDefinition[]; call(toolName: string, args: Record): Promise; } /** * Aggregates multiple `McpToolProvider` instances into a single MCP bridge. * * Handles `tools/list` by merging all definitions, and routes `tools/call` * to the first provider that owns the requested tool name. * * @example * ```typescript * const combined = new McpCombinedBridge('network-ai'); * combined.register(new McpBlackboardBridgeAdapter(myBridge)); * combined.register(new ExtendedMcpTools({ budget })); * combined.register(new ControlMcpTools({ config, orchestrator })); * * const server = new McpSseServer(combined, { port: 3001, secret: process.env['NETWORK_AI_MCP_SECRET']! }); * await server.listen(); * ``` */ export declare class McpCombinedBridge { readonly name: string; private readonly _providers; private readonly _toolIndex; constructor(name?: string); /** * Register a tool provider. Tools names must be globally unique across all * registered providers — duplicate names are silently overwritten with the * latest registration. */ register(provider: McpToolProvider): void; /** All tool definitions across every registered provider. */ allDefinitions(): MCPToolDefinition[]; /** * Handle a single JSON-RPC 2.0 request. Never rejects — errors are encoded * in the response `error` field. */ handleRPC(request: McpJsonRpcRequest): Promise; private _ok; private _error; } /** * Wraps an existing `McpBlackboardBridge` as a `McpToolProvider` so it can be * registered with `McpCombinedBridge` alongside extended and control tools. */ export declare class McpBlackboardBridgeAdapter implements McpToolProvider { private readonly bridge; constructor(bridge: McpBlackboardBridge); getDefinitions(): MCPToolDefinition[]; call(toolName: string, args: Record): Promise; } /** Options for `McpSseServer`. */ export interface McpSseServerOptions { /** TCP port to listen on. Defaults to `3001`. */ port?: number; /** * Hostname to bind to. Defaults to `'127.0.0.1'` (loopback only). * Set to `'0.0.0.0'` to bind all interfaces, but you MUST also supply * a `secret` to gate unauthenticated access. */ host?: string; /** Heartbeat interval in ms. Defaults to `15000`. Set to `0` to disable. */ heartbeatMs?: number; /** * Shared secret for bearer-token authentication. * When set, every POST /mcp and GET /sse request must supply * `Authorization: Bearer ` or receive a 401 response. * Load from the `NETWORK_AI_MCP_SECRET` environment variable in production. */ secret?: string; } /** * HTTP server that exposes a `McpCombinedBridge` (or any object with * `handleRPC`) over two endpoints: * * - `GET /sse` — Server-Sent Events stream; sends initial `endpoint` event * - `POST /mcp` — Receive JSON-RPC 2.0 requests, return responses * - `GET /health` — Health check returning `{ status: 'ok', bridge, clients }` * - `GET /tools` — List all available tools as JSON * * No external packages required — built on Node.js `node:http`. */ export declare class McpSseServer { private readonly _bridge; private readonly _opts; private _server; private readonly _sseClients; constructor(bridge: { handleRPC(req: McpJsonRpcRequest): Promise; name?: string; allDefinitions?: () => MCPToolDefinition[]; }, options?: McpSseServerOptions); /** Start listening. Resolves when the server is ready. */ listen(): Promise; /** Stop the server and close all SSE streams. */ close(): Promise; /** Current TCP port (useful when port was auto-assigned). */ get port(): number; /** Number of currently connected SSE clients. */ get clientCount(): number; /** * Broadcast an event to every connected SSE client. * Useful for pushing agent status updates, budget alerts, etc. */ broadcast(eventName: string, data: unknown): void; /** * Returns true when the request carries a valid `Authorization: Bearer ` * header. Fails closed: an empty or missing secret always returns false. * (CWE-306 / CWE-862 — incomplete fix guard: empty secret must never grant access.) */ private _isAuthorized; private _unauthorized; private _handleRequest; private _handleSse; private _handlePost; private _handleHealth; private _handleTools; } /** * `McpTransport` implementation that sends JSON-RPC requests to a remote * `McpSseServer` via HTTP POST. * * Use this when the MCP client and server are in different processes or * machines. Pairs with `McpBridgeClient` as a drop-in replacement for * `McpInProcessTransport`. * * @example * ```typescript * import { McpBridgeClient } from 'network-ai'; * import { McpSseTransport } from 'network-ai'; * * const transport = new McpSseTransport('http://localhost:3001', process.env['NETWORK_AI_MCP_SECRET']!); * const client = new McpBridgeClient(transport); * * const tools = await client.listTools(); * const result = await client.callTool('blackboard_read', { key: 'status', agent_id: 'my-agent' }); * ``` */ export declare class McpSseTransport implements McpTransport { private readonly _postUrl; private readonly _secret; private _idCounter; /** * @param baseUrl Base URL of the `McpSseServer`, e.g. `'http://localhost:3001'`. * The transport will POST to `/mcp`. * @param secret Bearer token matching `McpSseServerOptions.secret`. Required * because `McpSseServer` now rejects all requests when no secret * is configured. Pass the same value set on the server. */ constructor(baseUrl: string, secret?: string); /** Send a JSON-RPC request and wait for the response. */ send(request: McpJsonRpcRequest): Promise; /** Generate a unique request ID. */ nextId(): number; } //# sourceMappingURL=mcp-transport-sse.d.ts.map