# mcp-server _(no submodules)_ ## Purpose `MCPServer` is the host-side surface of the Model Context Protocol. Owners register tools, resources, prompts, and roots; the server then exposes them to a remote `MCPClient` (or any spec-compliant peer) over the JSON-RPC dispatcher and a pluggable transport. Every public method documented here either mutates the registration tables, configures a hook (authorize, sampling, completion, log level), or drives the dispatcher (start / stop / direct invocation). ## Features - Register / unregister tools, resources, resource templates, prompts, and roots — every collision raises `MCPError(DUPLICATE_REGISTRATION)`. - Lifecycle: `start(transport)` attaches a JSON-RPC dispatcher and registers handlers for every MCP method; `stop(reason?)` rejects pending requests and aborts in-flight handlers. - Authorize hook: `setAuthorizer(fn)` gates every method; rejection yields `-32001` mapped to `MCPError(UNAUTHORIZED)`. - Sampling + completion providers — surfaces are pluggable so consumers can wire LLM backends. - Progress + abort: tool handlers receive `progress(value, total?)` and an `AbortSignal`; clients see `notifications/progress` and `notifications/cancelled` over the wire. - Resource subscriptions: `markResourceUpdated(uri)` fans out `notifications/resources/updated` to subscribed peers. - Direct invocation API (`invokeTool`, `readResource`, `getPromptMessages`) — useful in tests and in-process callers. ## Usage: Basic Example ```ts import { InMemoryTransport, MCPServer } from "@suaveplan/mcp"; const server = new MCPServer({ info: { name: "demo", version: "0.1.0" } }); server.registerTool({ name: "echo", description: "Echo back the input", inputSchema: { type: "object", properties: { msg: { type: "string" } } }, async handler({ msg }) { return { content: [{ type: "text", text: String(msg) }] }; }, }); const [a, b] = InMemoryTransport.pair(); await server.start(a); ``` ## Usage: Advanced Example ```ts import { MCPServer } from "@suaveplan/mcp"; import z from "zod"; const server = new MCPServer({ info: { name: "admin", version: "0.1.0" } }); server.setAuthorizer(async (req) => req.method !== "tools/call" || hasPermission(req.params)); server.registerTool({ name: "deploy", description: "Deploy a release", inputSchema: { type: "object", properties: { tag: { type: "string" } }, required: ["tag"] }, paramsSchema: z.object({ tag: z.string().min(1) }), async handler(args, ctx) { for (let p = 0; p < 100 && !ctx.signal.aborted; p += 10) { await sleep(50); ctx.progress(p, 100); } return { content: [{ type: "text", text: `Deployed ${(args as { tag: string }).tag}` }] }; }, }); server.registerResource({ uri: "config://current", name: "Active configuration", mimeType: "application/json", async handler() { return { uri: "config://current", mimeType: "application/json", text: JSON.stringify(currentConfig) }; }, }); ``` ## API Reference - `new MCPServer(options: MCPServerOptions)`. - Registration: `registerTool`, `unregisterTool`, `registerResource`, `unregisterResource`, `registerResourceTemplate`, `unregisterResourceTemplate`, `registerPrompt`, `unregisterPrompt`, `registerRoot`, `unregisterRoot`. - Lists: `listTools`, `listResources`, `listResourceTemplates`, `listPrompts`, `listRoots`. - Hooks: `setAuthorizer`, `setSamplingProvider`, `setCompletionProvider`, `setLogLevel`. - Direct API: `invokeTool`, `readResource`, `getPromptMessages`, `createSamplingMessage`, `listClientRoots`, `emitLog`. - Lifecycle: `start(transport)`, `stop(reason?)`, `running`, `initialized`, `getInfo()`. - `events: EventBus` — subscribe to connection / request / notification lifecycle. ## Implementation Notes `start` builds a `paramSchemas` map keyed by method name so the dispatcher validates incoming params before invoking the handler — invalid params respond with JSON-RPC `-32602` mapped to `MCPError(INVALID_PARAMS)`. List-changed notifications fan out from registration mutators only when a dispatcher is attached; the rejection is swallowed because the transport may have closed between the registration and the notify. `invokeTool` wraps any thrown error into a `MCPToolResult` with `isError: true` so JSON-RPC level errors are reserved for protocol failures, not tool-handler failures (matching the MCP spec convention). `createSamplingMessage` and `listClientRoots` use the dispatcher to call out to the connected client — these methods exist on the server side because MCP's sampling and roots flows are client-side capabilities that the server invokes.