first commit
This commit is contained in:
@@ -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.
|
||||
Reference in New Issue
Block a user