# How to Build an MCP Server in Python

Building an MCP server in Python means using the MCP SDK to define 'tools', 'resources', and 'prompts' that an AI agent can consume over a standard protocol. This guide walks you through creating a server that makes a cloud storage bucket browsable for AI agents.

Source: https://fast.io/resources/build-mcp-server-python/
Last reviewed: 2026-02-09

## What is an MCP Server?

The Model Context Protocol (MCP) is an open standard that lets AI models interact with external data and systems safely. Instead of building custom integrations for every new LLM, you build one MCP server that works with Claude, IDEs like Cursor, and other MCP-compliant clients. An MCP server provides three main capabilities:
*   **Resources:** Data that agents can read (like files, logs, or API responses).
*   **Tools:** Functions that agents can execute (like "upload_file" or "query_database").
*   **Prompts:** Reusable templates that help agents use your server well. Python works well for data-heavy MCP servers because of its rich ecosystem for data science, API handling, and file manipulation.

Helpful references: [Fastio Workspaces](/product/workspaces/), [Fastio Collaboration](/product/collaboration/), and [Fastio AI](/product/ai/).

## Prerequisites and Setup

You'll need Python 3.10 or higher. We recommend using `uv` for fast package management, but `pip` works fine too. Create a new directory for your project and set up a virtual environment:

```bash
mkdir my-mcp-server
cd my-mcp-server
python -m venv .venv
source .venv/bin/activate  # On Windows: .venv\Scripts\activate
```

Next, install the official MCP SDK. We'll use the `mcp` package which includes the high-level `FastMCP` interface:

```bash
pip install "mcp[cli]"
```

Now you're ready to write your first server code. Getting started should be straightforward. A good platform lets you create an account, invite your team, and start uploading files within minutes, not days. Avoid tools that require complex server configuration or IT department involvement just to get running.

## Step 1: Create a Basic Server with FastMCP

The `FastMCP` class handles the low-level protocol details so you can focus on your logic. Create a file named `server.py`:

```python
from mcp.server.fastmcp import FastMCP

### Initialize the server
mcp = FastMCP("My Python Server")

### Define a simple tool
@mcp.tool()
def add_numbers(a: int, b: int) -> int:
    """Add two numbers together."""
    return a + b

if __name__ == "__main__":
    ### Run the server using stdio transport by default
    mcp.run()
```

This code creates a server with one tool, `add_numbers`. The docstring is critical. It tells the AI agent *when* and *how* to use this tool. Consider how this fits into your broader workflow and what matters most for your team. The right choice depends on your specific requirements: file types, team size, security needs, and how you collaborate with external partners. Testing with a free account is the fast way to know if a tool works for you.

## Step 2: Expose Cloud Storage as a Resource

One of the best features of MCP is **Resources**. Resources let you make data available via URI templates. Agents can "read" external systems as if they were local files. Here's how to simulate a cloud storage bucket (like S3) as an MCP resource. This lets an agent read `s3://my-bucket/data.txt` directly.

```python
from mcp.server.fastmcp import FastMCP, Context

### Simulated cloud storage
MOCK_BUCKET = {
    "reports/2025-q1.txt": "Revenue: $5M
Growth: 15%",
    "configs/app.json": '{"debug": true, "version": "1.0.0"}'
}

mcp = FastMCP("Cloud Storage Server")

@mcp.resource("s3://{bucket}/{path}")
def read_s3_object(bucket: str, path: str) -> str:
    """Read a file from the mock S3 bucket."""
    key = f"{path}"
    if key in MOCK_BUCKET:
        return MOCK_BUCKET[key]
    raise FileNotFoundError(f"File {path} not found in {bucket}")
```

With this code, if an agent asks to read `s3://my-bucket/reports/2025-q1.txt`, the server fetches the content and returns it. In a real application, you would use `boto3` to fetch actual data from AWS.

## Step 3: Define Tools for Action

While resources are for reading, **Tools** are for taking action. Let's add a tool that allows the agent to "upload" a file to our bucket.

```python
@mcp.tool()
def upload_file(filename: str, content: str) -> str:
    """Upload a new file to the storage bucket."""
    MOCK_BUCKET[filename] = content
    return f"Successfully uploaded {filename} ({len(content)} bytes)"
```

Notice how we use type hints (`str`, `int`). The MCP SDK uses these to automatically generate the JSON schema that the LLM uses to validate its inputs. Consider how this fits into your broader workflow and what matters most for your team. The right choice depends on your specific requirements: file types, team size, security needs, and how you collaborate with external partners. Testing with a free account is the fast way to know if a tool works for you.

## Step 4: Running and Testing Your Server

MCP servers typically run over `stdio` (standard input/output), which is how Claude Desktop and other clients communicate with them locally. To test your server with the **MCP Inspector** (a web-based debugger):

```bash
npx @modelcontextprotocol/inspector python server.py
```

This command launches a web UI where you can see your resources and test your tools manually. To connect it to **Claude Desktop**, edit your `claude_desktop_config.json`:

```json
{
  "mcpServers": {
    "my-python-server": {
      "command": "uv",
      "args": ["run", "server.py"]
    }
  }
}
```

Consider how this fits into your broader workflow and what matters most for your team. The right choice depends on your specific requirements: file types, team size, security needs, and how you collaborate with external partners. Testing with a free account is the fast way to know if a tool works for you.

## MCP Tools vs. Resources

When should you use a Tool versus a Resource? Use this comparison to decide:

| Feature | **Resource**

| **Tool** |
| :--- | :--- | :--- |
| **Purpose** | Reading data passively | Taking action or calculating |
| **Interaction** | GET (like a file read) | POST (function call) |
| **Side Effects** | None (should be idempotent) | Yes (database writes, API calls) |
| **Agent View** | "I want to read X" | "I want to do Y" |
| **Example** | Reading a log file | Sending a Slack message |

If you need to give the AI context (like a list of users), use a **Resource**. If you need the AI to perform a task (like creating a user), use a **Tool**.

## Production-Ready MCP with Fastio

Building your own MCP server is great for custom internal tools, but managing authentication, file uploads, and large-scale storage can be complex. Fastio provides a hosted **remote MCP server** that works out of the box. It gives your agents:
*   **A consolidated MCP toolset** for file management, search, and sharing.
*   **Persistent storage** in team workspaces so agents can save work between sessions.
*   **Cloud import**, enabling one-time imports from Google Drive, Dropbox, OneDrive, Box, and public URLs. Instead of building a file system server from scratch, you can connect any MCP client to https://mcp.fast.io/mcp and give your agent instant access to a production-ready cloud storage backend.

## Frequently asked questions

### How do I start an MCP server?

You start an MCP server by running your Python script. If using `FastMCP`, calling `mcp.run()` at the end of your script will automatically handle the stdio communication needed by clients like Claude Desktop.

### What is the difference between an MCP tool and a resource?

A resource is passive data that an agent can read (like a file or web page), while a tool is an executable function that performs an action (like calculating a sum or sending an API request).

### Can I host an MCP server on a VPS?

Yes, you can host an MCP server on a VPS. While stdio is common for local use, MCP also supports Server-Sent Events (SSE) over HTTP, allowing you to run a remote server that agents can connect to via a URL.

## About Fast.io

Fast.io provides shared workspaces where people and AI agents work on the same files, with built-in semantic search and citation-backed chat over what they hold. Agents reach it through a remote MCP server at https://mcp.fast.io/mcp, a REST API at https://api.fast.io/current/, and a command line client published on npm as @vividengine/fastio-cli.
