An MCP server exposes tools with clearly described inputs to an AI application. Start with a local function. Move to a remote interface once the tool and client work together.
This guide follows the stable version 2 of the official TypeScript SDK and protocol revision 2026-07-28, as of September 30, 2026. Older tutorials using @modelcontextprotocol/sdk and session transports belong to SDK v1. Do not mix the two APIs.
Create a project with Node.js 20 or later#
mkdir mein-mcp-server
cd mein-mcp-server
npm init -y
npm pkg set type=module
npm install @modelcontextprotocol/server@2 zod@4 tsx
mkdir src
type=module is required because the SDK uses ES modules. Here tsx runs TypeScript directly, so there is no tsc build or assumed dist/index.js. Shipping JavaScript instead requires a suitable build configuration; tsc --init alone does not establish that output path.
Register a tool#
Save this as src/index.ts:
import { McpServer } from "@modelcontextprotocol/server";
import { serveStdio } from "@modelcontextprotocol/server/stdio";
import * as z from "zod/v4";
serveStdio(() => {
const server = new McpServer({ name: "mein-server", version: "1.0.0" });
server.registerTool(
"add_numbers",
{
description: "Add two numbers and return their sum",
inputSchema: z.object({ a: z.number(), b: z.number() })
},
async ({ a, b }) => ({
content: [{ type: "text", text: String(a + b) }]
})
);
return server;
});
The schema validates both numbers. The description tells the assistant when the tool is useful. serveStdio handles communication over the process's standard streams. Run npx tsx src/index.ts to start it; waiting for input without printing a greeting is normal.
Connect a local client#
Claude Desktop's local configuration is at ~/Library/Application Support/Claude/claude_desktop_config.json on macOS and %APPDATA%\Claude\claude_desktop_config.json on Windows. Use absolute paths for the installed tsx executable and your source file:
{
"mcpServers": {
"mein-server": {
"command": "/absolute/path/to/node_modules/.bin/tsx",
"args": ["/absolute/path/to/mein-mcp-server/src/index.ts"]
}
}
}
On Windows, adjust the executable path to your installation too. Restart the client and check that add_numbers is available. Ask the assistant to add two numbers with that tool, then check both the answer and the actual tool call.
Claude Code can register the same local process:
claude mcp add mein-server -- /absolute/path/to/node_modules/.bin/tsx /absolute/path/to/mein-mcp-server/src/index.ts
Before production#
With stdio, do not print debugging output to stdout: that stream is for protocol messages. Send local logs to stderr. Limit inputs, handle expected failures clearly and return tool errors appropriately. External services need timeouts and, where relevant, rate limits.
The addition example has no external service dependencies and changes no data. Tools that write files or call external APIs require actual permissions, secret management and checks on side effects. Local transport does not guarantee the server sends no data over the network.
Serve it remotely over HTTP#
Use the SDK-v2 HTTP guidance and a complete integration for your runtime rather than an old session example. Revision 2026-07-28 uses per-request metadata; protocol sessions and the separate GET stream are not part of that revision.
Check origin validation, authentication and permissions. Local HTTP tests should listen on localhost only. A reachable endpoint alone is not a secure production integration. Also check which revision the intended client supports.
Sources: SDK-v2 quickstart, MCP transports, stdio, stdio versus HTTP.
