The Claude AI Community Discord is open.Join us

Your First MCP Server

12 min readBuild & Connect Your Server

Create src/index.ts and paste this in:

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: "my-first-mcp-server",
  version: "1.0.0",
});

// 2. Register a tool
server.tool(
  "say_hello",                          // tool name
  "Returns a greeting for a given name", // description Claude reads
  { name: z.string().describe("The name to greet") }, // input schema
  async ({ name }) => ({                // handler
    content: [{ type: "text", text: `Hello, ${name}! MCP is working.` }],
  })
);

// 3. Start the server
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("MCP server running"); // stderr only!

Run it:

npm run dev

If you see MCP server running with no errors — your server works. It's now sitting there waiting for a client to connect. Ctrl+C to stop it.

What each part does

McpServer — the main server object. Give it a name and version. That's it.

server.tool() — registers one tool. Takes four arguments:

  • Tool name (snake_case by convention)
  • Description — this is what Claude reads to decide when to call your tool. Be specific.
  • Input schema — defined with Zod. Each field describes what Claude needs to pass.
  • Handler — the async function that runs when Claude calls the tool. Always returns { content: [{ type: "text", text: "..." }] }.

StdioServerTransport — sets up the stdio communication channel. Claude Desktop will start your server as a subprocess and talk to it through this.

server.connect(transport) — starts listening. Your server is now alive.

That's the whole pattern. Everything you build from here is just adding more tools.