Skip to content

SDK Patterns — @modelcontextprotocol/sdk

FieldValue
TypeAgent Reference
Source~/.copilot/agents/_refs/typescript-mcp-expert/sdk-patterns.md
DescriptionNot specified

Source Content

SDK Patterns — @modelcontextprotocol/sdk

Reference for TypeScript MCP Server Expert. Canonical patterns for building servers with the official TypeScript SDK.

Imports

Always import from specific SDK paths with the .js suffix (ES module resolution):

import { McpServer, ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { completable } from '@modelcontextprotocol/sdk/server/completable.js';

Use ES modules (import/export) — never require.

Tool registration

Use registerTool() rather than the lower-level Server class unless you need maximum control.

  • Provide title (human-readable) and name (machine identifier).
  • Define inputSchema as a plain object of zod validators: { param: z.string() }.
  • Optionally define outputSchema mirroring structuredContent shape.
  • Implementations should return both:
    • content — array of text/image blocks for display
    • structuredContent — typed payload for programmatic consumers
  • Wrap the implementation in try/catch and return { isError: true, content: [...] } on failure with a meaningful message.

Resource registration

  • Static: registerResource(name, uri, metadata, reader).
  • Dynamic: pass a ResourceTemplate such as new ResourceTemplate('resource://{id}', { list: undefined }).
  • For very large payloads return a ResourceLink instead of inlining bytes.

Prompt registration

  • Always supply title and a description.
  • Use zod for argsSchema.
  • Wrap argument values in completable(z.string(), async (value, ctx) => [...]) to provide context-aware completions.

Structured returns — required shape

return {
content: [{ type: 'text', text: humanReadable }],
structuredContent: typedPayload,
};

LLMs consume content; downstream tools consume structuredContent. Always emit both.

Error handling

try {
// ...
} catch (err) {
return {
isError: true,
content: [{ type: 'text', text: `Failed: ${(err as Error).message}` }],
};
}

Never let exceptions cross the transport boundary unhandled.

Naming and descriptions

  • Titles and descriptions are read by the LLM — write them clearly, no jargon.
  • Prefer verbs in tool names (search_users, not users).
  • Document each parameter via zod’s .describe().