An MCP server is the piece that connects your data and your functions to any AI assistant that speaks the Model Context Protocol. In this guide you build one from scratch with the official Python SDK: you install the package, write a minimal server with FastMCP, expose tools, resources and prompts, and plug it into Claude, Cline or Goose through the stdio and HTTP transports. This is not the same as installing a ready-made MCP server: here you write your own. The same explanation is available in Spanish.

Key takeaways

  • The Model Context Protocol (MCP) is an open protocol that Anthropic introduced in November 2024 to connect language models with external data and tools through JSON-RPC[1] 2.0 messages.
  • An MCP server exposes three kinds of capability: tools (functions the model runs), resources (read-only data) and prompts (reusable templates).
  • The official Python SDK installs with pip install "mcp[cli]"; the stable version is 1.28.1 (June 2026) and it requires Python 3.10 or newer.
  • With the FastMCP class, type hints are the schema: you write no JSON Schema and no validation code, just functions decorated with @mcp.tool(), @mcp.resource() and @mcp.prompt().
  • A server communicates over stdio (local process) or Streamable HTTP (remote), and you register it in the assistant with a small JSON configuration.

What is the Model Context Protocol and why build your own server?

The Model Context Protocol is an open standard that defines how an AI application (the host) talks to external services that give it context and capabilities. The official documentation describes it as "a standardized way to connect LLMs with the context they need". The idea borrows the philosophy of the Language Server Protocol from code editors: instead of every assistant writing a different integration for each tool, they all speak the same protocol over JSON-RPC 2.0.

There are three roles in that architecture. The host is the application with the model (Claude, an editor running Cline, an agent built with the Anthropic SDK). The client lives inside the host and keeps a connection to each server. The server is what you build: a process that offers concrete data and actions, for example querying your internal database, reading a repository or calling a company API. Building your own makes sense when no public server covers your case: you want to expose a proprietary system, apply your own access rules or keep data inside your network.

What do tools, resources and prompts expose?

An MCP server offers the client up to three primitives, and it pays to understand the difference because the model treats them differently:

  • Tools: functions the model chooses to invoke in order to act. They take arguments, run code and return a result. They are the equivalent of function calling, but portable across assistants. They require user approval before running.
  • Resources: read-only data the host can load into the context, identified by a URI (for example file://report.txt). They resemble GET requests: they supply information, they do not run side effects.
  • Prompts: reusable message templates that the user invokes explicitly, useful for repeated flows such as "summarise this document in this format".

The rule of thumb is simple: if something changes the world or calls an API, it is a tool; if it only supplies information, it is a resource; if it is guiding text the user launches by hand, it is a prompt. The client can also offer capabilities to the server (sampling, roots and elicitation), but for a first server the three server-side primitives are enough.

How do you write a minimal MCP server with the Python SDK?

Start by installing the official SDK. The [cli] extra adds the mcp command, which brings the development inspector:

pip install "mcp[cli]"

If you use the uv package manager, the equivalent is uv add "mcp[cli]". With the SDK ready, create a server.py file. This example server exposes a tool that looks up a temperature and a resource with the list of available cities:

from mcp.server.fastmcp import FastMCP

mcp = FastMCP("weather")

@mcp.tool()
def temperature(city: str) -> str:
    """Return the current temperature of a city."""
    data = {"Madrid": 21, "Seville": 28, "Bilbao": 17}
    return f"{data.get(city, 15)} degrees in {city}"

@mcp.resource("cities://list")
def cities() -> str:
    """List of cities with available data."""
    return "Madrid, Seville, Bilbao"

if __name__ == "__main__":
    mcp.run(transport="stdio")

Notice what you did not write. There is no JSON Schema, no request parsing and no validation code: the type hints (city: str) are the schema, and the function’s docstring becomes the description the model sees. That is the advantage of FastMCP, the SDK’s high-level interface. Before wiring it into an assistant, test it with the inspector, which opens a web interface to invoke your tools by hand:

mcp dev server.py

How do you connect the server to Claude, Cline or Goose?

An assistant launches your server as a child process and communicates with it over its standard input and output. To register it, you add an entry to the host’s configuration file stating which command to run. In Claude Desktop it is claude_desktop_config.json; in Cline inside VS Code it is cline_mcp_settings.json; and Goose uses its own extensions file. The shape is the same in all three:

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

Always use the absolute path to the file and, if you work with a virtual environment, point command at that environment’s Python so the dependencies are available. When you restart the assistant, it starts your server, negotiates capabilities and discovers your tools and resources. From there the model can ask for the temperature in Bilbao, and the user approves the execution before it happens. With Claude Code the registration is even more direct: the claude mcp add command adds the server without editing the JSON by hand.

When should you use the stdio transport and when HTTP?

MCP defines two transports. The stdio one is what you used: the host launches the server as a local subprocess and they talk over pipes. It is the default option for servers that run on the same machine as the assistant, with no ports or network authentication. The Streamable HTTP one serves the server over the network and replaced the old HTTP+SSE transport; it is what you need if the server lives on another host or several users share it. Switching from one to the other is a single line:

if __name__ == "__main__":
    mcp.run(transport="streamable-http")

With HTTP, FastMCP publishes the server at http://localhost:8000/mcp by default. Bear in mind that exposing a server over the network means adding authentication and access control: an MCP server runs code and reaches data, so treat each tool as an attack surface and require explicit user consent before every action, just as the specification recommends. The most recent revision of the protocol is the one dated 25 November 2025, and the Python SDK has around 23,600 stars on GitHub, a sign of an already mature ecosystem.

Frequently asked questions

How is this different from installing a ready-made MCP server?

In that here you write the server yourself. Installing an MCP server means downloading a process someone else programmed (for GitHub, Slack or your file system) and registering it in your editor. Building your own is the option when no public server exposes what you need: an internal system, a private API or your own access rules. The Python SDK lets you go from idea to a working server in about twenty lines.

Do I need FastMCP or the low-level SDK?

For almost everything, FastMCP. It is the official SDK’s high-level interface and it resolves the schema, the parsing and the protocol for you from the type hints. The low-level SDK (mcp.server.Server) only pays off when you need to control protocol details that FastMCP hides. Worth knowing: the SDK’s 2.0 line, in beta during 2026, renames the FastMCP class to MCPServer; in the meantime, 1.x is the stable version recommended for production.

Is it safe to expose tools to a model?

Only if you design it carefully. A tool is code that runs, so the protocol requires the user to approve each invocation and the host to obtain consent before sharing data. Always validate the arguments that arrive, limit what each tool can touch and do not trust the descriptions of a third-party server. If you serve the server over HTTP, add authentication and do not expose it open to the Internet.

Conclusion

Building your own MCP server with the Python SDK is surprisingly direct: you install mcp[cli], define tools, resources and prompts as typed functions with FastMCP and choose the stdio or HTTP transport depending on where it runs. From that minimal server you can grow toward tools that query your database, resources that read your internal documents or flows that integrate a company API, and connect it to any assistant that speaks the protocol. The natural next step is to give your server a real tool and test it from Claude or Cline before taking it to your team.

Sources

  1. JSON-RPC
  2. Official Model Context Protocol specification
  3. MCP Python SDK on GitHub
  4. mcp package on PyPI
  5. MCP SDK documentation

Route: Agent Ecosystem: MCP, Gateways and Platforms