MCP server tutorial Python — build your first one in 2026

Surya Koritala
18 Min Read

This MCP server tutorial Python walks through a complete first server using the official Python SDK and FastMCP, the framework recommended in the Model Context Protocol ecosystem. You’ll build a minimal tool server, add a resource and a prompt, wire it into Claude Desktop, and avoid the stdout logging mistake that breaks many first-time setups.

What we’re building, and why MCP matters

1.27.0

Current Python SDK version

Verified in the editor brief for April 2026

3

Core MCP primitives

Tools, Resources, Prompts

3

Supported transport categories

stdio, HTTP/SSE, WebSocket

3.10+

Required Python version

Needed for modern async features

Model Context Protocol, or MCP, is an open standard launched in November 2024 to standardize how LLMs connect to external tools, data, and prompts. The official docs describe three core primitives: Tools for callable functions, Resources for read-only data, and Prompts for reusable templates. In practice, that gives developers a consistent way to expose capabilities to AI clients instead of writing one-off integrations for every app or model surface.

This MCP server tutorial Python uses the official Python SDK and FastMCP, which ships with that SDK. As of April 2026, the current SDK version is mcp 1.27.0. The goal here is modest on purpose: build a tiny weather server that runs locally, then extend it with one resource and one prompt template. Once that works, you have the canonical pattern for a much larger server.

The reference client in this guide is Claude Desktop, because it is the canonical client covered in MCP documentation and ecosystem tutorials. We’ll use the default stdio transport first, since that is the best fit for local desktop integration and the easiest place to understand how an MCP server behaves.

Model Context Protocol website shown in a browser
Image: source page. Used under fair use.

This tutorial is based on the official MCP docs, the official Python SDK, and FastMCP. The verified SDK version in the editor brief is mcp 1.27.0.

“MCP is an open protocol that standardizes how applications provide context to LLMs.”

Model Context Protocol documentation
https://github.com/modelcontextprotocol/python-sdk
Official MCP Python SDK repository
https://github.com/jlowin/fastmcp
FastMCP framework repository
ConceptWhat it doesExample in this tutorial
ToolCallable function an agent can invokeget_weather(city)
ResourceRead-only data exposed to the clientconfig://app-settings
PromptReusable prompt templatesummarize_article(url)
The three MCP primitives used in this tutorial

Stage 1: Set up Python and install the SDK

Before you write any code, make sure you are on Python 3.10 or newer. That requirement matters: modern async features are not optional in this stack. Then install the two packages used in this guide: mcp and fastmcp.

If you are following this MCP server tutorial Python in a fresh project, create a virtual environment first. That keeps your MCP dependencies isolated from other Python work and makes it easier to point Claude Desktop at a stable interpreter path later.

You need Python 3.10+, pip, and a local file path you can reference from Claude Desktop.

python3 --version
python3 -m venv .venv
source .venv/bin/activate
pip install mcp fastmcp
How do I confirm the packages installed correctly?

Run pip show mcp fastmcp inside your virtual environment. You should see package metadata for both. If you are using multiple Python installations, verify that python and pip point to the same environment.

Stage 2: Build the minimal weather server

Now for the smallest useful server. The code below is a complete, working MCP server using FastMCP. It exposes one tool called get_weather. Save it as server.py and run it with python server.py.

This is the core pattern to remember from the MCP server tutorial Python: create a FastMCP app, decorate a function with @mcp.tool(), give the function proper type hints, and call mcp.run() in the main block. FastMCP uses those type hints to build the JSON schema for tool parameters.

# 1. Install: pip install mcp fastmcp
from mcp.server.fastmcp import FastMCP

mcp = FastMCP("weather-server")

@mcp.tool()
def get_weather(city: str) -> str:
    """Get current weather for a city."""
    return f"It's 18°C and foggy in {city}."

if __name__ == "__main__":
    mcp.run()
Why are type hints required in FastMCP?

