How to build an MCP server

Building an MCP server means writing a program that speaks JSON-RPC over one of the standard transports and describes its own capabilities well enough that a model can decide when to use them. The protocol is deliberately small — its stated design principle is that servers should be extremely easy to build — and the official SDKs handle the wire format for you.

This covers the decisions and the rules rather than one SDK's syntax, which changes. The architecture page explains the structure this assumes.

Decide these first

What it wraps, and how narrowly
Focused servers compose better than sprawling ones, which is one of the protocol's design principles. A server for one system with a handful of well-named tools beats a catch-all that does six unrelated things.
Which primitives you need
Tools are actions the model can invoke; resources are read-only context it can pull in; prompts are reusable templates you offer the client. Many servers only ever need tools, and adding the others because they exist is how a simple server stops being simple.
stdio or HTTP
stdio is the default choice — the client runs your program as a subprocess and nothing is exposed to the network. Reach for Streamable HTTP only when the server genuinely has to run somewhere else and serve more than one client.
Which language
Official SDKs exist for the common languages, and the SDK you pick mostly determines how people will install your server: a Node package is run with npx, a Python one with uvx. Pick the ecosystem your users already have.

Describing your tools

The single thing that most decides whether your server is any good is how you name and describe its tools. The model chooses what to call based only on those descriptions, so each one should say what the tool does, when to reach for it, and what its arguments mean — the same discipline a good function signature needs, written for a reader who cannot see your code.

What the protocol requires of you

These are specification requirements, not style preferences — ignoring them produces servers that break sessions or expose users:

Keep stdout clean (stdio)
On the stdio transport your server must not write anything to standard output that isn't a valid MCP message, and messages must not contain embedded newlines. A stray print statement corrupts the stream and breaks the session — use standard error for logging, which clients may capture or ignore.
Validate the Origin header (HTTP)
Servers using Streamable HTTP must validate Origin on every incoming connection. Without it, a web page the user visits can reach a server running on their machine through DNS rebinding.
Bind to localhost when local (HTTP)
A locally-run HTTP server should listen on 127.0.0.1 rather than 0.0.0.0, so it isn't reachable from the rest of the network.
Never pass tokens straight through
If your server sits in front of a third-party API, it must not accept tokens that weren't issued for it and forward them on. The specification forbids this outright — it breaks audit trails and lets a stolen token use your server as a proxy.
Declare only what you implement
Capabilities are negotiated at initialization and both sides must honour what they declared. Advertising a capability you haven't built produces failures the client has no way to anticipate.

If your server can modify anything, offer a read-only mode and consider making it the default. It costs little to implement, it's one of the signals this directory scores on, and it lets cautious users adopt your server before they fully trust it.

Mistakes worth avoiding

Logging to stdout
The most common way a first server fails. Everything not an MCP message belongs on stderr.
Vague tool descriptions
A tool called run with the description "runs the thing" will be invoked at the wrong moments or not at all. Write descriptions for the model, not for a human who already knows the codebase.
Returning enormous results
Every result lands in the model's context. A tool that returns an entire table crowds out the conversation — paginate, summarise, or accept a limit argument.
Unhelpful errors
The model reads your error text and decides what to do next. "Error 500" leads nowhere; "no record with that id — try searching by name first" lets it recover on its own.
Shipping without a licence
Unlicensed code has unclear usage terms, so cautious users and companies can't adopt it. It's also a scored signal here, and one of the easiest to fix.

Publishing it

When it's ready, what people need from you is small: a README that states plainly what the server does, the exact config entry to paste, every environment variable it expects, and what access it needs and why. Servers that are hard to install are usually servers whose README skipped one of those four.

Curious how yours would score? Run it through the scanner — it reads the same public signals as our scoring methodology.

Frequently asked questions

How hard is it to build an MCP server?

A server exposing two or three tools over an API you already know is a small piece of work — the SDK handles the protocol, so most of what you write is the code that does the actual job plus good descriptions of it. Making one people trust takes longer than making one that runs: scoping permissions, handling errors usefully, and documenting it honestly is most of the effort.

What language should you write an MCP server in?

Whichever your users already have installed, because that decides how painlessly they can run it. Node servers are launched with npx and Python ones with uvx, which means no separate install step for the people adopting yours. Beyond that the protocol is language-agnostic — it only specifies the messages.

Do you need to implement the HTTP transport?

Not usually. stdio covers the case where the client runs your server locally, and the specification tells clients they should support it wherever possible. Streamable HTTP matters only if the server has to run somewhere else and serve multiple clients — and it brings requirements stdio doesn't, including Origin validation and authentication.

How do you test an MCP server?

Two levels. The tools themselves are ordinary code and can be unit-tested like any other function. The part worth testing separately is whether a model actually picks the right tool from your descriptions — which means connecting the server to a real client and watching what it chooses when you phrase a request the way a user would.

How do you publish an MCP server so people can find it?

Put it in a public repository with a licence and a README carrying the exact config entry, the environment variables it needs, and the access it requires. Publishing to the package registry for your language is what makes the one-line npx or uvx install work. Directories, including this one, index public repositories that look like real MCP servers.