Short answer To build an MCP server: install an official MCP SDK, declare your tools with typed inputs, optionally expose resources and prompts, run the server over stdio or HTTP, then connect an MCP client like Claude and test it.
A minimal Python server is about ten lines; the work is in choosing what to expose and validating every input.
This is the build.
For what MCP is, its three primitives, and how it differs from an API, start with what is the Model Context Protocol — this page assumes that and goes straight to code.
Prerequisites You need very little to get a server running locally: A language with an official SDK.
Python and TypeScript are the most mature; the same protocol is also implemented for other languages.
This guide uses the Python SDK (the secondary path most people search for), with notes on where the TypeScript SDK is equivalent.
Python 3.10 or newer and uv (recommended) or to manage the environment.
An MCP client to test against — Claude Desktop, or the MCP Inspector that ships with the SDK.
You do not need cloud credentials to build or run the server itself.
Conceptually a server exposes three things — tools (model-callable functions), resources (readable data), and prompts (reusable templates).
The steps below add them in that order.
Exact SDK signatures evolve, so treat the snippets as the current shape and check the live docs before shipping.
Which spec revision this builds against.
The code here targets MCP revision — the revision the spec's versioning page still names as the current protocol version.
Revision is published and reworks the wire format substantially.
A server built against stays conformant today; what the new revision changes for a server author is set out below, so you can build now and plan the move.
Step 1: scaffold the server Create a project, install the SDK, and write the smallest server that runs.
With : Then create with the server object and an entry point. is the high-level Python API: you name the server and run it over a transport.
That already runs — it just exposes nothing yet.
In the TypeScript SDK the equivalent is creating an from and connecting it to a transport; the structure is the same, only the syntax differs.
Step 2: define a tool A tool is a function the model can decide to call.
In the Python SDK you decorate a normal typed function: the type hints become the input JSON schema and the docstring becomes the description the model reads to decide when to call it.
Real tools wrap something useful — a database query, an internal API, a file operation.
The handler is just a function, so this is where you call your existing service and return a result the model can use: Two things matter here, both architectural: the schema is the contract the model fills in, so keep argument names and descriptions precise; and the function runs with whatever privileges the process has, so scope it to the minimum it needs.
We come back to that in Step
5.
Step 3: add resources and prompts Tools take actions; resources expose data the model can read, and prompts are reusable templates.
Not every server needs all three — add what your use case calls for.
A resource is addressed by a URI.
It can be static, or a template with parameters the client fills in: A prompt is a parameterized template the server publishes so common workflows are authored once, not re-written in every client: The distinction is deliberate: a client may surface resources and prompts to the user (to pull into context or pick from a menu) while letting the model invoke tools autonomously.
Keeping them separate is what makes a server predictable across clients.
Step 4: run and connect it There are two transports you will actually use: stdio — the server runs as a local subprocess the client launches.
This is the default for desktop clients and local development: .
Streamable HTTP — the server runs as a web service a remote client connects to, for hosted or shared servers: .
This is the transport revision reworks hardes