FastMCP reads Python type hints to generate the JSON schema that clients use to understand tool inputs. In this example, city: str tells the client that the tool expects a string parameter named city. Without type hints, schema generation breaks down.

Stage 3: Add a resource for read-only data

Tools are only one part of MCP. Resources let you expose read-only data that a client can fetch without invoking a side-effecting function. In many real deployments, resources are a clean way to publish configuration, documentation, schemas, or internal reference material.

Here is the verified resource example from the brief. Add it to the same file under your tool. It exposes a static config resource at config://app-settings, which a client can read as part of its context flow.

@mcp.resource("config://app-settings")
def app_settings() -> str:
    """Static config resource exposed to the agent."""
    return '{"max_retries": 3, "timeout_ms": 5000}'
When should I use a resource instead of a tool?

Use a resource when the client needs read-only context rather than an action. Configuration, policy text, product docs, and static metadata are good fits. Use a tool when the client needs to perform work, call an API, or compute a result from user input.

Stage 4: Add a reusable prompt template

The third MCP primitive is prompts. A prompt template gives the client a reusable instruction pattern with parameters. That can be useful when you want a standard summarization, extraction, or transformation workflow available alongside your tools and resources.

In this MCP server tutorial Python, the prompt example takes a URL and returns a short instruction asking the client to summarize the article in three bullets. Add this below the resource or tool definitions.

@mcp.prompt()
def summarize_article(url: str) -> str:
    """Reusable prompt for article summarization."""
    return f"Read the article at {url} and summarize in 3 bullets."

Stage 5: Run the server and connect it to Claude Desktop

At this point, your file can expose a tool, a resource, and a prompt. Run it locally with python server.py. For desktop testing, the default stdio transport is the right choice. MCP also supports HTTP/SSE and WebSocket, but stdio is the simplest path for a first local integration.

To connect Claude Desktop, add your server entry to the Claude Desktop config file. The verified path in the brief is ~/Library/Application Support/Claude/claude_desktop_config.json. Replace the placeholder path with the absolute path to your local server.py file.

Use stdio for Claude Desktop. HTTP/SSE is better suited to web clients, while WebSocket is used by some newer IDE integrations.

{
  "mcpServers": {
    "weather": {
      "command": "python",
      "args": ["/absolute/path/to/server.py"]
    }
  }
}
How do I run an MCP server over HTTP?

The editor brief confirms that MCP servers can use HTTP/SSE for web-based clients. If you need browser-accessible or networked deployment, HTTP is the transport to explore next in the official docs and SDK examples. For a first server, stay on stdio until your tool definitions and schemas work reliably.

TransportBest use caseNote
stdioLocal desktop integrationDefault and best for Claude Desktop
HTTP/SSEWeb-based clientsUseful when exposing a server over HTTP
WebSocketSome IDE integrationsNewer option in the ecosystem
The three transport modes referenced in this tutorial

Stage 6: Avoid the four gotchas that break first servers

The biggest failure mode in a first local setup is logging to the wrong stream. In a stdio MCP server, stdout is reserved for JSON-RPC messages. If you call print("debug"), you can corrupt the protocol stream and make the client think your server is broken. That one mistake explains a lot of confusing first-run failures.

Use stderr for logging instead. A direct approach is print("debug", file=sys.stderr). Standard Python logging also helps because logging.basicConfig() defaults to stderr. The other two gotchas are just as important: type hints are required for FastMCP schema generation, and Python 3.10+ is required because the async features in this stack are not optional.

If you remember only one section from this MCP server tutorial Python, make it this one. When a server seems to start but the client cannot talk to it, stdout contamination is the first thing to check.

Do not print debug output to stdout when using stdio transport.

import sys
import logging

print("debug to stderr", file=sys.stderr)
logging.basicConfig(level=logging.INFO)
logging.info("logging also goes to stderr by default")
What does stdout corruption look like in practice?

