Back to blogTutorials

How to Build Your First MCP Server

By DReview Team · Published July 18, 2026

Key takeaways

A practical walkthrough for building a working MCP server with the official TypeScript SDK — from scaffolding a project to testing it in the MCP Inspector and wiring it into Claude Desktop.

If you've spent any time in an AI coding assistant lately, you've probably noticed it can now read your files, run your tests, or query your database directly from chat. That capability almost always comes from a Model Context Protocol (MCP) server — a small program that exposes a specific set of tools to an AI agent over a standard interface. Building your own is far more approachable than it sounds, and this guide walks through the whole process using the official TypeScript SDK.

What an MCP server actually does

An MCP server is not a chatbot and it doesn't call an LLM itself. It's a thin adapter: it declares a list of "tools" (functions with typed inputs), and when a connected client — Claude Desktop, Claude Code, Cursor, or any other MCP-compatible agent — decides a tool is relevant, it sends a structured call to your server and gets a structured result back. Your server's only job is to do the actual work: hit an API, read a file, query a database, and return the result in a format the model can reason about.

This separation is what makes MCP useful. You write the integration once, and it works in every client that speaks the protocol, instead of building a bespoke plugin for each AI tool separately.

Prerequisites

You'll need Node.js 18 or later and a package manager (npm, pnpm, or yarn all work). No prior protocol knowledge is required — the SDK handles the JSON-RPC transport, message framing, and capability negotiation for you.

Step 1: Scaffold the project

Create a new directory and install the official SDK along with the schema validation library it expects:

mkdir my-mcp-server && cd my-mcp-server
npm init -y
npm install @modelcontextprotocol/sdk zod
npm install -D typescript @types/node

Add a minimal tsconfig.json targeting ES2022 with "module": "Node16" and "moduleResolution": "Node16" — the SDK ships as native ESM, so your build needs to match.

Step 2: Define a tool

Every tool needs three things: a name the model will reference, a description that tells the model when to use it, and an input schema so the model knows what arguments to provide. Here's a complete server exposing one tool that fetches the current weather for a city:

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-server",
  version: "1.0.0",
});

server.registerTool(
  "get_weather",
  {
    title: "Get Weather",
    description: "Get the current weather conditions for a given city",
    inputSchema: { city: z.string().describe("City name, e.g. 'Austin, TX'") },
  },
  async ({ city }) => {
    const res = await fetch(`https://wttr.in/${encodeURIComponent(city)}?format=3`);
    const text = await res.text();
    return { content: [{ type: "text", text }] };
  }
);

const transport = new StdioServerTransport();
await server.connect(transport);

That's a working MCP server. The description field matters more than it looks — the model reads it to decide whether your tool is relevant to the user's request, so write it the way you'd explain the tool to a new teammate, not as a code comment.

Step 3: Choose a transport

The example above uses stdio, the right choice for a server that runs locally, spawned as a subprocess by the client (this is how most desktop AI tools launch local MCP servers). If you need a server reachable over a network — for a hosted integration, or to share one server across multiple users — the SDK also supports Streamable HTTP, which runs as a standard Express-style HTTP server and handles multiple concurrent client sessions. Start with stdio; only move to HTTP once you actually need remote access.

Step 4: Test it locally

Run npx @modelcontextprotocol/inspector node dist/index.js to launch the official MCP Inspector, a browser-based tool that connects to your server, lists its tools, and lets you invoke them manually with test inputs before wiring up a real client. This step catches schema mistakes and malformed responses far faster than debugging inside an actual chat session.

Once it works in the Inspector, add it to a real client. For Claude Desktop, that means adding an entry to claude_desktop_config.json:

{
  "mcpServers": {
    "weather-server": {
      "command": "node",
      "args": ["/absolute/path/to/dist/index.js"]
    }
  }
}

Restart the client and the tool should appear in its tool list.

Common mistakes to avoid

Vague tool descriptions. "Does stuff with files" gives the model nothing to work with. Be specific about what the tool does and doesn't do, and mention constraints (file size limits, supported formats) directly in the description.

Returning raw JSON blobs as text. The model can parse JSON, but a wall of unstructured data burns context and attention. Summarize or structure your responses so the useful information is easy to extract.

No error handling. If an API call fails, return a clear text error in the tool result rather than throwing — an unhandled exception looks like a broken connection to the client, while a returned error message lets the model explain the failure to the user and possibly retry with different arguments.

Overly broad tools. A single "do_anything" tool with a dozen optional parameters is harder for the model to use correctly than several small, well-named tools. Split by intent, not by convenience.

Publishing your server

Once your server works, package it so others can install it with one command. Publish to npm and document a stdio config block in your README — that's the pattern nearly every MCP server on this directory follows, and it's what lets someone go from README to working install in under a minute. If your server needs credentials (an API key, OAuth token), document the required environment variables clearly and never hardcode secrets into example configs beyond an obvious placeholder.

From there, submit it — [DReview](/) is a free, curated directory of real, open-source MCP servers and AI Skills, and adding your own is one of the best ways to get it in front of developers already searching for exactly what you built.

FAQ

Do I need to know the Model Context Protocol spec in detail to build a server?

No. The official SDKs (TypeScript and Python) handle the JSON-RPC transport, message framing, and capability negotiation for you. You only need to define tools, resources, or prompts using the SDK's high-level API.

What's the difference between stdio and HTTP transport?

Stdio is for local servers spawned as a subprocess by the client (the default for desktop AI tools). Streamable HTTP is for servers that need to be reachable over a network, such as a hosted integration shared across multiple users. Start with stdio unless you have a specific reason to need remote access.

How do I test an MCP server before connecting it to Claude or Cursor?

Use the official MCP Inspector: run `npx @modelcontextprotocol/inspector node dist/index.js` to open a browser UI that lists your server's tools and lets you invoke them manually with test inputs.

Can I submit my own MCP server to DReview?

Yes — DReview is a free, curated directory of real, open-source MCP servers and Skills. Once your server is published (with a working README and install config), you can submit it through the Submit a Tool page.