Skip to main content

Build Your Own MCP Server — A Minimal Python or TypeScript Server, Connected to Claude Code

A hands-on guide to building a minimal MCP server from scratch in Python and TypeScript using the official SDKs, then connecting it to Claude Code with claude mcp add and confirming the tool actually gets called.

By
🌐 This article was machine-translated and may contain inaccuracies. Read the Korean original if in doubt.

MCP (Model Context Protocol — the official open standard that lets AI applications like Claude connect to external tools and data) servers aren't limited to the ones other people publish. Once you build your own, you can attach exactly the functionality you want to Claude Code as a tool. This guide builds a minimal MCP server with no external dependencies in both Python and TypeScript using the official SDKs, then connects it to Claude Code with claude mcp add and confirms the tool actually gets called. Following it end to end takes roughly 30-40 minutes, though this varies by setup.

🟢 Model references match the current lineup · model notice · Fable subscription
🟢 Model references match the current lineup · Claude Opus 5.5 / Claude Sonnet 5 / Claude Haiku 4.5 (higher tier: Claude Fable 5.1). This notice changes only when Anthropic ships a new model.

Fable 5 and 5.1 subscription (updated September 7, 2026): Claude Fable 5.1, released September 1, 2026, is the current Fable model and Fable 5 is now legacy. Plan terms are the same for both — Max and Team Premium plans include Fable at up to 50% of the weekly usage limit; Pro and Team Standard use usage credits (

🟢 Model references match the current lineup · Claude Opus 5.5 / Claude Sonnet 5 / Claude Haiku 4.5 (higher tier: Claude Fable 5.1). This notice changes only when Anthropic ships a new model.
0/M input, $50/M output tokens). The one-time
🟢 Model references match the current lineup · Claude Opus 5.5 / Claude Sonnet 5 / Claude Haiku 4.5 (higher tier: Claude Fable 5.1). This notice changes only when Anthropic ships a new model.
00 credit applied only to the Fable 5 transition and is not offered for 5.1. Some coding and debugging requests may be answered by an Opus model due to a security classifier (both models). See the Fable 5.1 guide and the Fable 5 availability guide for details.

Claude Code (MCP client) Your own MCP server (Python or TypeScript) External system (API, DB, files, etc.) tool call/response API call

What exactly does an MCP server do?