A client may fail to initialize, show a generic connection error, or behave as if your tool schema is invalid. The server process itself can still appear to be running, which makes this bug easy to misdiagnose. Remove all plain print() calls or redirect them to sys.stderr.

GotchaWhy it mattersFix
Printing to stdoutCorrupts JSON-RPC over stdioLog to stderr instead
No type hintsBreaks parameter schema generationAnnotate every tool parameter and return type
Old Python versionMissing required async featuresUse Python 3.10+
Wrong transport assumptionClient cannot connect as expectedUse stdio for Claude Desktop first
The four first-run issues most likely to derail a basic MCP server
Never print to stdout on stdio transport

Stage 7: Keep the advanced deployment questions separate

Once your local stdio server works, you can think about deployment and hardening. The key is not to mix those concerns into your first build. Get the basic server stable, verify the client can see your tool, resource, and prompt, and only then move to HTTP exposure, process supervision, validation, and auth.

For teams using this MCP server tutorial Python as a starting point for internal tooling, the production checklist is straightforward: supervise the process, validate inputs more strictly than bare type hints, authenticate networked endpoints, and rate-limit calls if the server fronts a paid API.

Pros
  • systemd or supervisor for auto-restart
  • Pydantic for stronger input validation
  • Authentication for HTTP-exposed servers
Cons
  • More moving parts than a local stdio setup
  • Network exposure increases security requirements
  • Validation and rate limiting add implementation overhead
Production deployment with systemd: what should I do first?

The editor brief recommends running production servers with systemd or supervisor for auto-restart. That gives you process supervision, restart policies, and better operational hygiene than a manually launched shell process.

MCP authentication patterns: what changes over HTTP?

If you expose an MCP server over HTTP, implement proper authentication before opening it to broader networks. The brief does not prescribe a single auth scheme, so the safe guidance is to treat a networked MCP server like any other API surface: authenticate requests, restrict access, and audit usage.

How should I validate inputs beyond simple type hints?

The brief recommends pydantic for validation beyond type hints. Type hints help FastMCP generate schemas, but they are not a full substitute for stricter validation rules, bounds checks, and structured error handling in production.

When do I need rate limiting on tool calls?

Rate-limit tool calls when your server is backed by paid APIs or any dependency with quota constraints. That protects both cost and reliability, especially if multiple users or automated agents can trigger the same tool repeatedly.

Where to go from here

You now have the basic shape of a real MCP server: one tool, one resource, one prompt, and a Claude Desktop config that can launch it locally. That is enough to start replacing hard-coded demos with something useful, whether that means wrapping an internal API, exposing product documentation as resources, or standardizing prompt templates for recurring workflows.

The next practical step after this MCP server tutorial Python is to swap the fake weather response for a real backend call and keep the same server structure. From there, read the official MCP docs, the Python SDK repository, and the Anthropic course to understand richer patterns, transports, and deployment options. Keep your first iterations boring: typed inputs, stderr logging, stdio transport, and one client. That discipline will save you time when the server grows.

Replace the placeholder tool body with a real API call, then add validation and better error handling.

Frequently asked questions

What is MCP in plain English?

MCP stands for Model Context Protocol, an open protocol for connecting LLMs to tools, data, and prompts. The official overview is at modelcontextprotocol.io.

Do I need FastMCP to build a Python MCP server?

FastMCP is the recommended framework in this tutorial and the editor brief notes that it ships with the official SDK. You can review the official Python SDK at GitHub and FastMCP at gofastmcp.com.

Why does printing to stdout break my server?

With the stdio transport, stdout carries JSON-RPC protocol messages between client and server. Any stray output can corrupt that stream. Use stderr for logs instead; the MCP docs hub is here.

Which client should I test with first?

This guide uses Claude Desktop as the reference client because the editor brief identifies it as the canonical client for this workflow. For broader background, see the official MCP site at modelcontextprotocol.io and Anthropic’s course at Skilljar.

Primary sources

Last updated: May 23, 2026. Related: Agent Infrastructure.

Share This Article
Leave a Comment