MCP Integration Complete Guide
Purpose: This is the comprehensive technical guide covering ALL aspects of MCP integration.
Quick Links:
- Claude Setup Guide - Just want to use MCP with Claude? Start here
- Quick Reference - Common commands and quick setup
- Sunholo Patterns - FastAPI integration patterns
Table of Contents
- Overview & Terminology
- Architecture & Data Flow
- Integration Types
- API Flow Analysis
- Adding New MCP Tools
- Testing & Verification
- Troubleshooting
Overview & Terminology
Core Concepts
MCP (Model Context Protocol)
- Definition: A standardized protocol for AI models to interact with tools and services
- Protocol: JSON-RPC 2.0 over stdio, HTTP, or HTTPS
- Purpose: Enables AI assistants to use external tools in a consistent, secure manner
- Use Cases:
- Claude Desktop/Code direct integration (stdio)
- Web-based tool integrations (HTTP/HTTPS)
- Dynamic tool discovery and execution
MCP Server vs MCP Client
| Aspect | MCP Server | MCP Client |
|---|---|---|
| Role | Provides tools | Connects to servers |
| Examples | demo_server.py, mcp_stdio.py |
mcp_client.py, Claude Desktop |
| Location | backend/tools/mcp_tools/ |
backend/tools/mcp_client.py |
| Protocol | Implements MCP protocol | Consumes MCP protocol |
| Direction | Receives requests, sends responses | Sends requests, receives responses |
Three Types of Tool Integration
1. Native API Tools (Direct Python Functions):
- Location:
backend/tools/(e.g.,google_search.py) - Integration: Direct function calls in
tool_orchestrator.py - Examples:
google_search,ai_search,file-browser - Use When: You need tight integration with backend services
2. Registered MCP Tools (mcp_* prefix):
- Location: Registry in
backend/tools/mcp_servers.py - Server Code:
backend/tools/mcp_tools/*.py - Examples:
mcp_demo,mcp_database - Use When: You have permanent MCP servers to integrate
- Benefits: Pre-configured, appear as named tools, no runtime config needed
3. Dynamic MCP Tools (external_mcp):
- Configuration: Provided at runtime in request
- Connection: Any MCP server (HTTP or local script)
- Use When: Testing, ad-hoc connections, external services
- Benefits: Flexible, no pre-registration needed
Architecture & Data Flow
Complete Request Flow
graph TD
A[User Request] --> B[vac_service.py]
B --> C[Permission Check]
C --> D[First Impression]
D --> E[Tool Selection]
E --> F[tool_orchestrator.py]
F --> G{Tool Type?}
G -->|Native API Tool| H[Direct Function Call]
G -->|Registered MCP| I[mcp_registry.py]
G -->|Dynamic MCP| J[mcp_client.py]
I --> K[mcp_client.py]
J --> L[Connect to Server]
K --> L
L --> M[Tool Discovery]
M --> N[Execute Tool]
N --> O[Format Response]
O --> P[Return to Orchestrator]
H --> P
P --> Q[Aggregate Results]
Q --> R[Stream to User]
Key Components
- Entry Point (
vac_service.py):- Receives user request with
emissaryConfig - Extracts tools and toolConfigs
- Handles permission filtering
- Manages streaming responses
- Receives user request with
- Tool Orchestrator (
tool_orchestrator.py):- Merges tool configurations
- Routes to appropriate handlers
- Manages parallel execution
- Aggregates results
- MCP Registry (
mcp_registry.py):- Manages registered MCP servers
- Provides individual server access
- Handles group operations
- MCP Client (
mcp_client.py):- Maintains connection pool
- Handles protocol communication
- Caches tool discoveries
- Executes tool calls
Integration Scenarios
Scenario 1: Claude Desktop/Code Integration
Purpose: Enable Claude to use Aitana tools directly Method: stdio MCP server Configuration: Add to Claude’s MCP settings Guide: MCP Claude Integration
Scenario 2: Assistant Tool Integration
Purpose: Add tools to Aitana assistants Methods:
- Native Tools: Add Python function to
backend/tools/ - Registered MCP: Add to
mcp_servers.py - Dynamic MCP: Use
external_mcpin toolConfigs
Scenario 3: External Service Integration
Purpose: Connect to third-party MCP servers
Method: external_mcp tool with runtime config
Example:
{
"tools": ["external_mcp"],
"toolConfigs": {
"external_mcp": {
"servers": [{
"url": "https://api.service.com/mcp",
"auth": "api-key"
}]
}
}
}
API Flow Analysis
Request Processing Pipeline
- Initial Request (
/vac/assistant/{assistant_id}):# vac_service.py line 238-243 tools, toolConfigs = permitted_tools( current_user=currentUser, requested_tools=requested_tools, tool_configs=requested_toolConfigs, vac_name="aitana3" ) - Tool Selection (First Impression):
# vac_service.py line 388-397 first_response, tools_to_use, conversation_summary, pause_to_confirm = await first_impression(...) - Context Creation (
tool_orchestrator.py):# tool_orchestrator.py line 108 merged_tools = merge_tool_configs(first_response_tools, tool_configs=toolConfigs) - MCP Tool Detection:
# tool_orchestrator.py line 270-273 elif is_mcp_tool(tool_name): tools_to_use.append(tool_name) - MCP Tool Execution:
# tool_orchestrator.py line 324-333 elif is_mcp_tool(tool_name): async def mcp_wrapper(**kwargs): return await handle_mcp_tool_in_orchestrator( tool_name, input_list_dict=merged_tools.get(tool_name), fallback_question=new_question, **kwargs ) runner.add_task(mcp_wrapper, **common_args)
Tool Configuration Flow
- User provides
emissaryConfig:{ "tools": ["mcp_demo", "google_search"], "toolConfigs": { "mcp_demo": {"timeout": 30}, "google_search": {"max_results": 5} } } -
Permission filtering removes unauthorized tools
-
First impression selects which tools to use
-
Orchestrator merges configs with tool selections
- MCP handler routes to appropriate server
Adding New MCP Tools
Option 1: Registered MCP Server (Recommended)
- Create Server (
backend/tools/mcp_tools/your_server.py):from fastmcp import FastMCP mcp = FastMCP(name="Your MCP Server") @mcp.tool() def your_tool(param1: str, param2: int = 10) -> str: """Tool description""" return f"Result: {param1} - {param2}" if __name__ == "__main__": mcp.run() - Register Server (
backend/tools/mcp_servers.py):MCP_SERVERS = { "your_server": { "name": "Your MCP Server", "description": "Description of your server", "config": { "path": ".../your_server.py" }, "tags": ["category"], "auto_discover": True } } - Add Permissions (
backend/tools/tool_permissions.py):DEFAULT_PERMISSIONS = { "tools": [..., "mcp_your_server"], "toolConfigs": { "mcp_your_server": {"*": "*"} } }
Option 2: Dynamic MCP Connection
Use external_mcp tool with runtime configuration:
{
"tools": ["external_mcp"],
"toolConfigs": {
"external_mcp": {
"servers": [{
"path": "/path/to/your_server.py"
}],
"discover_tools": true
}
}
}
Testing & Verification
1. Direct API Testing
List Available Tools:
curl http://localhost:1956/direct/tools
List MCP Server Tools:
curl http://localhost:1956/direct/tools/mcp/demo/list
Call MCP Tool:
curl -X POST http://localhost:1956/direct/tools/mcp/demo/call \
-H "Content-Type: application/json" \
-d '{
"tool": "demo_text_processor",
"arguments": {
"text": "Hello World",
"operation": "reverse"
}
}'
2. Test Page Verification
- Navigate to http://localhost:1956/test
- Click “Load All Tools”
- Find your MCP server in the list
- Click to expand and see available tools
- Test individual tool calls
3. End-to-End Testing
Via Assistant API (requires configured assistant):
curl -X POST http://localhost:1956/vac/assistant/{assistant_id} \
-H "Content-Type: application/json" \
-d '{
"user_input": "Use the demo MCP to reverse text",
"emissaryConfig": {
"tools": ["mcp_demo"]
}
}'
4. Log Verification
# Check backend logs
tail -f logs/backend.log | grep MCP
# Expected log entries:
# "Connecting to MCP server"
# "Discovered X tools from MCP server"
# "MCP tool call completed"
Troubleshooting
Common Issues
1. MCP Server Not Found
Error: MCP server "xxx" not found in registry
Solution: Ensure server is registered in mcp_servers.py
2. Tool Discovery Fails
Error: Could not discover tools from server
Solutions:
- Check server is running (for HTTP servers)
- Verify script path exists (for local servers)
- Check Python dependencies installed
3. Permission Denied
Error: Tool filtered out by permissions
Solution: Add tool to tool_permissions.py or domain permissions
4. Connection Pool Issues
Symptom: Stale connections, timeouts Solution: Restart backend to clear connection pool
5. Tool Not Listed
Error: Tool 'xxx' not listed, no validation will be performed
Note: This is a warning, not an error. Tool will still execute.
Debug Commands
Check MCP Server Status:
# From backend directory
python3 -c "
from tools.mcp_client import get_mcp_manager
import asyncio
async def test():
manager = await get_mcp_manager()
print(f'Active connections: {len(manager._connections)}')
print(f'Tool cache entries: {len(manager._tool_cache)}')
asyncio.run(test())
"
Test MCP Server Directly:
# Test local FastMCP server
cd backend/tools/mcp_tools
python3 demo_server.py
# In another terminal
echo '{"jsonrpc": "2.0", "method": "tools/list", "id": 1}' | python3 demo_server.py
Performance Optimization
- Connection Pooling: Connections cached for 5 minutes
- Tool Discovery Cache: Tool lists cached per server
- Parallel Execution: Multiple MCP calls run concurrently
- Timeout Configuration: Configurable per context (ui, email, etc.)
Summary
The MCP integration provides three ways to add tools:
- Native API Tools: Direct Python functions for tight integration
- Registered MCP Servers: Pre-configured servers that appear as named tools
- Dynamic MCP Connections: Runtime configuration for flexibility
The system handles all three seamlessly through the same API flow, with intelligent routing in the tool orchestrator. MCP tools can be tested directly via API endpoints or through the full assistant pipeline.
Tested Working Configuration
After extensive testing, here’s the confirmed working setup for Claude Desktop:
{
"mcpServers": {
"aitana": {
"command": "/Users/mark/dev/aitana-labs/frontend/backend/mcp_server_claude_desktop.sh"
},
"aitana_demo": {
"command": "/Users/mark/.local/bin/uv",
"args": ["run", "/Users/mark/dev/aitana-labs/frontend/backend/tools/mcp_tools/demo_server.py"]
}
}
}
Key Learnings from Implementation
-
cwdparameter issues: Thecwdparameter in Claude Desktop configuration doesn’t work reliably. Use absolute paths or bash wrapper commands instead. - Inline script dependencies: For standalone MCP servers, use UV’s inline script metadata:
# /// script # requires-python = ">=3.10" # dependencies = ["fastmcp>=0.1.0"] # /// - UV command syntax:
- Use full path:
/Users/mark/.local/bin/uv - Correct syntax:
uv run script.py(notuv run python script.py)
- Use full path:
- Launcher scripts: The
mcp_server_claude_desktop.shscript provides:- gcloud authentication setup
- Client detection for analytics
- Logging with rotation
- Python environment management
- Multiple servers: You can run multiple MCP servers simultaneously, each appearing as a separate tool source in Claude.
Key benefits:
- Modularity: Tools can be developed independently
- Reusability: MCP servers work with any MCP-compatible client
- Security: Permission system controls access
- Performance: Connection pooling and caching optimize repeated calls
- Flexibility: Support for both local scripts and remote services