first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,87 @@
# mcp-server
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## 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<MCPEventMap>` — 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.