hello@nodemode.dev

Guide

Write an MCP server with the TypeScript SDK v2.

The official TypeScript SDK is now split into @modelcontextprotocol/server & @modelcontextprotocol/client, implementing the 2026-07-28 spec. It runs on Node.js, Bun & Deno. Older tutorials that import Server from @modelcontextprotocol/sdk & call setRequestHandler are on the v1 line, which keeps receiving fixes for at least six months after v2 but is not what you should start with today.

1. Create the project

terminal
mkdir rack-status && cd rack-status
npm init -y
npm install @modelcontextprotocol/server zod

Set "type": "module" in package.json. Tool & prompt schemas use Standard Schema, so Zod v4 is one option; Valibot or ArkType work the same way.

2. Write the server

server.js
import { McpServer } from "@modelcontextprotocol/server";
import { StdioServerTransport } from "@modelcontextprotocol/server/stdio";
import * as z from "zod/v4";

const server = new McpServer({ name: "rack-status", version: "1.0.0" });

server.registerTool(
  "rack_status",
  {
    description: "Report a rack's power draw and inlet temperature",
    inputSchema: z.object({ rack: z.string() }),
  },
  async ({ rack }) => ({
    content: [{ type: "text", text: `${rack}: 14.5 kW, inlet 22 C` }],
  })
);

server.registerTool(
  "get_time",
  { description: "Current server time, ISO 8601", inputSchema: z.object({}) },
  async () => ({ content: [{ type: "text", text: new Date().toISOString() }] })
);

const transport = new StdioServerTransport();
await server.connect(transport);

Two tools, one file. registerTool takes the name, a description plus input schema, & the handler; the SDK answers tools/list & tools/call for you.

3. Run it

terminal
node server.js

Over stdio the server waits silently for a client. Wire it into a desktop app with the config in Connect a desktop client, or list & call the tools from a client built with @modelcontextprotocol/client.

4. Serve it over HTTP

For remote clients, swap the transport for Streamable HTTP. The SDK publishes thin middleware packages so the server sits inside the web framework you already run:

@modelcontextprotocol/nodeStreamable HTTP over Node's IncomingMessage / ServerResponse
@modelcontextprotocol/expressExpress defaults plus Host header validation
@modelcontextprotocol/fastifyFastify defaults plus Host header validation
@modelcontextprotocol/honoHono defaults, JSON body parsing hook, Host header validation

On the fabric you do not pick the middleware: push the repository, name a pool, & the server answers at https://rack-status.<org>.nodemode.dev/mcp. The stdio build stays valid for local use.

Practices we hold servers to

  • Validate every argument: type, required fields, length limits, permissions. The schema is the first gate, not the only one.
  • No unrestricted shell, file deletion or system commands unless the tool runs in its own sandbox with a stated scope.
  • One tool, one job. search_customer, create_ticket, generate_report, never one tool that does everything.

Sources: the modelcontextprotocol/typescript-sdk README on GitHub (v2, 2026-07-28 spec), fetched 2026-09-03; the v1 shape is from a June 2026 walkthrough of the older @modelcontextprotocol/sdk API.