ProtoboxProtobox
Tools & MCP

Build your first MCP server in 10 minutes

From zero to a working MCP server connected to Claude Desktop. We use the official TypeScript SDK so you can copy-paste a real, runnable example today — and we close with a preview of what shipping on Protobox will look like.

DGDean GroverFounder, ProtoboxFollow
April 26, 2026
8 min read
Terminal showing an MCP server starting up next to Claude Desktop's developer settings panel listing the new server

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 typescript

Open 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.ts

Step 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 build

You 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 StdioServerTransport for a StreamableHTTPServerTransport to 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:

  1. Switch to Streamable HTTP transport so the server runs on the internet
  2. Add OAuth 2.1 with PKCE so users can authenticate without sharing keys
  3. Host it somewhere with session affinity, TLS, and rate limits
  4. 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.

DG

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.

Sé de los primeros

Frequently Asked Questions