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
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 (
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
claudecommand 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.
-
Install uv (skip if you already have it).
curl -LsSf https://astral.sh/uv/install.sh | shRestart your terminal afterward so the
uvcommand is picked up. -
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.pySuccess looks like: the commands finish with no errors, and a
hello.pyfile now exists inside thehello-mcpfolder. -
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 strayprint()call corrupts the messages and breaks the server. Use theloggingmodule instead, which writes to stderr and is safe. -
Confirm it runs locally.
uv run hello.pySuccess 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+Cto 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.
-
Register the server. Replace
/absolute/path/hello-mcpwith the path you just copied.claude mcp add --transport stdio hello-mcp -- uv --directory /absolute/path/hello-mcp run hello.pySuccess looks like: a line starting with
Addedprints. That confirms the configuration was saved — not that the connection itself succeeded. -
Check the connection status.
claude mcp listSuccess looks like:
hello-mcpshows✔ Connectednext to it. If you see✘ Failed to connect, check the troubleshooting section below. -
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
greettool 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.
-
Check Node.js. Confirm it's version 20 or higher.
node --version npm --version -
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 -
Update package.json. Add
"type": "module"and a build script at the top level.{ "type": "module", "scripts": { "build": "tsc && chmod 755 build/index.js" } } -
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"] } -
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. Useconsole.error()instead, which writes to stderr. -
Build it.
npm run buildSuccess looks like: it finishes with no errors and creates a
build/index.jsfile. 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.
-
Register the server.
claude mcp add --transport stdio hello-mcp-ts -- node /absolute/path/hello-mcp-ts/build/index.js -
Check the connection.
claude mcp listSuccess looks like:
hello-mcp-tsshows✔ Connected. You can also check the same status with/mcpinside a Claude Code session. -
Try it for real, the same way as the Python track.
Use the greet tool from hello-mcp-ts to say hello to "Woojoo"
| Item | Python | TypeScript |
|---|---|---|
| Required version | Python 3.10+ | Node.js 20+ |
| Package manager | uv | npm |
| Core SDK install | uv add "mcp[cli]" | npm install @modelcontextprotocol/server zod |
| Server class | MCPServer | McpServer |
| Local run command | uv run hello.py | node build/index.js after building |
Common sticking points and fixes
claude mcp listshows ✘ Failed to connect: run the exact launch command you gaveclaude mcp add(uv --directory ... run hello.pyornode .../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) orconsole.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 addrequires an absolute path. A relative path can fail depending on where you run it from — confirm the exact path withpwdin 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 inclaude 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 buildand thatbuild/index.jsexists.
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.
Related articles
- Build Your Own MCP Server: From First Python Server to Claude (2026)
- GitHub MCP Server Setup: Repos, Issues & PRs in Claude (2026)
- PostgreSQL MCP Server: Connect a Database to Claude (2026)
- Playwright MCP Server Setup: Browser Automation in Claude (2026)
- Claude Code MCP Server Recommendations: What to Connect and How