MCP Directory

How to Build an MCP Server (2026 Guide)

From an empty folder to a tool your agent actually calls — the short path, the real config, and the traps that waste an afternoon.

Hua·June 30, 2026·6 min read
Detailed macro shot of a CPU microchip with focus on golden pins, highlighting technology details.
Photo by Pixabay on Pexels

To build an MCP server you pick a language SDK, write a function, wrap it with a tool decorator, and run it over stdio. That's the whole job for the common case — everything else is polish. This guide takes you from an empty folder to a server your agent actually calls, using real code and the config that gets it loaded.

Before you write anything, know what you're building against: an MCP server is a small program that exposes tools, resources, or prompts to an AI client over a defined protocol. If that sentence needs unpacking, read what an MCP server is first, then come back. The rest of this assumes you know why you want one.

Step 1: Pick an SDK (don't overthink it)

Use the Python or TypeScript SDK unless your host app forces another language. Both are official, both generate the JSON-RPC framing for you, and both ship a high-level API that turns a typed function into a registered tool. Reach for Go, Rust, or C# only when you're embedding the server inside an existing app in that language.

The deeper choice inside Python is FastMCP versus the low-level SDK, and the honest default is FastMCP — it's the same decorator style, and the official SDK even vendors FastMCP v1 as mcp.server.fastmcp.FastMCP. I've written the full decision in FastMCP vs the MCP SDK; the one-line version is below.

ChoiceUse whenSkip when
FastMCP / high-level SDKYou're exposing tools and want to write logic, not plumbingYou need a custom event loop or client-side protocol control
Low-level ServerBuilding a client, deep capability control, embeddingA standard tool server — it's more code for nothing
Non-Python SDKYour host app is Go/Rust/C#You're free to choose — Python/TS are shorter

Step 2: Expose one tool, correctly

Start with a single tool and let its type hints generate the input schema. A tool is just a function the model can call; the SDK reads your signature and docstring to tell the client what arguments it takes. Here's a complete, runnable server in Python:

from fastmcp import FastMCP

mcp = FastMCP("weather")

@mcp.tool()
def get_forecast(city: str) -> str:
    """Return today's forecast for a city."""
    return fetch_forecast(city)

if __name__ == "__main__":
    mcp.run()  # stdio by default

The TypeScript shape is the same idea — a server.tool(name, schema, handler) call — with a Zod schema instead of type hints. In both, the docstring or description is not decoration: it's the only thing the model reads to decide when to call your tool. Vague descriptions are the number-one reason a tool sits unused.

Three things that separate a toy from a real tool:

  • Return a string or structured content the model can read, not a raw object dump. Format errors as plain sentences.
  • Validate inputs and fail with a clear message — the model will retry on a good error and give up on a stack trace.
  • Name the tool for intent (get_forecast, not handler). The name is part of the prompt.

Step 3: Choose your transport (stdio, almost certainly)

Run over stdio unless you specifically need a network-accessible server. Roughly 90% of the servers we track run locally over stdio — the client launches your process and talks to it over stdin/stdout, so there's no port, no auth, and no deployment. That's what mcp.run() gives you with zero arguments.

Switch to HTTP only when the server must live somewhere else: a shared team instance, a hosted service, or a browser-based client. Then it's mcp.run(transport="http") and you inherit real concerns — auth, TLS, rate limits. The trade-off between the two is laid out in local vs remote MCP; for a first server, stdio is the right answer and you can promote it later.

Step 4: Test with the MCP Inspector before wiring it to an agent

Test your server in the MCP Inspector first — never debug protocol issues from inside a chat client. The Inspector is the official web tool that launches your server, lists its tools, and lets you call them by hand:

npx @modelcontextprotocol/inspector python server.py

It shows you exactly what the client sees: the tool list, each input schema, and the raw result of a call. If a tool doesn't appear here, it won't appear in Claude or Cursor either — so this is where you catch a bad decorator, a schema that won't serialize, or a server that crashes on startup. Iterate here until every tool calls cleanly, then connect it to an agent. Skipping this step is how people spend an afternoon blaming their editor. If a tool still misbehaves once connected, the troubleshooting guide covers the usual client-side causes.

Step 5: Add it to a client

Once the Inspector is green, register the server in your client's config with the command that launches it. For a stdio server that's a command and args entry:

{
  "mcpServers": {
    "weather": {
      "command": "python",
      "args": ["/absolute/path/to/server.py"]
    }
  }
}

Use an absolute path — a relative one breaks the moment the client's working directory differs from yours. The step-by-step for each editor is in how to add an MCP server, and if you'd rather not hand-write JSON, the config generator produces the exact block for your client.

What to skip: don't ship thirty tools

Expose the fewest tools that cover the job — the client, not your server, sets the ceiling. Cursor and similar clients degrade past roughly a 40-tool budget across every connected server combined. A user who installs yours plus four others is near that limit before your server even loads. The math is in Cursor's tool limit.

So fold read variants into one parameterized tool, drop tools the model never picks, and resist the urge to mirror an entire API surface. Good reference points: the official Filesystem server does a lot with a tight set of read/write tools scoped to allowed directories, Context7 is essentially one job — inject current library docs — done well, and the GitHub MCP Server shows how a large surface gets organized when it's genuinely needed. Study a few on the best MCP servers list before you finalize your tool set; the bar for what "good" looks like is set by what's already shipping.

One last note on trust: label your server honestly as community or official, and if it touches the filesystem, network, or credentials, scope those permissions tightly. Users are right to be cautious about what they install — MCP security, what actually matters is worth a read before you publish anything that others will run.

FAQ

Do I need to know the MCP protocol to build a server?

No. The Python and TypeScript SDKs generate the JSON-RPC framing and input schemas for you, so a working tool server is about ten lines. You only touch the raw protocol when building a client or when you need control the high-level API doesn't expose.

Is building an MCP server free?

Yes. The official MCP SDKs and FastMCP are open source and free, and a local stdio server costs nothing to run — the client just launches your process. You only pay for hosting if you deploy a remote HTTP server, plus whatever APIs your tools call.

What language should I build an MCP server in?

Python or TypeScript for most people — both have official, mature SDKs with a high-level tool API. Use Go, Rust, or C# only when you're embedding the server in an existing app written in that language. The protocol is identical across languages; only the framework ergonomics differ.

How do I test an MCP server?

Use the MCP Inspector: run `npx @modelcontextprotocol/inspector <your command>`. It launches your server, lists every tool with its schema, and lets you call each one by hand. If a tool works in the Inspector it will work in Claude or Cursor; if it doesn't appear there, fix it before connecting to any client.

How many tools should my server expose?

As few as cover the job. Clients like Cursor degrade past roughly 40 tools across all connected servers combined, so a bloated server crowds out the user's others. Fold read variants into one parameterized tool and drop any the model never selects.

Put this into practice

Browse MCP servers by capability, or check your own setup's tool budget and security.

More essays