Integrating FastMCP with Cursor IDE
Integrating FastMCP with Cursor IDE
Cursor IDE provides excellent support for Model Context Protocol (MCP) servers, allowing you to seamlessly integrate your FastMCP servers directly into your development workflow. This guide walks you through setting up and using FastMCP servers with Cursor.
What is MCP in Cursor?
Model Context Protocol (MCP) is a protocol for connecting Cursor to external tools and data sources. Instead of explaining your project structure repeatedly, you can integrate directly with your tools.
Cursor supports MCP servers written in any language that can print to stdout
or serve an HTTP endpoint - making FastMCP (Python) a perfect fit!
Why Use FastMCP with Cursor?
FastMCP's simplicity and powerful features make it ideal for Cursor integration:
- Quick Development: Build MCP servers with minimal boilerplate
- Rich Tools: Create sophisticated tools with type safety
- Resource Access: Provide contextual data to your AI assistant
- Easy Deployment: Multiple transport options for different use cases
Transport Methods
Cursor supports three transport methods for MCP servers:
Transport | Execution | Deployment | Users | Input | Auth |
---|---|---|---|---|---|
stdio |
Local | Cursor manages | Single user | Shell command | Manual |
SSE |
Local/Remote | Deploy as server | Multiple users | URL to SSE endpoint | OAuth |
Streamable HTTP |
Local/Remote | Deploy as server | Multiple users | URL to HTTP endpoint | OAuth |
For FastMCP servers, we'll focus on the stdio
method for local development and testing.
Setting Up FastMCP with Cursor
Step 1: Create Your FastMCP Server
First, let's create a simple FastMCP server that provides development tools:
# cursor_dev_tools.py
import asyncio
import subprocess
from pathlib import Path
from fastmcp import FastMCP
# Create the MCP server
mcp = FastMCP("Cursor Development Tools")
@mcp.tool()
def get_project_structure(max_depth: int = 2) -> str:
"""Get the current project structure up to specified depth."""
try:
result = subprocess.run(
["find", ".", "-type", "f", "-name", "*.py", "-o", "-name", "*.js", "-o", "-name", "*.ts", "-o", "-name", "*.json"],
capture_output=True,
text=True,
cwd="."
)
files = result.stdout.strip().split('\n')
# Organize by directory
structure = {}
for file in files:
if file:
parts = Path(file).parts
current = structure
for part in parts[:-1]:
if part not in current:
current[part] = {}
current = current[part]
current[parts[-1]] = "file"
return f"Project Structure:\n{_format_structure(structure)}"
except Exception as e:
return f"Error getting project structure: {str(e)}"
def _format_structure(structure, indent=0):
"""Format the structure dictionary as a tree."""
result = []
for key, value in structure.items():
prefix = " " * indent + "βββ "
if isinstance(value, dict):
result.append(f"{prefix}{key}/")
result.append(_format_structure(value, indent + 1))
else:
result.append(f"{prefix}{key}")
return "\n".join(result)
@mcp.tool()
def run_command(command: str, working_dir: str = ".") -> str:
"""Execute a shell command in the specified directory."""
try:
result = subprocess.run(
command,
shell=True,
capture_output=True,
text=True,
cwd=working_dir,
timeout=30
)
output = []
if result.stdout:
output.append(f"STDOUT:\n{result.stdout}")
if result.stderr:
output.append(f"STDERR:\n{result.stderr}")
output.append(f"Return code: {result.returncode}")
return "\n\n".join(output)
except subprocess.TimeoutExpired:
return "Command timed out after 30 seconds"
except Exception as e:
return f"Error executing command: {str(e)}"
@mcp.tool()
def search_code(pattern: str, file_extension: str = "py") -> str:
"""Search for code patterns in project files."""
try:
result = subprocess.run(
["grep", "-r", "--include", f"*.{file_extension}", pattern, "."],
capture_output=True,
text=True
)
if result.returncode == 0:
return f"Found matches for '{pattern}':\n\n{result.stdout}"
else:
return f"No matches found for pattern '{pattern}' in *.{file_extension} files"
except Exception as e:
return f"Error searching code: {str(e)}"
@mcp.resource("file://project-docs")
async def get_project_docs():
"""Provide project documentation and README files."""
docs = []
# Look for common documentation files
doc_files = ["README.md", "README.rst", "docs/README.md", "CONTRIBUTING.md"]
for doc_file in doc_files:
path = Path(doc_file)
if path.exists():
try:
content = path.read_text()
docs.append(f"=== {doc_file} ===\n{content}\n")
except Exception as e:
docs.append(f"=== {doc_file} ===\nError reading file: {str(e)}\n")
if not docs:
docs.append("No documentation files found in project root.")
return "\n".join(docs)
if __name__ == "__main__":
mcp.run()
Step 2: Configure MCP in Cursor
You have two options for configuration:
Option A: Project-Specific Configuration
Create .cursor/mcp.json
in your project root:
{
"mcpServers": {
"fastmcp-dev-tools": {
"command": "python",
"args": ["cursor_dev_tools.py"],
"env": {}
}
}
}
Option B: Global Configuration
Create ~/.cursor/mcp.json
in your home directory for global access:
{
"mcpServers": {
"fastmcp-dev-tools": {
"command": "python",
"args": ["/path/to/your/cursor_dev_tools.py"],
"env": {}
}
}
}
Step 3: Using Environment Variables
For servers that need API keys or configuration:
{
"mcpServers": {
"fastmcp-api-server": {
"command": "python",
"args": ["api_server.py"],
"env": {
"API_KEY": "your-api-key-here",
"DEBUG": "true"
}
}
}
}
Using MCP Tools in Cursor
Automatic Tool Usage
The Cursor Composer Agent automatically uses MCP tools listed under "Available Tools" when relevant. Your FastMCP tools will appear in the chat interface.
Manual Tool Invocation
You can also ask for specific tools by name:
- "Use the get_project_structure tool to show me the project layout"
- "Run the search_code tool to find all async functions"
- "Execute run_command to install dependencies"
Tool Approval and Auto-run
By default, Cursor asks for approval before using MCP tools. You can:
- Manual Approval: Click the arrow next to tool names to see arguments and approve
- Auto-run Mode: Enable "Yolo mode" for automatic tool execution without approval
Advanced FastMCP Features for Cursor
Returning Images
FastMCP servers can return images that Cursor will display in chat:
import base64
from fastmcp import FastMCP
mcp = FastMCP("Image Generator")
@mcp.tool()
def generate_diagram() -> dict:
"""Generate a simple diagram and return as image."""
# Create or generate your image
with open("diagram.png", "rb") as f:
image_data = base64.b64encode(f.read()).decode()
return {
"content": [
{
"type": "image",
"data": image_data,
"mimeType": "image/png"
}
]
}
Complex Resource Providers
Use FastMCP's resource system to provide rich contextual data:
@mcp.resource("file://git-status")
async def get_git_status():
"""Provide current git repository status."""
try:
# Get git status
status_result = subprocess.run(
["git", "status", "--porcelain"],
capture_output=True,
text=True
)
# Get recent commits
log_result = subprocess.run(
["git", "log", "--oneline", "-10"],
capture_output=True,
text=True
)
return f"Git Status:\n{status_result.stdout}\n\nRecent Commits:\n{log_result.stdout}"
except Exception as e:
return f"Error getting git status: {str(e)}"
Debugging and Troubleshooting
Viewing MCP Logs
To debug MCP server issues in Cursor:
- Open the Output panel (
ββ§U
on Mac,Ctrl+Shift+U
on Windows/Linux) - Select "MCP Logs" from the dropdown
- Check for connection errors, authentication issues, or server crashes
Common Issues
Server Not Starting
- Check that Python and your script are in the correct path
- Verify the mcp.json
configuration syntax
- Ensure all dependencies are installed
Tools Not Appearing - Restart Cursor after configuration changes - Check MCP logs for error messages - Verify your FastMCP server runs correctly from command line
Permission Errors - Ensure the Python script has execute permissions - Check that Cursor has access to the specified directories
Testing Your Server
Test your FastMCP server independently:
# Run your server directly
python cursor_dev_tools.py
# Test with MCP client tools
npx @modelcontextprotocol/inspector cursor_dev_tools.py
Security Best Practices
When using FastMCP servers with Cursor:
- Verify Sources: Only use MCP servers from trusted developers
- Review Code: Audit server code before installation, especially for sensitive projects
- Limit Permissions: Use restricted API keys with minimal required permissions
- Environment Variables: Store secrets in environment variables, never hardcode them
- Local Development: Use
stdio
transport for sensitive development work
Real-World Examples
Development Workflow Integration
# Enhanced development tools server
from fastmcp import FastMCP
import subprocess
import json
mcp = FastMCP("Advanced Dev Tools")
@mcp.tool()
def run_tests(test_pattern: str = "") -> str:
"""Run project tests with optional pattern filtering."""
cmd = ["python", "-m", "pytest"]
if test_pattern:
cmd.extend(["-k", test_pattern])
result = subprocess.run(cmd, capture_output=True, text=True)
return f"Test Results:\n{result.stdout}\n{result.stderr}"
@mcp.tool()
def format_code() -> str:
"""Format Python code using black."""
result = subprocess.run(
["black", ".", "--check", "--diff"],
capture_output=True,
text=True
)
if result.returncode == 0:
return "Code is already formatted correctly"
else:
return f"Code formatting suggestions:\n{result.stdout}"
@mcp.tool()
def check_dependencies() -> str:
"""Check for outdated package dependencies."""
result = subprocess.run(
["pip", "list", "--outdated", "--format", "json"],
capture_output=True,
text=True
)
try:
outdated = json.loads(result.stdout)
if outdated:
output = "Outdated packages:\n"
for pkg in outdated:
output += f"- {pkg['name']}: {pkg['version']} -> {pkg['latest_version']}\n"
return output
else:
return "All packages are up to date!"
except:
return "Error checking dependencies"
API Integration Tools
@mcp.tool()
def fetch_api_docs(api_url: str) -> str:
"""Fetch API documentation from OpenAPI/Swagger endpoints."""
import requests
try:
response = requests.get(f"{api_url}/openapi.json", timeout=10)
if response.status_code == 200:
api_spec = response.json()
return f"API Documentation for {api_url}:\n{json.dumps(api_spec, indent=2)}"
else:
return f"Failed to fetch API docs: HTTP {response.status_code}"
except Exception as e:
return f"Error fetching API docs: {str(e)}"
Next Steps
Now that you have FastMCP integrated with Cursor:
- Explore Available Tools: Check out the MCP Tools directory for pre-built servers
- Build Custom Tools: Create domain-specific tools for your development workflow
- Share Your Servers: Consider publishing useful FastMCP servers for the community
- Production Deployment: Move to SSE or HTTP transport for team collaboration
The combination of FastMCP's ease of development and Cursor's powerful MCP integration creates a seamless AI-assisted development experience. Start building tools that understand your specific codebase and workflow!
Troubleshooting FAQ
Q: My FastMCP server isn't appearing in Cursor's tool list
A: Check that your mcp.json
syntax is correct and restart Cursor. Verify the server runs independently with python your_server.py
.
Q: Tools are being called but returning errors A: Check the MCP logs in Cursor's Output panel. Common issues include missing dependencies or incorrect working directories.
Q: How can I disable a server temporarily?
A: Go to Settings (ββ§J
) β Features β Model Context Protocol and toggle the server off without removing the configuration.
Q: Can I use FastMCP with remote Cursor installations? A: For remote development, consider deploying your FastMCP server with SSE or HTTP transport instead of stdio.
With FastMCP and Cursor, you now have a powerful platform for creating AI-assisted development tools that understand your specific needs and codebase!