Building an MCP Server from Scratch
Learn how to create a Model Context Protocol (MCP) server in TypeScript that connects to Claude Desktop. This tutorial covers setting up the project, building a simple tool to fetch Pokémon data from the PokéAPI, and integrating it with Claude for real-time responses.
The Model Context Protocol (MCP) provides a way to connect external tools and services directly into AI assistants like Claude. In this tutorial, we’ll walk step by step through creating a simple MCP server in TypeScript, connecting it to Claude Desktop, and building our first tool.
By the end, you’ll have a working MCP server that can fetch Pokémon data from the PokéAPI and return it to Claude.
You can also jump straight to the code on github
Prerequisites
- Node.js and
pnpminstalled - Basic familiarity with TypeScript
- Claude Desktop installed
1. Initialize the Project
We’ll start by creating a new project directory and initializing it with pnpm:
mkdir mcp-pokedex
cd mcp-pokedex
pnpm init
This generates a package.json file, which we’ll expand as we go.
2. Install Dependencies
Install the MCP SDK and the zod library for input validation:
pnpm install @modelcontextprotocol/sdk
👉 For compatibility, I had to install a specific version of zod:
pnpm install zod@^3.25.76
Then add development dependencies for TypeScript:
pnpm install -D typescript @types/node
3. Set Up Project Structure
Create a src directory for your code:
mkdir src
Then add an entry file:
touch src/index.ts
4. Configure package.json
Update your package.json so Node knows how to run the project:
{
"type": "module",
"main": "dist/index.js",
"files": ["dist"],
"scripts": {
"build": "tsc",
"start": "node dist/index.js"
}
}
5. Create a tsconfig.json
Add a TypeScript configuration file to the root of the project:
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "Node",
"outDir": "dist",
"strict": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"skipLibCheck": true
},
"include": ["src"]
}
6. Build a Minimal MCP Server
Let’s start with a barebones MCP server in src/index.ts:
import { Server } from "@modelcontextprotocol/sdk/server";
const server = new Server({
name: "pokedex",
version: "1.0.0",
});
server.start();
console.log("MCP server running...");
At this point, you have a server that Claude can connect to—but it doesn’t do anything yet.
7. Add a Client API Helper
Let’s connect to the PokéAPI. Create a new file src/api.ts:
export async function fetchPokemon(name: string) {
const res = await fetch(`https://pokeapi.co/api/v2/pokemon/${name}`);
if (!res.ok) throw new Error("Pokemon not found");
return res.json();
}
This helper will let our tools query Pokémon data.
8. Create Your First Tool
Now let’s add a tool that fetches Pokémon details. Update src/index.ts:
import { Server } from "@modelcontextprotocol/sdk/server";
import { z } from "zod";
import { fetchPokemon } from "./api.js";
const server = new Server({
name: "pokedex",
version: "1.0.0",
});
server.tool(
"get_pokemon",
"Fetch details about a Pokémon by name",
{
name: z.string()
},
async handler({ name }) {
const data = await fetchPokemon(name);
const types = data.types.map((t: any) => t.type.name).join(", ");
return {
content: [{
type: "text",
text: `Pokémon: ${data.name}\nTypes: ${types}`
}],
}]
},
});
server.start();
console.log("MCP server running with get_pokemon tool...");
Now our server can respond to MCP requests.
9. Configure Claude Desktop
To connect Claude to the MCP server, edit its config file:
# On mac
~/Library/Application\ Support/Claude/claude_desktop_config.json
# On Linux
/etc/claude-code/managed-settings.json
# On Windows
C:\ProgramData\ClaudeCode\managed-settings.json
Add an entry for your server:
"pokedex": {
"command": "node",
"args": [
"/Users/dev/code/hollanddd/mcp-servers/pokedex/dist/index.js",
"--port",
"3000"
]
}
Save and close.
10. Build and Run
Compile your TypeScript:
pnpm run build
Restart Claude Desktop (it won’t pick up changes if it’s already running).
Now you should see the pokedex server listed, and you can test your new tool by asking Claude to fetch details about a Pokémon:
“Fetch details about Pikachu.”
11. Troubleshooting
Sometimes your server might not show up in Claude, or tools don’t respond as expected. Here are some steps to debug:
Check the logs
Claude Desktop logs MCP server activity. You can tail the logs like this:
tail -n 20 -f ~/Library/Logs/Claude/mcp-server-pokedex.log
This shows the last 20 lines and streams new logs in real time.
Common issues
- Claude not detecting the server → Make sure you restarted Claude Desktop after editing the config.
- Port conflicts → Ensure no other process is running on the same port (e.g.,
3000). Change the port in config if needed. - Build errors → Run
pnpm run buildagain and check for TypeScript errors. - JSON config errors → Verify that
claude_desktop_config.jsonhas valid JSON syntax.
🎉 Done
You’ve built and connected your first MCP server:
- Set up a TypeScript project
- Created an MCP server with
@modelcontextprotocol/sdk - Added a tool that integrates with the PokéAPI
- Connected it to Claude Desktop
- Debugged with log tailing
From here, you can expand your server with more tools, custom APIs, and even local integrations.