Setup for MCP (Model Context Protocol)
Model Context Protocol (MCP) is the universal adapter for LLMs. Instead of writing custom API wrappers for every database, filesystem, or third-party service, you plug them into a standardized protocol. Your LLM client (like Claude Desktop or Cursor) can then call them natively.
Zero-Setup Running (The Modern Way)
You don't need to manually clone repositories or configure virtual environments to run popular MCP servers. The fastest way to run them is by using uvx (Python's ephemeral runner) or npx (Node's equivalent) directly in your client's config file.
Open your Claude Desktop configuration file:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json
Here's a minimal config that runs a SQLite database explorer and a web fetcher instantly:
{
"mcpServers": {
"sqlite": {
"command": "uvx",
"args": ["mcp-server-sqlite", "--db-path", "/path/to/your/database.db"]
},
"web-fetch": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-fetch"]
}
}
}
Restart Claude Desktop, and you'll see the plug icon. The model now has direct access to read your DB and scrape web pages.
Build a Custom Server with FastMCP
When you need to build custom integrations, Python's FastMCP is the easiest SDK. It handles the boilerplate, schema generation, and argument parsing under the hood.
1. Init the Project
We'll use uv to set up the project and manage dependencies instantly:
# Initialize the project
uv init custom-mcp
cd custom-mcp
# Add FastMCP and HTTP client
uv add "mcp[cli]" httpx
2. Write the Server (server.py)
Create a server.py file. Here's a clean implementation of a server providing a tool, a dynamic resource, and a prompt template:
from mcp.server.fastmcp import FastMCP
import httpx
# Initialize server
mcp = FastMCP("Developer-Tools")
# 1. Register a Tool (LLM can execute this)
@mcp.tool()
async def fetch_xkcd_comic(comic_id: int) -> str:
"""Fetch comic details from XKCD.
Args:
comic_id: The ID of the XKCD comic.
"""
async with httpx.AsyncClient() as client:
resp = await client.get(f"https://xkcd.com/{comic_id}/info.0.json")
if resp.status_code == 200:
data = resp.json()
return f"Title: {data['title']}\nAlt: {data['alt']}\nImage: {data['img']}"
return "Comic not found."
# 2. Register a Resource (Static or dynamic data the LLM can read)
@mcp.resource("config://app-settings")
def get_config() -> str:
"""Provide system application settings."""
return "Theme: Dark\nEnvironment: Local\nDebugMode: True"
# 3. Register a Prompt (Pre-defined templates for interactions)
@mcp.prompt()
def review_code(code: str) -> str:
"""Generate a code review template."""
return f"Please review the following code for performance bottlenecks and bugs:\n\n```python\n{code}\n```"
if __name__ == "__main__":
mcp.run(transport="stdio")
Connecting to Your Editor (Cursor)
While Claude Desktop is great for testing, having MCP servers directly inside your code editor is a massive productivity boost. Cursor has native, robust support for MCP. You can configure it globally or lock it to your workspace—both use the exact same JSON configuration format.
Option 1: Global Setup
- Go to Cursor Settings (gear icon in the top right).
- Navigate to Tools & MCPs in the sidebar.
- Click the Add Custom MCP button. This opens Cursor's global
mcp.jsonconfiguration file.
Option 2: Per-Project Setup
To share configurations with your team or activate servers only inside a specific project, create a .cursor/mcp.json file in your project root.
The JSON Configuration
Paste your server details into either your global or project-level mcp.json file:
{
"mcpServers": {
"custom-tools": {
"command": "uv",
"args": [
"--directory",
"/path/to/custom-mcp",
"run",
"server.py"
]
}
}
}
Once saved, Cursor automatically connects to the server and makes the tools available in Composer or Chat.
The Stdio Logging Trap
MCP servers communicate with clients using standard input/output (stdio). This means `stdout` is reserved exclusively for protocol messages.
Never use print() statements in your server code for debugging.
If you run a print statement, it will write to stdout, corrupting the JSON-RPC messages and causing the client to silently disconnect or crash. If you need to log information, redirect it to stderr instead:
import logging
import sys
from mcp.server.fastmcp import FastMCP
# Initialize server
mcp = FastMCP("Logging-Demo")
# Configure logging to write to stderr
logging.basicConfig(level=logging.INFO, stream=sys.stderr)
logger = logging.getLogger(__name__)
@mcp.tool()
def process_data() -> str:
# ❌ AVOID THIS:
# print("Processing...")
# ✅ DO THIS:
logger.info("Processing data safely...")
return "Done"
MCP completely shifts how we think about LLM integrations. By standardizing the interface between the model and external data sources, you write the connection logic once and use it across any supporting IDE or assistant. It removes standard API glue code and lets you focus on building features.