How to Build Your First MCP Server in 10 Minutes
The shortest path from zero to a working MCP server that Claude can call. TypeScript SDK, working code, and the gotchas that will save you an hour.
On this page8 sections
This is the shortest path from zero to a working MCP server that Claude can call. We'll use the official TypeScript SDK because it's the most mature.
Prereqs
- Node 18+
- An Anthropic Claude Desktop install, or any MCP-aware client
Step 1: Scaffold
mkdir hello-mcp && cd hello-mcp
npm init -y
npm install @modelcontextprotocol/sdk zod
Step 2: Write the server
Create server.ts:
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
const server = new Server(
{ name: "hello-mcp", version: "0.1.0" },
{ capabilities: { tools: {} } }
);
server.tool(
"greet",
"Returns a friendly greeting for a given name",
z.object({ name: z.string() }),
async ({ name }) => ({
content: [{ type: "text", text: `Hello, ${name}!` }],
})
);
const transport = new StdioServerTransport();
await server.connect(transport);
Step 3: Register it with Claude Desktop
Open ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or the Windows equivalent. Add:
{
"mcpServers": {
"hello": {
"command": "npm",
"args": ["exec", "-y", "tsx", "--", "/absolute/path/to/server.ts"]
}
}
}
Restart Claude Desktop. Open a chat. You should see a small plug icon. Click it; greet should appear.
Step 4: Test it
Ask Claude: "Use the greet tool with name=Alice." It will call your server and reply with Hello, Alice!.
Step 5: Add a real tool
Replace greet with something useful. A common first real tool is one that hits an internal API:
server.tool(
"search_customers",
"Search the customer database by email or name",
z.object({ query: z.string() }),
async ({ query }) => {
const res = await fetch(
`https://api.example.com/customers?q=${encodeURIComponent(query)}`,
{ headers: { Authorization: `Bearer ${process.env.API_KEY}` } }
);
const data = await res.json();
return { content: [{ type: "text", text: JSON.stringify(data) }] };
}
);
Common gotchas
- Logs go to stderr, not stdout. Stdout is reserved for the protocol.
- Tool descriptions are read by the model. Write them like you're explaining to a junior dev, not a compiler.
- Don't catch and swallow errors. Let them bubble up so the model sees something useful.
Test without restarting Claude every time
Restarting Claude to test each change gets old fast. PreMan can act as an MCP client over HTTP or stdio, so you point it at your server, hit "Refresh tools," and see the schema, descriptions, and live responses without leaving the browser.
Bring the loop to your API
Catch the regression. Open a verified fix.
Join the waitlist to see which users a release may affect, monitor endpoints in production, and prepare a reviewable fix PR when something breaks.