An MCP server is a small program that exposes "tool" functions an AI client like Claude Code can call. Per the official documentation, an MCP server can offer three kinds of capabilities: tools (functions the LLM calls with user approval), resources (file-like data clients can read), and prompts (pre-written task templates). This guide focuses on building tools, the most commonly used one. The server and Claude Code talk over stdio (standard input/output — the terminal's standard input/output channel) using JSON-RPC (a structured request/response message format), which is why a server written in Python and one written in TypeScript look identical to Claude Code.

Before you start

Make sure you have the following in place so you don't get stuck partway through.

  • Claude Code CLI installed and signed in (the claude command works in your terminal)
  • Basic comfort with a terminal — changing directories, running commands
  • Python 3.10 or higher for the Python track, or Node.js 20 or higher for the TypeScript track
  • A text editor (VS Code or similar)

You don't need to complete both tracks — pick whichever language you're more comfortable with.

Quick terms

  • MCP (Model Context Protocol) — the official open standard for connecting AI applications to external tools and data
  • SDK (Software Development Kit) — a pre-built library that makes implementing a specific capability easier; here, the official library for building MCP servers
  • stdio (standard input/output) — the standard channel a program uses to exchange data with the terminal; MCP's most basic connection method
  • Tool — an individual function a server registers so an AI like Claude can call it
  • uv — the tool the official docs recommend for creating Python projects, virtual environments, and installing packages in one step

Build a minimal server in Python (the mcp package)

Start with the Python track, using uv as recommended by the official docs.

  1. Install uv (skip if you already have it).

    curl -LsSf https://astral.sh/uv/install.sh | sh

    Restart your terminal afterward so the uv command is picked up.

  2. Create the project and install the SDK.

    uv init hello-mcp
    cd hello-mcp
    uv venv
    source .venv/bin/activate
    uv add "mcp[cli]"
    touch hello.py

    Success looks like: the commands finish with no errors, and a hello.py file now exists inside the hello-mcp folder.

  3. Write hello.py. Paste this in as-is. It includes just two tools — a greeting and an addition — so it runs with no external API dependency.

    from mcp.server import MCPServer
    
    mcp = MCPServer("hello-server")
    
    @mcp.tool()
    def greet(name: str) -> str:
        """Return a greeting for the given name.
    
        Args:
            name: The name to greet
        """
        return f"Hello, {name}! Your MCP server is responding correctly."
    
    @mcp.tool()
    def add_numbers(a: float, b: float) -> str:
        """Return the sum of two numbers.
    
        Args:
            a: The first number
            b: The second number
        """
        return f"{a} + {b} = {a + b}"
    
    if __name__ == "__main__":
        mcp.run(transport="stdio")

    Warning: never use print() in a stdio server. print() writes to standard output, and MCP uses that exact channel to exchange JSON-RPC messages — a single stray print() call corrupts the messages and breaks the server. Use the logging module instead, which writes to stderr and is safe.

  4. Confirm it runs locally.

    uv run hello.py

    Success looks like: it runs with no error and the terminal just sits there quietly. That's expected — the server is waiting for a client (Claude Code) to connect. Press Ctrl+C to stop it.

Connect it to Claude Code (Python server)

Register the local stdio server with the Claude Code CLI's claude mcp add command. The launch command needs an absolute path, so run pwd in the project folder first and copy the full path.

1 Init project install SDK 2 Write tool functions 3 Connect via claude mcp add 4 Verify with /mcp · list
  1. Register the server. Replace /absolute/path/hello-mcp with the path you just copied.

    claude mcp add --transport stdio hello-mcp -- uv --directory /absolute/path/hello-mcp run hello.py

    Success looks like: a line starting with Added prints. That confirms the configuration was saved — not that the connection itself succeeded.

  2. Check the connection status.

    claude mcp list

    Success looks like: hello-mcp shows ✔ Connected next to it. If you see ✘ Failed to connect, check the troubleshooting section below.

  3. Call it for real from inside Claude Code. Start a Claude Code session and ask in natural language.

    Use the greet tool from hello-mcp to say hello to "Woojoo"

    Success: Claude calls the greet tool and its response reflects the exact sentence the server generated.

Build a minimal server in TypeScript (the official SDK)

Now build the same server in TypeScript. Skip this section if you already finished the Python track.

  1. Check Node.js. Confirm it's version 20 or higher.

    node --version
    npm --version
  2. Create the project and install the SDK.

    mkdir hello-mcp-ts
    cd hello-mcp-ts
    npm init -y
    npm install @modelcontextprotocol/server zod
    npm install -D @types/node typescript
    mkdir src
    touch src/index.ts
  3. Update package.json. Add "type": "module" and a build script at the top level.

    {
      "type": "module",
      "scripts": {
        "build": "tsc && chmod 755 build/index.js"
      }
    }
  4. Create tsconfig.json at the project root.

    {
      "compilerOptions": {
        "target": "ES2022",
        "module": "Node16",
        "moduleResolution": "Node16",
        "types": ["node"],
        "outDir": "./build",
        "rootDir": "./src",
        "strict": true,
        "esModuleInterop": true,
        "skipLibCheck": true,
        "forceConsistentCasingInFileNames": true
      },
      "include": ["src/**/*"],
      "exclude": ["node_modules"]
    }
  5. Write src/index.ts.

    import { McpServer } from "@modelcontextprotocol/server";
    import { StdioServerTransport } from "@modelcontextprotocol/server/stdio";
    import { z } from "zod";
    
    const server = new McpServer({
      name: "hello-server",
      version: "1.0.0",
    });
    
    server.registerTool(
      "greet",
      {
        description: "Return a greeting for the given name",
        inputSchema: z.object({
          name: z.string().describe("The name to greet"),
        }),
      },
      async ({ name }) => {
        return {
          content: [
            {
              type: "text",
              text: `Hello, ${name}! Your MCP server is responding correctly.`,
            },
          ],
        };
      },
    );
    
    async function main() {
      const transport = new StdioServerTransport();
      await server.connect(transport);
      console.error("hello-server MCP server running on stdio");
    }
    
    main().catch((error) => {
      console.error("Fatal error in main():", error);
      process.exit(1);
    });

    Warning: just like Python, never use console.log() — it corrupts JSON-RPC over stdout. Use console.error() instead, which writes to stderr.

  6. Build it.

    npm run build

    Success looks like: it finishes with no errors and creates a build/index.js file. Skipping this step means the next step's connection will fail.

Connect it to Claude Code (TypeScript server)

As with Python, you need an absolute path. Run pwd inside hello-mcp-ts to get it.

  1. Register the server.

    claude mcp add --transport stdio hello-mcp-ts -- node /absolute/path/hello-mcp-ts/build/index.js
  2. Check the connection.

    claude mcp list

    Success looks like: hello-mcp-ts shows ✔ Connected. You can also check the same status with /mcp inside a Claude Code session.

  3. Try it for real, the same way as the Python track.

    Use the greet tool from hello-mcp-ts to say hello to "Woojoo"
ItemPythonTypeScript
Required versionPython 3.10+Node.js 20+
Package manageruvnpm
Core SDK installuv add "mcp[cli]"npm install @modelcontextprotocol/server zod
Server classMCPServerMcpServer
Local run commanduv run hello.pynode build/index.js after building

Common sticking points and fixes

  • claude mcp list shows ✘ Failed to connect: run the exact launch command you gave claude mcp add (uv --directory ... run hello.py or node .../build/index.js) directly in your terminal. The error it prints there tells you exactly what's wrong.
  • The server never responds, or the connection drops: check your code for a stray print() (Python) or console.log() (TypeScript). In a stdio server, these are the most common cause of corrupted JSON-RPC messages.
  • The connection fails because of a path issue: claude mcp add requires an absolute path. A relative path can fail depending on where you run it from — confirm the exact path with pwd in your project folder.
  • Claude's CLI misreads the server's own flags as its own: always put -- (double dash) before the server's launch command in claude mcp add. Without it, Claude Code tries to parse arguments meant for your server as its own options.
  • The TypeScript server won't connect: confirm you actually ran npm run build and that build/index.js exists.

Going further

Once it's working, add more @mcp.tool() blocks (Python) or server.registerTool(...) calls (TypeScript) to the same file to expose whatever functionality you need — reading files, calling an internal API, and so on. To share it with teammates, add --scope project to claude mcp add; that stores the configuration in a .mcp.json file at the project root, which you can check into version control. If you'd rather find an already-built server instead of writing one, a guide focused on choosing existing MCP servers is the faster path.

Frequently asked questions

Q. Can MCP servers only be built in Python or TypeScript?
No. The official SDKs also cover Java, Kotlin, C#, Ruby, and more. This guide covers Python and TypeScript because they're the most widely used.

Q. How do I share a server I built with teammates?
Add --scope project to claude mcp add. That saves the configuration to .mcp.json at the project root, which you can share through version control — though each teammate still has to approve the server the first time.

Q. How do I add more tools?
Add another function with the @mcp.tool() decorator (Python) or another server.registerTool(...) call (TypeScript) in the same file. Restart the server and the new tool shows up — no need to re-register it with Claude Code.

Q. My server keeps showing as failed to connect and I can't figure out why.
Run the exact launch command you gave claude mcp add directly in your terminal. Whatever error it prints there is your most reliable clue.

Was this helpful?

Keep reading