Mindket

Build an MCP server in TypeScript with the official SDK

A black banner titled "MCP course". Its line reads "Build an MCP server in Python or TypeScript, test it, connect it".

To build an MCP server in TypeScript, install @modelcontextprotocol/server and zod, create an McpServer and register each tool with registerTool and a Zod input schema. Serve it with serveStdio, which lets a host launch the file as a local process. Run it with npx tsx src/index.ts, so there is no build step, and call the tool from the MCP Inspector.

In this lesson

  • You will build a stdio server with one tool, get-alerts, that looks up US weather alerts, in a single TypeScript file.
  • The Zod schema is the only schema you write. The SDK builds the rest from it, including the argument check that runs before your handler.
  • The package for the current SDK is @modelcontextprotocol/server. Older tutorials install @modelcontextprotocol/sdk, the 1.x line.
  • One console.log on a stdio server corrupts the protocol. Log with console.error.
All courses
On this page
  1. Before you start
  2. Build the server in six steps
  3. What the code does
  4. What happens when a model calls your tool
  5. David Soria Parra on the stateless spec and the v2 SDKs
  6. Common mistakes
  7. Try it yourself
  8. Next: test it properly with the MCP Inspector
  9. Check yourself
  10. Frequently asked questions
  11. Sources

Before you start

This lesson follows the first-server tutorial in the TypeScript SDK's own repository, which runs TypeScript directly with tsx. The MCP build guide builds a similar weather server with a second tool and compiles it with tsc. Pick that one if you want a build step.

You need Node.js 20 or later, npm and an internet connection, because the tool calls the US National Weather Service API. The Inspector in the last step needs a patched Node release, 22.23.4, 24.22.1 or 26.12.1 or later on each line, which lesson 3 explains. Check the package name before you install anything, because TypeScript tutorials come in two generations.

The packages behind TypeScript MCP tutorials
PackageSDK lineWhere you meet it
@modelcontextprotocol/serverv2, the current stable lineThe official docs and this lesson
@modelcontextprotocol/clientv2, for code that connects to serversClients and test scripts
@modelcontextprotocol/sdkv1.x, the older line, still maintainedOlder tutorials and existing code

Build the server in six steps

The code comes in two pieces, the tool and the serve call. Both go in one file, src/index.ts.

import { McpServer } from '@modelcontextprotocol/server';
import { serveStdio } from '@modelcontextprotocol/server/stdio';
import * as z from 'zod/v4';

const NWS_API = 'https://api.weather.gov';

interface AlertsResponse {
    features: { properties: { event?: string; headline?: string } }[];
}

function createServer(): McpServer {
    const server = new McpServer({ name: 'weather', version: '1.0.0' });

    server.registerTool(
        'get-alerts',
        {
            description: 'Get the active weather alerts for a US state',
            inputSchema: z.object({
                state: z.string().length(2).describe('Two-letter US state code, e.g. CA')
            })
        },
        async ({ state }) => {
            const code = state.toUpperCase();
            const url = `${NWS_API}/alerts/active?area=${code}`;
            const res = await fetch(url, { headers: { 'User-Agent': 'mcp-weather-tutorial/1.0' } });
            if (!res.ok) {
                return { content: [{ type: 'text', text: `NWS API error: HTTP ${res.status}` }], isError: true };
            }
            const { features } = (await res.json()) as AlertsResponse;
            if (features.length === 0) {
                return { content: [{ type: 'text', text: `No active alerts for ${code}.` }] };
            }
            const lines = features.map(f => f.properties.headline ?? f.properties.event ?? 'Unnamed alert');
            return { content: [{ type: 'text', text: lines.join('\n') }] };
        }
    );

    return server;
}

void serveStdio(createServer);
console.error('weather MCP server running on stdio');
  1. Check Node with node --version. It must report version 20 or later.
  2. Create the project with mkdir weather && cd weather, then npm init -y, then npm pkg set type=module. The type=module setting matters, because the SDK ships ES modules only.
  3. Install the SDK, Zod and tsx with npm install @modelcontextprotocol/server zod tsx, then create the source folder with mkdir src.
  4. Create src/index.ts and paste in the code above.
  5. Start the server from the project root with npx tsx src/index.ts. It prints weather MCP server running on stdio to stderr and then waits. Stop it with Ctrl+C.
  6. Call the tool with npx @modelcontextprotocol/inspector npx tsx src/index.ts. In the browser tab it opens, click Connect, open the Tools tab, pick get-alerts, enter a state code such as TX and run it.

What the code does

