This walkthrough gets you from an empty directory to a working MCP server, connected to Claude Desktop, with a tool you can call from any conversation. We use the official Model Context Protocol TypeScript SDK — no extra layers, nothing fictional, all of this works today.
At the end we'll show what shipping the same server on Protobox will look like once the managed runtime is open.
Prereqs
- Node 18 or later (
node --version) - Claude Desktop (free) — macOS or Windows
- A terminal you're comfortable with
That's it. No Docker, no Postgres.
Step 1 — Set up the project
mkdir my-first-mcp && cd my-first-mcp
npm init -y
npm install @modelcontextprotocol/sdk zod@3
npm install -D @types/node typescriptOpen package.json and add "type": "module" plus a build script:
{
"type": "module",
"scripts": {
"build": "tsc && chmod 755 build/index.js"
}
}Then create a minimal tsconfig.json:
{
"compilerOptions": {
"target": "ES2022",
"module": "Node16",
"moduleResolution": "Node16",
"outDir": "./build",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*"]
}mkdir src && touch src/index.tsStep 2 — Define a tool
We'll build a get_weather tool — small, useful, and Claude will actually call it when you ask about the weather.
Paste this into src/index.ts:
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
// 1) Create the server.
const server = new McpServer({
name: "weather",
version: "1.0.0",
});
// 2) Register a tool.
server.registerTool(
"get_weather",
{
description: "Get current weather for a city",
inputSchema: {
city: z.string().describe("City name, e.g. 'San Francisco'"),
units: z.enum(["celsius", "fahrenheit"]).default("celsius"),
},
},
async ({ city, units }) => {
const res = await fetch(
`https://wttr.in/${encodeURIComponent(city)}?format=j1`,
);
const data = await res.json();
const tempC = Number(data.current_condition[0].temp_C);
const temp = units === "celsius" ? tempC : (tempC * 9) / 5 + 32;
const conditions = data.current_condition[0].weatherDesc[0].value;
return {
content: [
{
type: "text",
text: `${city}: ${temp}°${units === "celsius" ? "C" : "F"}, ${conditions}`,
},
],
};
},
);
// 3) Run over stdio. Claude Desktop spawns this process and talks JSON-RPC over its stdin/stdout.
const transport = new StdioServerTransport();
await server.connect(transport);
// IMPORTANT: stdio servers must never console.log to stdout — it corrupts the protocol.
// Use console.error for any logging.
console.error("weather MCP server running on stdio");That's the entire server. The SDK handles capability negotiation, request routing, and JSON-RPC framing.
Step 3 — Build it
npm run buildYou should see a build/index.js file appear. That's what Claude Desktop will execute.
Step 4 — Connect it to Claude Desktop
Open Claude Desktop, then Settings → Developer → Edit Config. That opens claude_desktop_config.json:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json
Add your server under mcpServers:
{
"mcpServers": {
"weather": {
"command": "node",
"args": ["/absolute/path/to/my-first-mcp/build/index.js"]
}
}
}Replace the path with the absolute path to your built file. Save and completely quit + relaunch Claude Desktop (Cmd-Q, not just close window).
Step 5 — Use it
In any new Claude Desktop conversation, click the MCP indicator in the bottom-right of the input box. You should see weather listed with one tool: get_weather.
Now ask:
What is the weather in Lisbon right now?
Claude calls get_weather, your local server fetches wttr.in, and the answer comes back in the conversation. Claude will ask you to approve the tool call the first time — that's the protocol's user-consent layer working as designed.
What you just learned
Every line of code above is real and runnable. The patterns scale:
- Add another tool with another
server.registerTool(...)call - Add resources (read-only data like files or DB records) with
server.registerResource(...) - Add prompts (reusable templates the user can pick from) with
server.registerPrompt(...) - Swap
StdioServerTransportfor aStreamableHTTPServerTransportto host the server remotely
The official SDK quickstart has expanded examples for each of these.
Where this gets harder
Going to production with a stdio server is a non-starter — every user has to clone your repo, build it, and edit a config file. To make your tools available to a real audience, you need to:
- Switch to Streamable HTTP transport so the server runs on the internet
- Add OAuth 2.1 with PKCE so users can authenticate without sharing keys
- Host it somewhere with session affinity, TLS, and rate limits
- Surface a stable URL users can paste into Claude's Custom Connectors UI, ChatGPT, Cursor, or anything else that speaks MCP
Each of those is a project. Most teams build all four.
Coming soon: the same server on Protobox
The managed Protobox runtime is in private preview as of April 2026. The shape of the developer experience we're targeting:
# 1) Install the CLI
npm install -g @protoboxai/cli # not yet on npm
# 2) Deploy your existing MCP server (the same src/index.ts above)
protobox deploy
# Output:
# ✓ Built 1 tool: get_weather
# ✓ Deployed to https://weather-a7f2.protobox.app/mcp
# ✓ OAuth 2.1 PKCE configured (default)
# ✓ Per-key rate limits applied (1000/min)You'd hand the URL to any MCP client (Claude via Custom Connectors, ChatGPT, Cursor) and the same get_weather tool would be available — with auth, rate limits, observability, and zero-downtime redeploys handled by the runtime.
This is design, not shipped code. The CLI command above does not yet exist on npm. We're publishing this preview so you can shape the API before it lands. If you'd like to influence it, reply to the changelog or reach out.
What's next
- Read the MCP spec. The 2025-11-25 revision is short, dense, and worth the hour.
- Browse open-source servers. The official servers repo has reference implementations for filesystem, GitHub, Slack, Postgres, and more — read a few to see the patterns.
- Try a remote server. Add an existing MCP server to Claude via Custom Connectors to see what the user experience looks like.
- Subscribe to our changelog (below) — we'll post here when the Protobox CLI hits npm.
Companion piece: What is MCP, and why it matters now — the conceptual background and where the protocol fits in the stack.
Founder, Protobox
Building TBD bio at Protobox — tools, testing, and observability for customer experience.
Changelog MCP y notas para devs
Actualizaciones cortas y ocasionales sobre la spec MCP, nuevas funciones de Protobox y patrones que vemos en producción. Sin marketing innecesario.

