Files
skills/docs/devtools/tooling/mcp/src/server/mcp-server/mcp-server.md
2026-05-29 08:20:56 +01:00

4.6 KiB

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

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

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.