createServer is a factory that builds a new McpServer and registers one tool on it. registerTool takes a name, a config and an async handler. The inputSchema is a Zod schema, and from that one schema the SDK produces the JSON Schema for the model, checks the arguments before your handler runs and infers the handler's argument types.

The handler returns content, a list of typed blocks, here a single text block. Setting isError: true marks a failed result that the model can read and react to. Call get-alerts with { "state": "California" } and the SDK refuses before your handler runs. The model sees this message:

Input validation error: Invalid arguments for tool get-alerts: state: Too big: expected string to have <=2 characters

The factory shape is on purpose. The MCP build guide says serveStdio calls it whenever it needs an instance, so every call must return a fresh McpServer. The SDK README's shorter example wires a server by hand with server.connect(new StdioServerTransport()). The build guide says a server started that way only serves clients that open with the older 2025-era handshake, so serveStdio is the safer choice.

What happens when a model calls your tool

The model never sees your code. It sees the tool's name, its description and the JSON Schema derived from inputSchema, so those three decide whether the tool gets called at all. Plug the server into a host and a single answer takes six steps.

One tool call, from question to answer
  1. The host sends the question

    In the SDK's example the host is VS Code. It sends the question to the model together with the name, description and input schema of every available tool.

  2. The model picks a tool

    It decides get-alerts answers the question and emits a call with the arguments it chose.

  3. The client calls your server

    The MCP client inside the host sends a tools/call request over stdio.

  4. The SDK runs your handler

    It validates the arguments against inputSchema first, then runs the handler.

  5. Your content goes back

    The handler's content returns over stdout as the tools/call result.

  6. The model writes the answer

    It reads the text block and writes the reply you see in the chat.

David Soria Parra on the stateless spec and the v2 SDKs

David Soria Parra, a member of technical staff at Anthropic and co-creator of MCP, announced the 2026-07-28 release on X the day it shipped. MCP became stateless, with semantics for requests that take several round trips, and the Python and TypeScript SDKs released v2.0.0 for it. That fits this lesson's factory: with no session tying a client to one object, serveStdio can build a fresh McpServer whenever it needs one.

The same post named extensions as the other headline: MCP Apps, Tasks, Enterprise Managed Auth and more, each a domain-specific way to use MCP. The TypeScript SDK's version 2.4.0 added Skills over MCP on 23 October. Read the SDK's releases before you build such a feature by hand.

Later that day he thanked the SDK maintainers on X and listed what supports the spec: v2.0.0 of the TypeScript, Python and C# SDKs, plus v1.7.0 of the Go SDK. The protocol is the same in each, so pick the SDK by the language your team writes.

Serving MCP just got much simpler and more scalable.

David Soria Parra, Member of technical staff at Anthropic, co-creator of MCP · X (read November 5, 2026)
David Soria Parra (@dsp_) on X, 28 July 2026. Thrilled to announce the MCP 2026-07-28 release. MCP is now stateless, with semantics for multi-round-trip requests. Serving MCP just got much simpler and more scalable. MCP now has extensions. Python, Typescript, C# (soon) SDKs released a v2.0.0 for the new spec with improved ergonomics.
David Soria Parra on X, 28 July 2026
David Soria Parra (@dsp_) on X, later on 28 July 2026. Special thanks to the SDK maintainers getting the v2.0.0 with fundamental upgrades ready in time with the release. Grab v2.0.0 of the Typescript, Python and C# SDKs and v1.7.0 of the Go SDK supporting the spec.
David Soria Parra on X, 28 July 2026

Screenshots of X's own embeds of the posts, taken on 5 November 2026, with the time in UTC. X cuts both posts short with Show more. Select one to open the post on X.

Common mistakes

  • Using console.log. On stdio, stdout is the protocol channel, and the host reports SyntaxError: Unexpected token ... is not valid JSON. The quoted token is the first character of the stray line. Switch to console.error.
  • Leaving out the type=module line in package.json from step 2.
  • Two copies of zod in the tree. TypeScript can fail with TS2589: Type instantiation is excessively deep. Run npm ls zod and make it list one version, with an overrides entry in package.json if a dependency pins another.
  • Running Node 18 or older. Every SDK package needs a newer runtime, and the troubleshooting page says ReferenceError: crypto is not defined means an older one.
  • Copying a 1.x tutorial. If it installs @modelcontextprotocol/sdk, its imports will not match this lesson.

Try it yourself

  • Call get-alerts in the Inspector with { "state": "California" } and compare the message with the one above.
  • Add a console.log("hello") to the top of the file, run the Inspector command again and read what breaks. Then change it to console.error.
  • Add a second tool next to the first inside createServer and check that the Tools tab lists both. The get_forecast tool in the MCP build guide is a ready example.

