# How to install the MCP Python SDK

- Tool: MCP
- Difficulty: Beginner
- Time required: 10 min
- Compatibility: Python 3.10+, pip or uv package manager
- Last updated: March 2026

## TL;DR

Install the MCP Python SDK with pip install mcp[cli] or uv add mcp[cli], then create a minimal server using the FastMCP class with decorator-based tool definitions. The Python SDK uses type hints for input validation (no Zod needed), supports async handlers natively, and can be run directly with python server.py or via the mcp CLI. A working server is under 15 lines of code.

## Setting Up the MCP Python SDK with FastMCP

The Python MCP SDK provides FastMCP, a high-level class that makes building MCP servers as simple as writing decorated functions. Unlike the TypeScript SDK which uses Zod for schemas, the Python SDK uses native type hints and docstrings to generate input schemas automatically. This tutorial walks you through installation, project setup, and creating your first Python MCP server.

## Before you start

- Python 3.10 or later installed (check with python --version)
- pip or uv package manager available
- A code editor (VS Code, Cursor, or similar)
- Basic familiarity with Python and async/await

## Step-by-step guide

### 1. Install the MCP Python SDK

Install the mcp package with the cli extra, which includes the FastMCP class and the mcp command-line tool for running and testing servers. You can use pip or uv (the fast Python package manager). The cli extra is important — it includes the development server and inspector integration.

```
# Using pip
pip install "mcp[cli]"

# Using uv (recommended — faster)
uv add "mcp[cli]"
```

**Expected result:** The mcp package is installed. Running python -c "import mcp" should produce no errors.

### 2. Create your project structure

Create a project directory with a minimal structure. Python MCP servers can be as simple as a single file. For larger projects, you will want a proper package structure, but for getting started, one file is enough.

```
mkdir my-mcp-server
cd my-mcp-server

# If using uv, initialize the project
uv init
uv add "mcp[cli]"

# Create the server file
touch server.py
```

**Expected result:** A project directory with server.py ready to edit.

### 3. Create a minimal FastMCP server

Open server.py and create a FastMCP instance. The FastMCP class handles all protocol details — you just define tools, resources, and prompts using decorators. The @mcp.tool() decorator turns a regular Python function into an MCP tool. Type hints are used to generate the input schema automatically, and the docstring becomes the tool description.

```
# server.py
from mcp.server.fastmcp import FastMCP

# Create the server
mcp = FastMCP("my-mcp-server")

@mcp.tool()
async def hello(name: str) -> str:
    """Greet someone by name."""
    return f"Hello, {name}!"

@mcp.tool()
async def add(a: float, b: float) -> str:
    """Add two numbers together."""
    return str(a + b)
```

**Expected result:** A server.py file with two tool definitions using FastMCP decorators.

### 4. Run the server with the mcp CLI

The mcp CLI tool (included with mcp[cli]) can run your server directly. Use mcp dev to start the server with the MCP Inspector for interactive testing. Use mcp run for production-style stdio execution. The dev command opens a browser-based inspector where you can call your tools and see the JSON-RPC messages.

```
# Development mode with Inspector
mcp dev server.py

# Production mode (stdio transport)
mcp run server.py

# Or run directly with Python
python server.py
```

**Expected result:** Running mcp dev server.py opens the MCP Inspector in your browser where you can test the hello and add tools.

### 5. Add a resource and a prompt

FastMCP supports all three MCP capability types. Use @mcp.resource() with a URI pattern to define resources, and @mcp.prompt() to define prompt templates. Resources return data for context, prompts return message templates for structured interactions.

```
# Add to server.py

@mcp.resource("config://app")
async def get_config() -> str:
    """Current application configuration."""
    return '{"version": "1.0.0", "env": "development"}'

@mcp.resource("users://{user_id}/profile")
async def get_user_profile(user_id: str) -> str:
    """Get a user's profile by ID."""
    return f'{{"id": "{user_id}", "name": "Jane Doe"}}'

@mcp.prompt()
async def review_code(language: str) -> str:
    """Generate a code review prompt for the specified language."""
    return f"Review the following {language} code for bugs, security issues, and best practices."
```

**Expected result:** Your server now exposes tools, resources, and prompts. The MCP Inspector shows all three capability types.

### 6. Configure the server for Claude Desktop or Cursor

To connect your Python MCP server to an AI host, add it to the host's configuration file. For Claude Desktop, edit claude_desktop_config.json. For Cursor, edit .cursor/mcp.json. The command should point to your Python interpreter and the server file. Use uv run if you are using uv for dependency management.

```
// claude_desktop_config.json example
{
  "mcpServers": {
    "my-server": {
      "command": "uv",
      "args": ["run", "--directory", "/path/to/my-mcp-server", "server.py"]
    }
  }
}

// Alternative using python directly
{
  "mcpServers": {
    "my-server": {
      "command": "python",
      "args": ["/path/to/my-mcp-server/server.py"]
    }
  }
}
```

**Expected result:** After restarting the AI host, your Python MCP server's tools appear and can be called by the AI model.

## Complete code example

File: `server.py`

