Building Your First Python MCP Server: Connect AI Agents to Local Files, APIs, and Databases

Learn how to build a Model Context Protocol (MCP) server in Python from scratch. This tutorial covers tools, resources, prompts, and connecting AI agents to your own data sources.

The Model Context Protocol (MCP) has rapidly become the standard way to connect AI language models to external tools, files, databases, and APIs. If you are a Python developer looking to give AI agents access to your own systems, building an MCP server is the right approach.

In this tutorial, we will build a complete MCP server in Python that lets an AI agent read files, query a SQLite database, and call a web API — all through a standardized protocol that works with Claude, ChatGPT, and any MCP-compatible client.

What Is MCP?

MCP is an open protocol that defines a standard way for AI applications (clients) to discover and use tools provided by a server. Think of it like a USB-C port for AI: instead of writing custom integration code for each AI model, you build one MCP server and any compatible client can use it.

The protocol supports three types of capabilities:

  1. Tools — functions the AI can call with structured arguments (e.g., query_database(sql: str))
  2. Resources — data the AI can read (e.g., a file, a database table schema, API documentation)
  3. Prompts — reusable prompt templates the AI can fill in with context

Our server will implement all three.

Prerequisites

  • Python 3.10 or later
  • Basic familiarity with async Python (asyncio)

Install the fastmcp package:

pip install fastmcp

FastMCP is the most popular Python MCP server framework. It provides a simple decorator-based API that handles the JSON-RPC protocol for you. Alternatively, you can use the official mcp package directly for lower-level control.

Building the Server

Here is our complete server. It exposes three tools, one resource, and one prompt template:

from fastmcp import FastMCP
import sqlite3
import aiohttp
import os

mcp = FastMCP(
    name="DataConnector",
    instructions="Connect AI agents to files, databases, and web APIs"
)

# --- Tool 1: Read a local file ---
@mcp.tool()
async def read_file(path: str) -> str:
    """Read the contents of a local file. Returns the full text content."""
    if not os.path.exists(path):
        return f"Error: File not found: {path}"
    with open(path, "r") as f:
        return f.read()

# --- Tool 2: Query a SQLite database ---
@mcp.tool()
async def query_database(sql: str) -> str:
    """Execute a read-only SQL query against a SQLite database and return results."""
    # Safety: only allow SELECT
    if not sql.strip().upper().startswith("SELECT"):
        return "Error: Only SELECT queries are allowed"
    try:
        conn = sqlite3.connect("data.db")
        cursor = conn.execute(sql)
        rows = cursor.fetchall()
        columns = [desc[0] for desc in cursor.description]
        conn.close()
        # Format as markdown table
        header = "| " + " | ".join(columns) + " |"
        separator = "| " + " | ".join("---" for _ in columns) + " |"
        data_rows = ["| " + " | ".join(str(v) for v in row) + " |" for row in rows]
        return "\n".join([header, separator] + data_rows)
    except Exception as e:
        return f"Error: {e}"

# --- Tool 3: Fetch JSON from a URL ---
@mcp.tool()
async def fetch_json(url: str) -> str:
    """Fetch JSON data from a URL and return it as formatted text."""
    async with aiohttp.ClientSession() as session:
        async with session.get(url) as resp:
            if resp.status != 200:
                return f"Error: HTTP {resp.status}"
            data = await resp.json()
            import json
            return json.dumps(data, indent=2)[:5000]  # Limit response size

# --- Resource: API documentation ---
@mcp.resource("docs://api")
async def api_docs() -> str:
    """Read-only access to the API documentation."""
    return """
Available Tools:
- read_file(path): Read a local file
- query_database(sql): Execute a SELECT query on data.db
- fetch_json(url): Fetch JSON from a URL

Safety Notes:
- Database queries are limited to SELECT only
- File reading has no write/delete access
- JSON fetches are limited to 5000 characters
"""

# --- Prompt template ---
@mcp.prompt()
async def analyze_data(question: str) -> str:
    """A prompt template for data analysis workflows."""
    return f"""
You are a data analyst. The user has asked: {question}

Use the available tools to:
1. Check the database schema first
2. Run the appropriate queries
3. Summarize findings in plain language
"""

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

How the AI Agent Uses These Tools

When an MCP client (like Claude Desktop, Cursor, or a custom agent) connects to this server, here is what happens:

  1. Discovery: The client calls the server to list available tools, resources, and prompts. It sees read_file, query_database, fetch_json, and the docs://api resource.

  2. Tool Selection: When the user asks “What’s in my data.db?”, the AI recognizes that query_database is the appropriate tool. It constructs a SELECT query and sends it to the server.

  3. Execution: The server runs the query and returns the results as a formatted markdown table. The AI presents this to the user in natural language.

This loop repeats for as many tool calls as the conversation needs.

Adding MCP to Your Existing Projects

The pattern is straightforward:

  1. Identify the functions in your existing code that an AI agent would find useful
  2. Decorate them with @mcp.tool() and add a docstring that describes what the tool does (the AI reads this docstring to decide when to call the tool)
  3. Start the server and point your MCP client at it

For production deployments, you will want to add:

  • Authentication — verify that the connecting client is authorized
  • Rate limiting — prevent the AI from calling expensive tools in tight loops
  • Input validation — especially for SQL queries and file paths, to prevent path traversal or injection attacks
  • Logging — track which tools the AI called and with what arguments, for debugging and auditing

Connecting to MCP Clients

Once your server is running (python server.py), you can connect it to various MCP clients:

Claude Desktop (Anthropic): Add your server config to Claude’s claude_desktop_config.json:

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

Cursor: Add the same configuration to Cursor’s MCP settings.

Custom Python Client: Use the mcp package to build a client that connects to your server programmatically. This is useful for automated testing of your MCP tools before exposing them to a conversational AI.

Beyond FastMCP

FastMCP is great for getting started, but you can also build MCP servers using the lower-level mcp package directly. This gives you more control over:

  • Transport protocol: stdio (for local tools) or HTTP/SSE (for remote servers)
  • Custom JSON-RPC handling: if you need special error formats or middleware
  • Resource subscriptions: letting the client subscribe to real-time updates from a resource

For example, if you want to build an MCP server that streams live data from a sensor or a stock price API, you would use HTTP/SSE transport so the client can receive push updates.

The Bottom Line

MCP is becoming the standard way to connect AI agents to external systems. By building an MCP server in Python, you give any MCP-compatible AI agent access to your tools, data, and APIs — without writing custom integration code for each AI model.

The key insight is that the AI does not need to understand your entire codebase. It just needs a clear description of each tool’s purpose and arguments, delivered through the MCP protocol. Good tool descriptions (docstrings) are just as important as the tool implementations.

Start simple: expose one or two functions as MCP tools, test with Claude Desktop or Cursor, and expand from there.


Sources: Dark Reading — Securing the AI-Powered DevOps Stack, Hospitality Net — Simple Booking MCP Connectors

Related reading: How to Build a Python MCP Server with FastMCP, Pydantic AI MCP Integration Options

Spread The Article

Share this guide

Send this article to your network or keep a copy of the direct link.

X Facebook LinkedIn Reddit Telegram

Discussion

Leave a comment

No comments yet

Be the first to start the conversation.