Creating an MCP server means writing a small program that exposes functions as tools an AI client can discover and call — you define each tool with a name, a description and a typed input schema, serve it over a transport, and any MCP-capable host (Claude, Cursor, VS Code, Cline) can use it. The official SDKs do the protocol work; your job is the tools and their descriptions.
Before you write code: what should it expose?
An MCP server is worth building when you have a capability a model should be able to invoke on its own — query your database, hit your internal API, act on your product, or fetch live data. Start by listing the two or three actions that matter, not everything possible. A focused server with four well-described tools beats a sprawling one with forty the model can't choose between. If you are wrapping an existing API, the tools usually map to its most useful endpoints, reshaped for how a model thinks about the task rather than how the API is organized.
Step 1: pick the SDK and transport
Official SDKs exist for TypeScript and Python and handle the JSON-RPC protocol, the handshake and message routing for you. Choose the language your backend already lives in. Then pick a transport: stdio if the server runs locally and the client launches it as a subprocess (the common case for developer tools), or streamable HTTP if it runs remotely and clients connect over the network. Start with stdio — it is the simplest to develop and test.
Step 2: define a tool
A tool is a function plus metadata. In the Python SDK it is close to a decorated function:
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("price-tools")
@mcp.tool()
def get_price(sku: str) -> dict:
"""Get the current price and stock for a product SKU.
Call this when the user asks about a specific product's price
or availability."""
row = db.lookup(sku) # your real logic
return {"price": row.price, "in_stock": row.stock > 0}
if __name__ == "__main__":
mcp.run() # stdio by defaultThree things carry the weight: the type hints become the input schema the client validates against, the docstring becomes the description the model reads to decide whether to call the tool, and the return value is what lands back in the model's context. Get the docstring right and the model calls the tool correctly; leave it vague and the tool sits unused.
Step 3: run and connect it to a client
Register the server in your client's config. For Claude Desktop or Claude Code that is a small JSON entry naming the command that launches it:
{
"mcpServers": {
"price-tools": {
"command": "python",
"args": ["/path/to/server.py"]
}
}
}Restart the client, and your tools appear. Cursor, VS Code and Cline use the same shape with their own config location. The client handles the initialize handshake and tools/list automatically — you just see the tool become available to the model. We break down that request flow in how MCP servers work.
The rules that decide whether your tools get used
- Write descriptions for the model, not the docs. State when to call the tool, not just what it does — "Call this when the user asks about current prices" outperforms "Returns price data." This single habit moves the needle most.
- Keep inputs flat and typed. Simple, well-named parameters with clear types are chosen correctly far more often than nested config objects.
- Return structured, compact results. Give the model what it needs, not your API's full envelope. Trim noise before it hits the context window.
- Handle errors as data. Return a clear error message rather than throwing — the model can read it and retry or adjust.
- Scope credentials tightly. The server runs with real permissions and is called by a model reading untrusted content; give it only the access its tools need, and gate irreversible actions.
Wrapping an existing API vs building capability
Most first MCP servers wrap something that already exists — an internal API, a SaaS, a database. That is the fast, high-value path: the capability is proven, you are just adding the discoverable, model-friendly interface on top. Building genuinely new capability (a browser that navigates, a data collector that fetches the live web) is a bigger job, which is why many teams connect an existing web-data server instead of writing one. Our MCP server is exactly that shape — a thin protocol adapter over a web-data API, giving an agent search, scrape, crawl and map as tools without you building the fetch, proxy and parsing stack. If your server needs to reach the open web, wrapping a capability like that is faster than reinventing it; if it needs your proprietary data, you build it, and the pattern above is the whole recipe. The docs show the client setup for each host.