Building an MCP Server
— connecting your tools to AI
MCP has become the "USB-C of AI" — one protocol that connects any model to any tool. This guide will not stop at concepts: we will build your own MCP server step by step — tools, resources, prompts, the right transport, connecting to Claude and agents, and the security pitfalls you must know.
What an MCP server is, and why build one
Model Context Protocol (MCP) is an open protocol that defines how a language model (or agent) talks to external sources — databases, APIs, files, internal systems. Instead of writing a dedicated integration for each model, you write one MCP server, and any client that supports MCP (Claude Desktop, IDEs, agents) can use it immediately.
When is it worth building your own server? When you have an internal capability you want to expose to AI — for example access to the company CRM, database queries, or triggering a workflow inn8n. The server wraps the capability in a standard interface, and the AI gets safe, controlled access to it.
MCP has two sides: thehost/client (Claude Desktop, an agent) that manages the conversation, and theserver (what you build) that exposes capabilities. They communicate over JSON-RPC. You build the server; the client already exists.
The three primitives: Tools, Resources, Prompts
An MCP server exposes three kinds of capability. Understanding the distinction matters — it determines how the model uses each one:
Transport — stdio vs HTTP
How do the client and server physically talk? There are two main options, and it is an important architectural decision:
| Transport | Where it runs | Best for |
|---|---|---|
| stdio | Local, on the same machine | Personal tools, Claude Desktop |
| Streamable HTTP | A remote server | A multi-user service, cloud |
stdio — the server runs as a local process and communication is over stdin/stdout. The simplest to start with, perfect for a personal tool running alongside Claude Desktop. Streamable HTTP — the server runs remotely and is reachable over HTTP; suited to serving multiple users or deploying to the cloud. Start with stdio, move to HTTP when you need scale.
Build your first server — with the official SDK
The easiest path is the official SDK. In Python, FastMCP lets you define a tool with a single decorator — the schema is derived automatically from the type hints and the docstring:
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("weather-server")
@mcp.tool()
def get_weather(city: str) -> str:
"""Returns the current weather for a given city."""
# the real logic goes here — a call to a real API
return f"In {city}: 24°C, clear"
if __name__ == "__main__":
mcp.run() # default: stdio transport
That is it — you have a working MCP server with one tool. Three principles that make a tool good:
- A clear description — the docstring is what the model reads to decide when to use it. Write it as if explaining to a colleague, not a machine.
- A precise schema — type hints define exactly which parameters are allowed. The tighter it is, the fewer errors.
- A concise output — everything a tool returns enters the model context and costs tokens. Return what is needed, not a full dump.
Before connecting to a client, test the server with the MCP Inspector — an official tool that runs the server and lets you call tools manually and see the responses. It saves hours of debugging against Claude.
Connecting to Claude & agents
Once the server works, you connect it to a client. Claude Desktop reads a JSON config file where you register the server’s launch command — the client runs it automatically and discovers the tools. Agents (LangGraph, the Claude Agent SDK and others) can connect to MCP servers as a source of tools, so the same server serves both a human chat and automation.
This is exactly the power of MCP: you wrote the server once, and now it is available to Claude Desktop, an IDE, and any agent you build — without duplicating code. Theagent’s framework only needs to know how to speak MCP.
Security & common pitfalls
An MCP server exposes real capabilities to AI — which also makes it an attack surface. These are the things you must not miss:
- Least privilege — give the server only the access it truly needs. A server that reads from a database does not need delete permission.
- Input validation — the schema helps, but do not rely on it alone. Sanitize parameters before running a query or a system command (SQL/command injection is a real danger).
- Prompt injection via content — if a tool returns external content (a web page, an email), it may contain malicious instructions trying to "command" the model. See the prompt injection guide.
- Approval for sensitive actions — state-changing actions (sending, deleting, payment) need human approval, not automatic execution. See Agent Security.
- Secrets outside the code — API keys in environment variables, not in the server file. The server runs locally on the user’s machine — do not embed secrets in it.
It is tempting to expose dozens of tools "just in case". But every tool consumes tokens in context and confuses the model’s choice. A server with 5 focused tools beats one with 30. Expose only what is truly needed.