AI Catchup

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 --version to 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:

  1. McpServer creates your server instance with a name and version.
  2. server.tool() registers a tool with a name, description, input schema (using Zod), and a handler function.
  3. The handler function receives validated inputs and returns a response with typed content blocks.
  4. StdioServerTransport connects 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 cwd and args paths in your MCP configuration point to the correct location of your built files.
  • Build errors: Run npx tsc and 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.js if needed.
  • Console output: Avoid using console.log() in your server -- MCP communicates over stdio, so any stray output can break the protocol. Use console.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.