Let's be completely honest with each other: we have all suffered through the painful copy-paste marathon.
You are working on a tricky bug in your application. Your backend keeps throwing an error, and you need help from Claude or Cursor. So you open your database app, run a query, copy 40 rows of JSON results, jump back to your AI chat, paste it in, write out your table schema by hand, and explain what each column means. Five minutes later, you change a query parameter and you have to repeat the entire dance all over again.
It drains your energy, breaks your creative flow, and frankly makes modern AI feel a lot less magical than it should be.
What if your AI assistant could simply reach out, look at your database tables with its own eyes, run safe queries, and help you fix the bug immediately—all without you lifting a finger to copy-paste anything? That is exactly what the Model Context Protocol (MCP) does. And thanks to a delightful Python tool called FastMCP, you can build your own custom database bridge in less than 15 minutes, using plain, friendly Python that anyone can write.
In this hands-on, step-by-step guide, I will walk you through building your very first FastMCP server from scratch. We will connect a real database (SQLite first, then PostgreSQL), hook it up to both Claude Desktop and Cursor, and wrap it in tight safety locks so your data stays 100% safe. No confusing computer science buzzwords, no endless boilerplate—just simple steps that actually work.
Quick Answer: What is FastMCP and How Does It Work?
FastMCP is an open-source Python framework that makes building Model Context Protocol (MCP) servers as easy as building a tiny web app with FastAPI. Instead of writing complex low-level JSON-RPC protocol code by hand, you write regular Python functions and add a simple @mcp.tool() tag above them.
Once your server is running, AI clients like Claude Desktop and Cursor can call your Python functions directly through standard input and output (stdio). When Claude wants to know your database structure or look up customer records, it asks your FastMCP server, your server runs a safe query, and returns the answers straight into the conversation.
Why FastMCP Is a Breath of Fresh Air for Developers
When Anthropic first launched the Model Context Protocol in late 2024, the official software development kit (SDK) was primarily built around TypeScript and Node.js. While it was powerful, writing a custom server felt heavy. You had to define JSON schemas by hand, handle input validation types, configure transport streams, and write dozens of lines of setup code just to return a simple list of names.
For Python developers, data scientists, and backend engineers, this felt frustrating. Python is the natural language of data analysis, AI models, and database scripting! Why should you have to leave your favorite language just to connect a database to your coding assistant?
Enter FastMCP. Created to bring the simplicity and joy of FastAPI to the MCP universe, FastMCP changes the game in three big ways:
- Python Type Hints Do the Work: You just write normal Python type hints (like
query: str, limit: int = 10). FastMCP automatically translates those types into the exact JSON schema that Claude and Cursor understand. - Docstrings Become Tool Descriptions: Whatever you write in your Python function docstring is read directly by the AI model to understand when and how to use the tool.
- Zero Boilerplate: A complete, working server that exposes two database tools takes fewer than 30 lines of code. You can read the whole program in one glance.
How FastMCP Connects Your Tools Together
What You Will Need Before We Begin (Prerequisites)
You do not need any fancy tools to follow along. Everything we use is free and takes just two minutes to get ready:
- Python 3.10 or newer: Installed on your Mac, Windows, or Linux machine. You can check by running
python3 --versionin your terminal. - A modern package manager: We recommend
uv(the blazing-fast Python tool from Astral) or standardpip. - Claude Desktop or Cursor IDE: Make sure you have at least one of these installed so you can test your new server live!
- A sample database: We will create a small SQLite database in Step 2 with fake customer and order data so you don't have to risk any sensitive company files.
Step 1: Install FastMCP in Under 30 Seconds
Let's start by setting up a clean folder for our project and installing the fastmcp package. Open your terminal and run these commands:
# 1. Create a new directory and move into it
mkdir my-first-fastmcp
cd my-first-fastmcp
# 2. Create a clean virtual environment (optional but recommended)
python3 -m venv .venv
source .venv/bin/activate # On Windows, use: .venv\Scripts\activate
# 3. Install FastMCP
pip install fastmcp
If you prefer using uv (which is wonderfully fast and great for running MCP servers), you can simply install it via:
uv add fastmcp
That is all the installation required! No complex build tools, no Node dependencies, no headaches.
Step 2: Create a Practice SQLite Database
To keep things 100% safe and simple, let's create a small SQLite database called store.db with two tables: customers and orders.
Create a file named setup_db.py and paste this code:
# setup_db.py
import sqlite3
# Connect to our local SQLite database file
conn = sqlite3.connect("store.db")
cursor = conn.cursor()
# Create a customers table
cursor.execute('''
CREATE TABLE IF NOT EXISTS customers (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
email TEXT UNIQUE NOT NULL,
plan TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')
# Create an orders table
cursor.execute('''
CREATE TABLE IF NOT EXISTS orders (
id INTEGER PRIMARY KEY AUTOINCREMENT,
customer_id INTEGER,
amount REAL NOT NULL,
status TEXT NOT NULL,
product_name TEXT NOT NULL,
FOREIGN KEY(customer_id) REFERENCES customers(id)
)
''')
# Insert a few friendly sample records
cursor.execute("INSERT OR IGNORE INTO customers (name, email, plan) VALUES ('Sarah Connor', 'sarah@example.com', 'Pro')")
cursor.execute("INSERT OR IGNORE INTO customers (name, email, plan) VALUES ('Alex Murphy', 'alex@example.com', 'Enterprise')")
cursor.execute("INSERT OR IGNORE INTO customers (name, email, plan) VALUES ('Ellen Ripley', 'ripley@example.com', 'Starter')")
cursor.execute("INSERT OR IGNORE INTO orders (customer_id, amount, status, product_name) VALUES (1, 49.99, 'completed', 'AI Code Assistant Annual')")
cursor.execute("INSERT OR IGNORE INTO orders (customer_id, amount, status, product_name) VALUES (2, 299.00, 'completed', 'Team Workspace Seat')")
cursor.execute("INSERT OR IGNORE INTO orders (customer_id, amount, status, product_name) VALUES (1, 15.00, 'pending', 'Additional Storage')")
conn.commit()
conn.close()
print("Success! Created store.db with sample customers and orders.")
Run it in your terminal:
python3 setup_db.py
You now have a clean local database ready to explore!
Step 3: Write Your FastMCP Server Code (The Magic Part)
Here is where FastMCP truly shines. We are going to create a file called server.py. We will give our AI pair programmer three superpowers:
list_tables: Tells the AI what tables exist in the database so it never has to guess.describe_table: Shows the exact columns, types, and constraints for any table.run_read_query: Executes a safe, read-only SQL query and returns the rows as neat text.
Take a look at how clean and readable this code is:
# server.py
import sqlite3
import os
from fastmcp import FastMCP
# 1. Initialize our FastMCP server with a friendly name
mcp = FastMCP("Local Store Database Manager")
# Helper function to get a database connection
def get_db_path() -> str:
# Always resolve the absolute path so Claude Desktop finds the file
base_dir = os.path.dirname(os.path.abspath(__file__))
return os.path.join(base_dir, "store.db")
@mcp.tool()
def list_tables() -> list[str]:
"""Get a list of all tables present in the store database."""
conn = sqlite3.connect(get_db_path())
cursor = conn.cursor()
cursor.execute("SELECT name FROM sqlite_master WHERE type='table';")
tables = [row[0] for row in cursor.fetchall() if row[0] != "sqlite_sequence"]
conn.close()
return tables
@mcp.tool()
def describe_table(table_name: str) -> list[dict]:
"""
Get column details, data types, and primary keys for a specific table.
Args:
table_name: The name of the table to inspect (e.g. 'customers' or 'orders')
"""
conn = sqlite3.connect(get_db_path())
cursor = conn.cursor()
# Use PRAGMA table_info to inspect the schema safely
cursor.execute(f"PRAGMA table_info({table_name});")
columns = []
for col in cursor.fetchall():
columns.append({
"column_id": col[0],
"name": col[1],
"type": col[2],
"not_null": bool(col[3]),
"primary_key": bool(col[5])
})
conn.close()
return columns
@mcp.tool()
def run_read_query(query: str) -> list[dict]:
"""
Run a safe read-only SQL SELECT query on the store database and return matching rows.
Args:
query: The SQL SELECT statement to execute. Must start with SELECT.
"""
clean_query = query.strip()
# Safety Check: strictly forbid modifying commands
forbidden_words = ["DROP", "DELETE", "UPDATE", "INSERT", "ALTER", "TRUNCATE"]
upper_query = clean_query.upper()
for word in forbidden_words:
if word in upper_query:
raise ValueError(f"Security Alert: The command '{word}' is not allowed. Only read-only SELECT queries are permitted.")
if not upper_query.startswith("SELECT") and not upper_query.startswith("PRAGMA"):
raise ValueError("Security Alert: Only SELECT queries are permitted.")
conn = sqlite3.connect(get_db_path())
conn.row_factory = sqlite3.Row # Returns rows as dictionaries
cursor = conn.cursor()
try:
cursor.execute(clean_query)
rows = [dict(row) for row in cursor.fetchall()]
return rows
finally:
conn.close()
# 2. Run the server using standard input/output when started
if __name__ == "__main__":
mcp.run(transport="stdio")
Notice how straightforward this is:
FastMCP("Local Store Database Manager")names your server so you can spot it inside your AI editor.@mcp.tool()is the only decorator you need. Any regular Python function marked with@mcp.tool()instantly becomes a tool that Claude or Cursor can execute!- The docstrings (the text inside comments) are not just notes for developers—Claude and Cursor actually read them to know when to call the tool and what arguments to provide.
Step 4: Test Your FastMCP Server in Your Terminal First
Before hooking this up to Claude or Cursor, it is always a smart idea to make sure our Python script runs without syntax errors. FastMCP comes with a handy built-in testing utility!
Run this command in your project directory:
fastmcp dev server.py
This command boots up an interactive test runner in your browser. You can click on each tool, pass in sample arguments (like typing customers into describe_table), and see the live JSON response immediately. When you see your customers table pop up on screen, celebrate for a second—you just built your very first Model Context Protocol server!
Step 5: Connect Your Server to Claude Desktop
Now let's bring our server to life inside Claude Desktop. Claude connects to local MCP servers through a small JSON configuration file on your computer.
1. Find Your Configuration File:
- On a Mac:
~/Library/Application Support/Claude/claude_desktop_config.json - On Windows:
%APPDATA%\Claude\claude_desktop_config.json
If the file or folder does not exist yet, you can create it. Open the file in your favorite text editor (VS Code, TextEdit, or Notepad) and add your server definition:
{
"mcpServers": {
"store-db": {
"command": "/Users/yourname/my-first-fastmcp/.venv/bin/python",
"args": [
"/Users/yourname/my-first-fastmcp/server.py"
]
}
}
}
server.py file. Never use shortcuts like ~ or relative paths like ./server.py, because Claude Desktop launches background processes from its own application root folder.
2. Test It Live in Claude Desktop:
- Completely quit Claude Desktop (on Mac, press Cmd + Q; on Windows, right-click the taskbar icon and select Quit).
- Open Claude Desktop again.
- Look at the bottom right of the message box. You will see a small hammer icon or electrical plug icon with a number next to it. Click it, and you should see
store-dbwith your 3 tools listed! - Type a prompt like this:
"Hey Claude, what tables are in our store database, and how many pending orders do we have?"
Watch in amazement as Claude calls list_tables, checks the orders table schema, runs a query to count pending orders, and gives you a clear, friendly answer in seconds!
Step 6: Connect Your Server to Cursor IDE
If you write code daily in Cursor, connecting your FastMCP server is just as effortless. Cursor gives you two easy ways to do it:
Method A: Through Cursor Settings (Easiest for Beginners)
- Open Cursor and press Cmd + , (or Ctrl + , on Windows) to open Settings.
- In the left sidebar, navigate to Features → MCP Servers.
- Click the Add New MCP Server button.
- Fill in the fields:
- Name:
store-db - Type:
command - Command:
/absolute/path/to/my-first-fastmcp/.venv/bin/python /absolute/path/to/my-first-fastmcp/server.py
- Name:
- Click Save. A green indicator dot will turn on, showing your server is live!
Method B: Project-Level .cursor/mcp.json (Best for Teams)
If you want everyone on your team to have access to this server when they open your project repository, create a file named .cursor/mcp.json right inside your project root:
{
"mcpServers": {
"store-db": {
"command": "python3",
"args": ["server.py"]
}
}
}
Now in Cursor Composer (press Cmd + I or Ctrl + I), you can ask:
Cursor will inspect the actual columns, see that customer_id and amount exist, and write mathematically accurate code on the first try!
Level Up: Connecting to PostgreSQL with Real Production Safety
SQLite is wonderful for local files and rapid prototyping. But in real-world software engineering, your application data lives in a production or staging database like PostgreSQL.
Can FastMCP handle PostgreSQL? Absolutely! The pattern is identical. All you do is swap SQLite's library for psycopg (or psycopg2). Here is a production-ready snippet:
# postgres_server.py
import psycopg
from fastmcp import FastMCP
mcp = FastMCP("Postgres Production Staging Assistant")
# Connect using a dedicated READ-ONLY database user
DB_URL = "postgresql://readonly_user:securepassword@localhost:5432/my_staging_db"
@mcp.tool()
def get_postgres_tables() -> list[str]:
"""List all public relational tables in the PostgreSQL database."""
with psycopg.connect(DB_URL) as conn:
with conn.cursor() as cur:
cur.execute("""
SELECT table_name
FROM information_schema.tables
WHERE table_schema = 'public'
ORDER BY table_name;
""")
return [row[0] for row in cur.fetchall()]
@mcp.tool()
def explain_slow_query(sql_statement: str) -> str:
"""
Run EXPLAIN (ANALYZE, COSTS, BUFFERS) on a query to understand execution bottlenecks.
Args:
sql_statement: The SELECT query to profile.
"""
if not sql_statement.strip().upper().startswith("SELECT"):
return "Error: Can only analyze SELECT statements."
with psycopg.connect(DB_URL) as conn:
with conn.cursor() as cur:
cur.execute(f"EXPLAIN ANALYZE {sql_statement}")
plan_lines = [row[0] for row in cur.fetchall()]
return "\n".join(plan_lines)
if __name__ == "__main__":
mcp.run(transport="stdio")
Imagine having Claude or Cursor analyze slow queries with EXPLAIN ANALYZE right inside your chat box and suggest the exact index that cuts your execution time from 400ms down to 4ms! That is what true AI-assisted engineering feels like.
3 Non-Negotiable Safety Rules When Connecting AI to Databases
Giving an AI assistant access to run queries is an incredible superpower, but you must keep your systems safe. Never skip these three golden rules:
1. Read-Only Always
Never give your MCP server credentials for an admin or write account. In PostgreSQL, create a user with GRANT SELECT ON ALL TABLES and nothing else. If an AI hallucinates, it cannot drop your tables.
2. Hide Sensitive Columns
If your tables have password hashes, billing credit card tokens, or private medical details, write an explicit block in your Python code that filters those columns out before returning data to the AI context window.
3. Limit Row Returns
Always add an automatic LIMIT 50 or LIMIT 100 to SQL queries. If an AI runs SELECT * FROM logs on a table with 2 million rows, it will instantly blow up your token limits and freeze your editor.
Troubleshooting: What to Do If Your Server Won't Connect
If you see a red indicator light in Cursor or the tool icon doesn't show up in Claude Desktop, don't panic! Here are the three most common hiccups and how to solve them in 30 seconds:
- Symptom: Claude Desktop says "Server disconnected" immediately upon opening.
Fix: Check yourclaude_desktop_config.jsonfile. Did you use relative paths like./server.py? Change them to full absolute paths like/Users/myname/projects/my-first-fastmcp/server.py. Also verify that your JSON file has no missing or trailing commas. - Symptom: The server starts, but says
ModuleNotFoundError: No module named 'fastmcp'.
Fix: Claude Desktop is using your system Python instead of your virtual environment. Point the"command"setting directly to the Python binary inside your.venv/bin/pythonfolder. - Symptom: Cursor shows a yellow loading spinner forever.
Fix: Make sure yourserver.pyfile does NOT have anyprint()statements outside of tools. Because MCP communicates over standard output (stdio), any looseprint("hello world")will corrupt the JSON-RPC message stream and confuse the client.
Frequently Asked Questions About Building FastMCP Servers
stdio transport (used for local editors on your laptop) and SSE (Server-Sent Events) over HTTP. This means you can deploy your FastMCP server to a cloud provider like Fly.io, Railway, or AWS, and let your team connect to it remotely.
@mcp.tool(), FastMCP provides @mcp.resource("schema://{table}") to expose passive context data, and @mcp.prompt() to create reusable prompt templates that guide your team in asking structured questions.
Final Thoughts: Your AI Pair Programmer Just Got a Real Brain
When you take a step back and look at what you just built, it is genuinely exciting. You did not just write a Python script—you gave Claude and Cursor real-world senses. Instead of guessing your column names or forcing you to manually copy-paste query results, your AI assistant can now explore your data responsibly, verify facts on the fly, and help you ship cleaner code in half the time.
The beauty of FastMCP is that databases are only the starting point. You can use the exact same pattern to connect your local Docker containers, your company's internal REST APIs, your Redis cache, or even your local files.
Start with this simple SQLite setup, test it out on your personal projects, and see how much lighter your workday feels when you never have to do the copy-paste marathon again.