Room 42, home

Build an MCP Server

Stand up a working MCP server with the official SDK, expose a tool and a resource, and connect it to a real client so Claude can use it right away.

MCPintermediate3 min readUpdated 12 Jun 2026

In short

  1. 01An MCP server exposes your tools and data to an AI model through a common interface any client can connect to.
  2. 02Scaffold one with the official SDK, then register a tool with a name, description, and a typed input schema.
  3. 03Add read-only context as a resource, run the server locally, and connect it to Claude Code with one command.
  4. 04Test it in isolation with the Inspector, then ship to production by swapping stdio for HTTP and adding auth.
Contents · 6 sections
  1. 1How do I scaffold an MCP server project?
  2. 2How do I expose a tool in an MCP server?
  3. 3What is an MCP resource and how do I add one?
  4. 4How do I run my MCP server and connect it to a client?
  5. 5The Python version
  6. 6How do I deploy an MCP server to production?

The best way to understand MCPModel Context Protocol, an open standard that lets AI models discover and call external tools and data sources through a common interface. is to build a server. This guide takes you from an empty folder to a working server with a tool and a resource, connected to a real client. We use the official TypeScript SDK; the Python equivalent is at the end. If you want the mental model first, read MCP fundamentals before you start.

How do I scaffold an MCP server project?

Install the SDKSoftware Development Kit, a ready-made library that handles the protocol details so you write less boilerplate. and Zod (the SDK uses it to validate tool inputs):

npm install @modelcontextprotocol/sdk zod

One thing to get right up front: import from the SDK's subpaths, with the .js extension, even in TypeScript. These are ESMECMAScript Modules, the standard module system for JavaScript that uses import and export statements. subpath exports.

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

If you copy an import that reads @modelcontextprotocol/server (no /sdk) from some docs, it will not resolve against the published package. Use the /sdk/... paths above.

How do I expose a tool in an MCP server?

A tool needs a name, a config (title, description, and an input schema), and an async handler that returns content. Here is a complete, minimal server with one tool:

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import * as z from "zod";
 
const server = new McpServer({ name: "my-server", version: "1.0.0" });
 
server.registerTool(
  "calculate-bmi",
  {
    title: "BMI Calculator",
    description: "Calculate Body Mass Index from weight and height.",
    inputSchema: { weightKg: z.number(), heightM: z.number() },
  },
  async ({ weightKg, heightM }) => ({
    content: [
      {
        type: "text",
        text: String(weightKg / (heightM * heightM)),
      },
    ],
  }),
);
 
const transport = new StdioServerTransport();
await server.connect(transport);

The schema is a plain object of Zod validators.Common gotchaPass the raw shape ({ weightKg: z.number() }), not z.object({ ... }).Wrapping it in z.object is the usual slip, and the SDK then fails to derive the field list, so the model sees a tool with no parameters. The SDK turns it into the JSON schemaA machine-readable description of what fields a tool expects and their types, which the model reads to call the tool correctly. the model sees, and validates incoming calls against it before your handler runs. The handler returns a content array; text is the common case, but you can also return images or embedded resources.

What is an MCP resource and how do I add one?

A resource is read-only context. Register one with a name, a URI, some metadata, and a reader:

server.registerResource(
  "config",
  "config://app",
  {
    title: "Application Config",
    description: "Current application configuration.",
    mimeType: "text/plain",
  },
  async (uri) => ({
    contents: [{ uri: uri.href, text: "log_level=info\nregion=eu" }],
  }),
);

How do I run my MCP server and connect it to a client?

During development, run the server straight from TypeScript:Common gotchaA stdio server speaks JSON-RPC over stdout, so a stray console.log inside a handler writes garbage into that same stream and the client drops the connection with a cryptic parse error.Send all your logging to stderr instead (console.error), which the protocol leaves alone.

npx tsx server.ts

Then connect it to Claude Code so the model can use it:

claude mcp add --transport stdio my-server -- npx tsx /absolute/path/to/server.ts

To inspect the server on its own, without a model in the loop, use the Inspector. It gives you a UI to list and call your tools and resources directly, which is the fastest way to confirm everything works before wiring it into an agent:

npx @modelcontextprotocol/inspector

The Python version

Python's SDK has a high-level API called FastMCP that mirrors this closely:

from mcp.server.fastmcp import FastMCP
 
mcp = FastMCP("my-server")
 
@mcp.tool()
def calculate_bmi(weight_kg: float, height_m: float) -> float:
    """Calculate Body Mass Index."""
    return weight_kg / (height_m ** 2)
 
if __name__ == "__main__":
    mcp.run()

Install it with pip install "mcp[cli]". Decorate functions with @mcp.tool(), @mcp.resource(...), and @mcp.prompt(), and run.

How do I deploy an MCP server to production?

Once it works locally, the path to production is the transportThe channel that carries messages between client and server, such as local pipes during development or HTTP over a network in production.: swap the stdioStandard input and output, the local pipe pair a program uses to talk to a parent process on the same machine. transport for Streamable HTTP, host it, and add auth. That move from local to networked is covered in local vs remote MCP servers. Keep secrets in environment variables, scope what the server can touch (see MCP security basics), and you have a capability any MCP client can connect to. To see servers other people have already built, browse useful MCP servers.