Next: test it properly with the MCP Inspector

You called the tool once in the Inspector. Lesson 3 goes through every tab, the command line mode and what to check when a server misbehaves. If you want the Python version of this server, lesson 1 builds it.

For the bigger picture, what is MCP explains how hosts, clients and servers fit together.

Check yourself

  1. Which package does the current TypeScript SDK use to build a server?

    Show answer

    C. @modelcontextprotocol/server The current SDK is @modelcontextprotocol/server, while @modelcontextprotocol/sdk is the older 1.x line that older tutorials install.

  2. The host of your stdio server reports SyntaxError: Unexpected token ... is not valid JSON. What is the likely cause?

    Show answer

    A. A console.log call wrote to stdout, which is the protocol channel On stdio, stdout carries the protocol, so a stray line breaks it and you log with console.error instead.

  3. A model calls get-alerts with { "state": "California" }. What happens?

    Show answer

    D. The SDK rejects it before your handler runs The SDK checks the arguments against inputSchema first, and the model reads the validation message.

  4. What decides whether a model calls your tool at all?

    Show answer

    B. The tool's name, its description and its input schema The model never sees your code, only the name, the description and the JSON Schema built from inputSchema.

Frequently asked questions

Can I still use the old @modelcontextprotocol/sdk package?

For now. It is the 1.x line, and the README says it keeps getting bug fixes and security updates for at least six months after v2 shipped on 27 July 2026. New code should start on @modelcontextprotocol/server.

What does the build guide's tsc setup add?

It compiles src into a build folder, and its package.json gains a bin entry, a files list and a build script that runs tsc. Hosts then run node on build/index.js, as in the guide's Claude Desktop config.

Do I have to use Zod for the input schema?

No. The README says tool and prompt schemas use Standard Schema, so Zod v4, Valibot, ArkType or any compatible library works. The tutorial uses Zod, and two copies of it in one dependency tree cause type errors.

Can I run the SDK on Bun or Deno?

Yes. The README says the SDK runs on Node.js, Bun and Deno. Install it with bun add @modelcontextprotocol/server or deno add npm:@modelcontextprotocol/server.

How do I serve the same server over HTTP?

Hand the same createServer factory to the SDK's HTTP serving instead of serveStdio. The tutorial says to do that when one endpoint should serve many clients. The SDK also publishes small adapter packages for Express, Fastify, Hono and Node's own HTTP server.

How do I log from a stdio server?

Use console.error, which writes to stderr, or a logging library that writes to stderr or files. A host captures a stdio server's stderr on its own, so nothing needs wiring up.

Sources

  1. MCP TypeScript SDK: README (read November 4, 2026)https://github.com/modelcontextprotocol/typescript-sdk
  2. MCP TypeScript SDK docs: Build your first server (read November 4, 2026)https://github.com/modelcontextprotocol/typescript-sdk/blob/main/docs/get-started/first-server.md
  3. MCP TypeScript SDK docs: Plug into a real host (read November 4, 2026)https://github.com/modelcontextprotocol/typescript-sdk/blob/main/docs/get-started/real-host.md
  4. MCP TypeScript SDK docs: Troubleshooting (read November 4, 2026)https://github.com/modelcontextprotocol/typescript-sdk/blob/main/docs/troubleshooting.md
  5. MCP docs: Build an MCP server (read November 4, 2026)https://modelcontextprotocol.io/docs/develop/build-server
  6. MCP docs: MCP Inspector (read November 4, 2026)https://modelcontextprotocol.io/docs/tools/inspector
  7. MCP docs: Debugging (read November 4, 2026)https://modelcontextprotocol.io/docs/2026-07-28/tools/debugging
  8. MCP Inspector: README (read November 5, 2026)https://github.com/modelcontextprotocol/inspector
  9. MCP TypeScript SDK release v2.4.0 (read November 5, 2026)https://github.com/modelcontextprotocol/typescript-sdk/releases/tag/v2.4.0
  10. David Soria Parra on X: the MCP 2026-07-28 release (read November 5, 2026)https://x.com/dsp_/status/2082173429399142616
  11. David Soria Parra on X: the SDK releases that support the spec (read November 5, 2026)https://x.com/dsp_/status/2082225962402402384

Share this lesson

See what other MCP servers can do

Browse the MCP servers on Mindket and compare them with the one you just built.

Browse MCP servers

This lesson as Markdown · MCP course: build, test and connect MCP servers · All courses