```python
#!/usr/bin/env python3
"""A complete MCP server demonstrating all capability types."""

from mcp.server.fastmcp import FastMCP

# Create the MCP server
mcp = FastMCP("my-mcp-server")


# === TOOLS (model-controlled actions) ===

@mcp.tool()
async def hello(name: str) -> str:
    """Greet someone by name."""
    return f"Hello, {name}!"


@mcp.tool()
async def calculate(a: float, b: float, operation: str) -> str:
    """Perform basic arithmetic.
    
    Args:
        a: First number
        b: Second number  
        operation: One of 'add', 'subtract', 'multiply', 'divide'
    """
    ops = {
        "add": a + b,
        "subtract": a - b,
        "multiply": a * b,
        "divide": a / b if b != 0 else "Error: division by zero",
    }
    result = ops.get(operation, f"Unknown operation: {operation}")
    return f"{a} {operation} {b} = {result}"


@mcp.tool()
async def search_files(pattern: str, directory: str = ".") -> str:
    """Search for files matching a glob pattern.
    
    Args:
        pattern: Glob pattern like '*.py' or '**/*.json'
        directory: Directory to search in (default: current)
    """
    import glob
    import os
    
    search_path = os.path.join(directory, pattern)
    matches = glob.glob(search_path, recursive=True)
    if not matches:
        return f"No files found matching '{pattern}' in {directory}"
    return "\n".join(matches[:50])  # Limit to 50 results


# === RESOURCES (app-controlled data) ===

@mcp.resource("config://app")
async def get_app_config() -> str:
    """Current application configuration."""
    import json
    return json.dumps({
        "name": "my-mcp-server",
        "version": "1.0.0",
        "python": "3.12",
    }, indent=2)


# === PROMPTS (user-controlled templates) ===

@mcp.prompt()
async def debug_error(error_message: str, language: str = "Python") -> str:
    """Help debug an error message."""
    return (
        f"I encountered this error in {language}:\n\n"
        f"{error_message}\n\n"
        f"Please explain what caused this error and provide a fix."
    )


if __name__ == "__main__":
    mcp.run()
```

## Common mistakes

- **Installing mcp without the [cli] extra** — undefined Fix: Running pip install mcp installs the base package but not the CLI tools (mcp dev, mcp run). Always install with pip install "mcp[cli]" to get the full development toolkit.
- **Using print() for debugging in stdio servers** — undefined Fix: Like TypeScript servers, Python MCP servers use stdout for protocol messages. Use import sys; print('debug', file=sys.stderr) or the logging module with a stderr handler instead of regular print().
- **Forgetting type hints on tool parameters** — undefined Fix: FastMCP generates input schemas from type hints. Without them, the SDK cannot tell the AI model what types of values to pass. Always annotate parameters: async def search(query: str, limit: int = 10).
- **Not writing docstrings for tools** — undefined Fix: The tool's docstring becomes its description in MCP. Without a docstring, the AI model has no context for when to use the tool. Write clear docstrings: "Search GitHub issues by keyword and return matching results."

## Best practices

- Use uv instead of pip for faster installs and better dependency resolution
- Always install mcp[cli] to get the development tools (mcp dev, mcp run)
- Write descriptive docstrings for every tool — the AI model uses them to decide when to call each tool
- Use type hints on all parameters so FastMCP generates accurate input schemas
- Log to stderr instead of stdout: import sys; print('msg', file=sys.stderr)
- Use async functions for tool handlers to avoid blocking the event loop during I/O operations
- Test with mcp dev server.py to use the Inspector before connecting to a host
- Pin your mcp package version in requirements.txt or pyproject.toml for reproducible builds

## Frequently asked questions

### What Python version do I need?

Python 3.10 or later is required. The MCP SDK uses features like match statements and improved type hints that are not available in earlier versions. Python 3.12 is recommended.

### Should I use pip or uv?

uv is recommended for new projects. It is 10-100x faster than pip, handles virtual environments automatically, and provides better dependency resolution. Install uv with curl -LsSf https://astral.sh/uv/install.sh | sh.

### Can I use synchronous functions instead of async?

Yes. FastMCP accepts both sync and async functions. However, async is recommended for I/O operations (API calls, file reads, database queries) to avoid blocking the server while waiting for responses.

### How do I pass environment variables to my Python MCP server?

In Claude Desktop or Cursor configs, add an "env" object to the server entry: {"command": "python", "args": ["server.py"], "env": {"API_KEY": "your-key"}}. Access them with os.environ["API_KEY"] in your server code.

### Can RapidDev help build Python MCP servers for our team?

Yes. RapidDev helps teams build custom Python MCP servers that integrate with internal APIs, databases, and workflows. This includes design, implementation, testing, and deployment guidance for production use.

### What is the difference between mcp dev and mcp run?

mcp dev starts your server with the MCP Inspector attached — a browser-based UI for testing tools interactively. mcp run starts the server in production stdio mode without the Inspector. Use dev for testing, run for production.

---

Source: https://www.rapidevelopers.com/mcp-tutorial/how-to-install-mcp-python-sdk
© RapidDev — https://www.rapidevelopers.com/mcp-tutorial/how-to-install-mcp-python-sdk
