FastMCP: Build MCP Servers in Minutes (2026)
The fastest way to create custom MCP servers for AI assistants
Start Building with Hypereal
Access Kling, Flux, Sora, Veo & more through a single API. Free credits to start, scale to millions.
No credit card required • 100k+ developers • Enterprise ready
FastMCP: Build MCP Servers in Minutes (2026)
The Model Context Protocol (MCP) is the emerging standard for connecting AI assistants to external tools and data. But building MCP servers from scratch can be tedious -- managing transports, serialization, and protocol details is not where you want to spend your time.
FastMCP fixes this. It is a high-level Python framework that lets you build fully functional MCP servers with decorators, just like building a Flask or FastAPI app. This guide shows you how to go from zero to a working MCP server in minutes.
What Is FastMCP?
FastMCP is an open-source Python library that wraps the MCP SDK with a developer-friendly API. Instead of manually handling protocol messages and transport layers, you write simple Python functions with decorators.
| Feature | Raw MCP SDK | FastMCP |
|---|---|---|
| Server setup | ~30 lines boilerplate | 2 lines |
| Tool definition | Manual schema + handler | @mcp.tool() decorator |
| Resource definition | Manual URI + handler | @mcp.resource() decorator |
| Type handling | Manual JSON Schema | Automatic from type hints |
| Testing | Manual transport mock | Built-in test client |
| Lines of code (simple server) | 80-100 | 15-20 |
Installation
pip install fastmcp
FastMCP requires Python 3.10 or higher.
Building Your First MCP Server
Here is a complete MCP server in 15 lines of code:
# server.py
from fastmcp import FastMCP
mcp = FastMCP("my-tools")
@mcp.tool()
def add(a: int, b: int) -> int:
"""Add two numbers together."""
return a + b
@mcp.tool()
def get_weather(city: str) -> str:
"""Get the current weather for a city."""
# In production, call a real weather API
return f"The weather in {city} is 72F and sunny."
if __name__ == "__main__":
mcp.run()
That is it. FastMCP automatically:
- Generates JSON schemas from your type hints.
- Handles the MCP protocol (tool listing, tool calling, error handling).
- Sets up stdio transport for compatibility with Claude Code, Claude Desktop, and other MCP clients.
Run the server
python server.py
Connect to Claude Code
Add to your .mcp.json or Claude Code config:
{
"mcpServers": {
"my-tools": {
"command": "python",
"args": ["/path/to/server.py"]
}
}
}
Or use the Claude Code CLI:
claude mcp add my-tools python /path/to/server.py
Core Concepts
Tools
Tools are functions that the AI can call. They perform actions and return results.
from fastmcp import FastMCP
import httpx
mcp = FastMCP("dev-tools")
@mcp.tool()
def search_npm(query: str, limit: int = 5) -> str:
"""Search npm registry for packages."""
response = httpx.get(
f"https://registry.npmjs.org/-/v1/search",
params={"text": query, "size": limit}
)
results = response.json()["objects"]
lines = []
for pkg in results:
p = pkg["package"]
lines.append(f"- {p['name']} (v{p['version']}): {p.get('description', 'No description')}")
return "\n".join(lines)
@mcp.tool()
def run_sql(query: str, database: str = "main.db") -> str:
"""Execute a read-only SQL query against a SQLite database."""
import sqlite3
conn = sqlite3.connect(database)
conn.execute("PRAGMA query_only = ON")
cursor = conn.execute(query)
columns = [desc[0] for desc in cursor.description] if cursor.description else []
rows = cursor.fetchall()
conn.close()
if not rows:
return "No results."
header = " | ".join(columns)
separator = " | ".join(["---"] * len(columns))
data = "\n".join([" | ".join(str(cell) for cell in row) for row in rows])
return f"{header}\n{separator}\n{data}"
Resources
Resources expose data that the AI can read. They are identified by URIs.
@mcp.resource("config://app")
def get_app_config() -> str:
"""Return the application configuration."""
import json
with open("config.json") as f:
return json.dumps(json.load(f), indent=2)
@mcp.resource("file://{path}")
def read_file(path: str) -> str:
"""Read a file from the project directory."""
with open(path) as f:
return f.read()
@mcp.resource("db://schema")
def get_database_schema() -> str:
"""Return the database schema."""
import sqlite3
conn = sqlite3.connect("main.db")
cursor = conn.execute(
"SELECT sql FROM sqlite_master WHERE type='table'"
)
schemas = [row[0] for row in cursor.fetchall()]
conn.close()
return "\n\n".join(schemas)
Prompts
Prompts are reusable prompt templates that the AI can use.
@mcp.prompt()
def code_review(code: str, language: str = "python") -> str:
"""Generate a code review prompt."""
return f"""Review the following {language} code for:
1. Security vulnerabilities
2. Performance issues
3. Code style and best practices
4. Potential bugs
Code:
```{language}
{code}
Provide specific, actionable feedback with code examples for fixes."""
@mcp.prompt() def explain_error(error_message: str, stack_trace: str = "") -> str: """Generate an error explanation prompt.""" prompt = f"Explain this error and suggest fixes:\n\nError: {error_message}" if stack_trace: prompt += f"\n\nStack trace:\n{stack_trace}" return prompt
## Practical Example: GitHub MCP Server
Here is a more complete example -- an MCP server that provides GitHub repository tools:
```python
# github_mcp.py
from fastmcp import FastMCP
import httpx
import os
mcp = FastMCP("github-tools")
GITHUB_TOKEN = os.environ.get("GITHUB_TOKEN", "")
HEADERS = {"Authorization": f"Bearer {GITHUB_TOKEN}"} if GITHUB_TOKEN else {}
@mcp.tool()
def search_repos(query: str, limit: int = 5) -> str:
"""Search GitHub repositories."""
response = httpx.get(
"https://api.github.com/search/repositories",
params={"q": query, "per_page": limit},
headers=HEADERS
)
repos = response.json().get("items", [])
lines = []
for repo in repos:
lines.append(
f"- [{repo['full_name']}]({repo['html_url']}) "
f"({repo['stargazers_count']} stars): {repo['description'] or 'No description'}"
)
return "\n".join(lines) or "No repositories found."
@mcp.tool()
def get_repo_readme(owner: str, repo: str) -> str:
"""Get the README content of a GitHub repository."""
response = httpx.get(
f"https://api.github.com/repos/{owner}/{repo}/readme",
headers={**HEADERS, "Accept": "application/vnd.github.raw+json"}
)
if response.status_code == 200:
return response.text
return f"Error: Could not fetch README ({response.status_code})"
@mcp.tool()
def list_issues(owner: str, repo: str, state: str = "open", limit: int = 10) -> str:
"""List issues for a GitHub repository."""
response = httpx.get(
f"https://api.github.com/repos/{owner}/{repo}/issues",
params={"state": state, "per_page": limit},
headers=HEADERS
)
issues = response.json()
lines = []
for issue in issues:
labels = ", ".join([l["name"] for l in issue.get("labels", [])])
lines.append(f"- #{issue['number']}: {issue['title']} [{labels}]")
return "\n".join(lines) or "No issues found."
@mcp.tool()
def get_file_contents(owner: str, repo: str, path: str, ref: str = "main") -> str:
"""Get the contents of a file from a GitHub repository."""
response = httpx.get(
f"https://api.github.com/repos/{owner}/{repo}/contents/{path}",
params={"ref": ref},
headers={**HEADERS, "Accept": "application/vnd.github.raw+json"}
)
if response.status_code == 200:
return response.text
return f"Error: Could not fetch file ({response.status_code})"
@mcp.resource("github://repos/{owner}/{repo}/info")
def get_repo_info(owner: str, repo: str) -> str:
"""Get basic information about a GitHub repository."""
response = httpx.get(
f"https://api.github.com/repos/{owner}/{repo}",
headers=HEADERS
)
repo_data = response.json()
return (
f"Name: {repo_data['full_name']}\n"
f"Stars: {repo_data['stargazers_count']}\n"
f"Language: {repo_data['language']}\n"
f"Description: {repo_data['description']}\n"
f"URL: {repo_data['html_url']}"
)
if __name__ == "__main__":
mcp.run()
Register it:
{
"mcpServers": {
"github": {
"command": "python",
"args": ["/path/to/github_mcp.py"],
"env": {
"GITHUB_TOKEN": "ghp_your_token"
}
}
}
}
Testing Your MCP Server
FastMCP includes a built-in test client:
# test_server.py
import asyncio
from fastmcp import Client
async def test():
async with Client("python server.py") as client:
# List available tools
tools = await client.list_tools()
print(f"Available tools: {[t.name for t in tools]}")
# Call a tool
result = await client.call_tool("add", {"a": 5, "b": 3})
print(f"5 + 3 = {result}")
# List resources
resources = await client.list_resources()
print(f"Available resources: {[r.uri for r in resources]}")
asyncio.run(test())
FastMCP vs. Raw MCP SDK
Here is the same tool defined both ways:
Raw MCP SDK
from mcp.server import Server
from mcp.types import Tool, TextContent
import json
server = Server("my-server")
@server.list_tools()
async def list_tools():
return [
Tool(
name="add",
description="Add two numbers",
inputSchema={
"type": "object",
"properties": {
"a": {"type": "integer", "description": "First number"},
"b": {"type": "integer", "description": "Second number"}
},
"required": ["a", "b"]
}
)
]
@server.call_tool()
async def call_tool(name, arguments):
if name == "add":
result = arguments["a"] + arguments["b"]
return [TextContent(type="text", text=str(result))]
FastMCP
from fastmcp import FastMCP
mcp = FastMCP("my-server")
@mcp.tool()
def add(a: int, b: int) -> int:
"""Add two numbers."""
return a + b
The FastMCP version is 4 lines versus 25+ lines. For servers with many tools, this difference compounds dramatically.
Tips for Building MCP Servers
- Start small. Build one or two tools, test them, then expand.
- Use type hints. FastMCP generates JSON schemas from your type hints automatically.
- Write good docstrings. The AI reads tool descriptions to decide when to use them.
- Handle errors gracefully. Return error messages as strings rather than raising exceptions.
- Keep tools focused. One tool should do one thing well. Let the AI compose them.
- Use environment variables. Pass API keys and configuration through
envin the MCP config.
Wrapping Up
FastMCP removes the boilerplate from building MCP servers, letting you focus on the actual functionality. Whether you are wrapping an API, connecting a database, or building custom developer tools, you can go from idea to working MCP server in minutes.
If your MCP tools need to generate media -- images, video, audio, or avatars -- try Hypereal AI free -- 35 credits, no credit card required. Its REST API integrates easily into any MCP server for AI-powered media generation.
Related Articles
Start Building Today
Get 35 free credits on signup. No credit card required. Generate your first image in under 5 minutes.
