Skilllibrary mcp-typescript-sdk
Build MCP servers in TypeScript using the official @modelcontextprotocol/sdk. Use when creating MCP tools/resources/prompts in TypeScript, using Zod for schema definitions, configuring TypeScript MCP transport, or applying the McpServer high-level API vs the low-level Server class.
install
source · Clone the upstream repo
git clone https://github.com/merceralex397-collab/skilllibrary
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/merceralex397-collab/skilllibrary "$T" && mkdir -p ~/.claude/skills && cp -r "$T/07-mcp/mcp-typescript-sdk" ~/.claude/skills/merceralex397-collab-skilllibrary-mcp-typescript-sdk && rm -rf "$T"
manifest:
07-mcp/mcp-typescript-sdk/SKILL.mdsource content
Purpose
Build MCP servers in TypeScript using the official
@modelcontextprotocol/sdk. This is the recommended SDK for MCP servers — it has the best host compatibility and uses Zod for type-safe schema definitions.
When to use this skill
- Building an MCP server in TypeScript/Node.js
- Using the McpServer high-level API for tool/resource/prompt registration
- Configuring TypeScript MCP transport (stdio or Streamable HTTP)
- Choosing between McpServer (high-level) vs Server (low-level) API
Do not use this skill when
- Building in Python → use
mcp-python-fastmcp - Building in Go → use
mcp-go-server - Need general server design guidance → use
mcp-development
Operating procedure
Phase 1 — Project setup
Using the scaffolder:
npx @anthropic-ai/create-mcp-server my-server cd my-server && npm install
Manual setup:
mkdir my-server && cd my-server npm init -y npm install @modelcontextprotocol/sdk zod npm install -D typescript @types/node npx tsc --init
tsconfig.json essentials:
{ "compilerOptions": { "target": "ES2022", "module": "Node16", "moduleResolution": "Node16", "outDir": "dist", "strict": true } }
Phase 2 — McpServer API (high-level, recommended)
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { z } from "zod"; const server = new McpServer({ name: "my-server", version: "1.0.0", });
Register tools
server.tool( "search_issues", { query: z.string().describe("Search query text"), state: z.enum(["open", "closed", "all"]).default("open").describe("Issue state filter"), limit: z.number().int().min(1).max(100).default(20).describe("Max results"), }, async ({ query, state, limit }) => { const results = await api.searchIssues(query, state, limit); return { content: [{ type: "text", text: JSON.stringify(results, null, 2), }], }; } );
Zod schema → JSON Schema: The SDK auto-converts Zod schemas to JSON Schema for
inputSchema. Use .describe() on every field.
Register tools with annotations
server.tool( "delete_issue", { issue_id: z.number().describe("Issue ID to delete") }, { annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, }, }, async ({ issue_id }) => { await api.deleteIssue(issue_id); return { content: [{ type: "text", text: `Deleted issue #${issue_id}` }] }; } );
Register resources
server.resource( "schema", "db://schema", { description: "Database schema definition", mimeType: "text/sql" }, async (uri) => ({ contents: [{ uri: uri.href, mimeType: "text/sql", text: await getSchemaSQL(), }], }) );
Register prompts
server.prompt( "analyze_data", { dataset: z.string().describe("Dataset name to analyze") }, ({ dataset }) => ({ messages: [{ role: "user", content: { type: "text", text: `Analyze the '${dataset}' dataset using the available query tools.`, }, }], }) );
Connect transport
// stdio (local servers) const transport = new StdioServerTransport(); await server.connect(transport);
// Streamable HTTP (remote servers) import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"; import express from "express"; const app = express(); app.use(express.json()); app.post("/mcp", async (req, res) => { const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: () => crypto.randomUUID() }); res.on("close", () => transport.close()); await server.connect(transport); await transport.handleRequest(req, res, req.body); }); app.listen(3000);
Phase 3 — Server API (low-level, for advanced use)
Use the low-level
Server class when you need custom request handling:
import { Server } from "@modelcontextprotocol/sdk/server/index.js"; const server = new Server( { name: "my-server", version: "1.0.0" }, { capabilities: { tools: { listChanged: true } } } ); server.setRequestHandler("tools/list", async () => ({ tools: [ { name: "search", description: "Search items", inputSchema: { type: "object", properties: { query: { type: "string" } }, required: ["query"] }, }, ], })); server.setRequestHandler("tools/call", async (request) => { const { name, arguments: args } = request.params; // ... handle tool call });
Phase 4 — Structured output (outputSchema)
server.tool( "get_weather", { location: z.string().describe("City name") }, { outputSchema: z.object({ temperature: z.number().describe("Temperature in Celsius"), conditions: z.string().describe("Weather conditions"), }), }, async ({ location }) => { const data = await fetchWeather(location); return { content: [{ type: "text", text: JSON.stringify(data) }], structuredContent: data, }; } );
Phase 5 — Build and test
npm run build npx @modelcontextprotocol/inspector node dist/index.js
Decision rules
- Use
(high-level) unless you need custom protocol handling — it handles capabilities, serialization, and transport wiringMcpServer - Use
(low-level) when you need to intercept raw JSON-RPC messages or implement custom capabilitiesServer - Always use Zod
on every field — this generates the.describe()
in JSON Schemadescription - Use
for constrained string valuesz.enum() - Use
for optional parameters with sensible defaultsz.default() - Use
for logging in stdio mode —console.error()
corrupts the transportconsole.log() - Set
in"type": "module"
or usepackage.json
extensions for ESM imports.mjs
Output requirements
- TypeScript project with
and@modelcontextprotocol/sdkzod - All tools registered with Zod schemas and
on every field.describe() - Transport configured (stdio or Streamable HTTP)
- Builds with
and passes MCP Inspector verificationtsc
Related skills
— Python alternativemcp-python-fastmcp
— Go alternativemcp-go-server
— tool design patternsmcp-tool-design
— debugging TypeScript MCP serversmcp-inspector-debugging
Failure handling
- If import errors: ensure
haspackage.json
and paths use"type": "module"
extension (not.js
).ts - If Zod validation fails at runtime: check that arguments match the Zod schema (number vs string coercion is common)
- If
doesn't work: ensure noStdioServerTransport
calls in the codeconsole.log() - If
doesn't exist: use@anthropic-ai/create-mcp-server
+ manual setup as fallbacknpm init