How to Build Your Own MCP Server: A Beginner's Guide
Building an MCP server involves three steps: scaffold a TypeScript project with the MCP SDK, define tools that expose your desired functionality, and test the server locally with Claude Code or another MCP client. The entire process takes under an hour for a basic server.
How to Build Your Own MCP Server: A Beginner's Guide
MCP servers are the fastest way to give your AI assistant new capabilities. Whether you want to connect it to an internal API, a proprietary database, or a custom workflow, building your own MCP server is the answer.
The good news: it is much simpler than you might expect. A basic MCP server is under 100 lines of TypeScript, and you can have one running in under an hour.
Key Takeaways
- MCP servers are just TypeScript (or Python) programs that follow a specific protocol. No special infrastructure required.
- The MCP SDK handles all the protocol details for you. You focus on defining tools and their logic.
- Local testing is fast. Configure your AI tool to point at your server and start using it immediately.
- You can expose tools (actions), resources (data), and prompts (templates) from a single server.
- Start simple with one tool, then expand as you learn the patterns.
What You Will Build
In this guide, you will build a simple MCP server that provides a weather lookup tool. When finished, you will be able to ask your AI assistant "What is the weather in San Francisco?" and it will use your server to fetch and return real weather data.
This example is intentionally simple, but the patterns you learn apply to any integration: internal APIs, databases, file systems, cloud services, or any system you can call from code.
Prerequisites
Before you start, make sure you have:
- Node.js 18 or later installed (
node --versionto check) - An AI tool that supports MCP -- Claude Code is the easiest to configure, but Cursor and VS Code with Copilot also work
- Basic TypeScript familiarity -- you do not need to be an expert, but you should be comfortable with types and async/await
- A text editor -- any editor will do, though your AI coding tool can help you write the server itself
Step 1: Set Up the Project
Create a new directory and initialize a TypeScript project:
mkdir my-weather-mcp
cd my-weather-mcp
npm init -y
npm install @modelcontextprotocol/sdk zod
npm install -D typescript @types/node
The key dependency is @modelcontextprotocol/sdk, which provides the server framework and handles all MCP protocol communication. We also install zod for input validation, which is the recommended approach for defining tool parameters.
Create a tsconfig.json:
{
"compilerOptions": {
"target": "ES2022",
"module": "Node16",
"moduleResolution": "Node16",
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"declaration": true
},
"include": ["src/**/*"]
}
Create the source directory:
mkdir src
Step 2: Define Tools
Create src/index.ts -- this is your entire server:
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
const server = new McpServer({
name: "weather",
version: "1.0.0",
});
server.tool(
"get_weather",
"Get the current weather for a city",
{
city: z.string().describe("The city name to look up weather for"),
},
async ({ city }) => {
// In a real server, you would call a weather API here.
// For this example, we return mock data.
const response = await fetch(
`https://wttr.in/${encodeURIComponent(city)}?format=j1`
);
const data = await response.json();
const current = data.current_condition[0];
return {
content: [
{
type: "text" as const,
text: `Weather in ${city}: ${current.temp_F}°F, ${current.weatherDesc[0].value}. Humidity: ${current.humidity}%. Wind: ${current.windspeedMiles} mph.`,
},
],
};
}
);
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
}
main().catch(console.error);
Let us break down what is happening:
McpServercreates your server instance with a name and version.server.tool()registers a tool with a name, description, input schema (using Zod), and a handler function.- The handler function receives validated inputs and returns a response with typed content blocks.
StdioServerTransportconnects the server over standard input/output, which is how most AI tools communicate with MCP servers.
The tool description is important -- this is what the AI reads to decide when to use your tool. Make it clear and specific.
Adding more tools
You can register as many tools as you need. Each server.tool() call adds another capability. For example, you might add a forecast tool alongside the current weather tool:
server.tool(
"get_forecast",
"Get a 3-day weather forecast for a city",
{
city: z.string().describe("The city name"),
days: z.number().min(1).max(3).default(3).describe("Number of days"),
},
async ({ city, days }) => {
// Forecast implementation here
}
);
Step 3: Test Locally
First, build the project:
npx tsc
Now configure your AI tool to use the server. In Claude Code, add this to your project's .mcp.json file:
{
"mcpServers": {
"weather": {
"command": "node",
"args": ["./dist/index.js"],
"cwd": "/path/to/my-weather-mcp"
}
}
}
Restart Claude Code, and you should see the weather tool listed when you check available MCP tools. Try asking: "What is the weather in New York?"
The AI will recognize that this question can be answered by your get_weather tool, call it with city: "New York", and present the results in its response.
Debugging tips
If the server does not work immediately, here are the most common issues:
- Path errors: Make sure the
cwdandargspaths in your MCP configuration point to the correct location of your built files. - Build errors: Run
npx tscand fix any TypeScript errors before testing. The server must compile cleanly. - Permission errors: Ensure the compiled JavaScript file is readable. On Unix systems,
chmod +r dist/index.jsif needed. - Console output: Avoid using
console.log()in your server -- MCP communicates over stdio, so any stray output can break the protocol. Useconsole.error()instead for debug logging, as stderr does not interfere with the protocol.
Next steps
Once your basic server works, consider these enhancements:
- Add resources to expose static data or configuration that the AI can read without calling a tool.
- Add prompts to provide pre-built templates that guide the AI's use of your tools.
- Publish to npm so others can install and use your server with a single command.
- Add error handling with clear error messages that help the AI understand what went wrong and suggest fixes to the user.
The MCP ecosystem is built on servers like the one you just created. Every server you build makes your AI assistant a little more capable, and the community a little richer.
Frequently Asked Questions
What programming languages can I use to build an MCP server?
The official MCP SDKs are available for TypeScript and Python. TypeScript is the most popular choice due to its strong typing and the maturity of the Node.js SDK. Community SDKs also exist for Go, Rust, and Java, though they may not support all protocol features yet.
Do I need to deploy my MCP server to use it?
No. Most MCP servers run locally on your machine. Your AI tool starts the server process automatically when it needs to use it. Deployment is only necessary if you want to share the server across a team or run it in a CI/CD environment.
Get the weekly AI Catchup
Tools, practices, and what matters -- in your inbox every Monday.