MCP Integration Complete Guide

Purpose: This is the comprehensive technical guide covering ALL aspects of MCP integration.

Quick Links:

Table of Contents

  1. Overview & Terminology
  2. Architecture & Data Flow
  3. Integration Types
  4. API Flow Analysis
  5. Adding New MCP Tools
  6. Testing & Verification
  7. 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

  1. Entry Point (vac_service.py):
    • Receives user request with emissaryConfig
    • Extracts tools and toolConfigs
    • Handles permission filtering
    • Manages streaming responses
  2. Tool Orchestrator (tool_orchestrator.py):
    • Merges tool configurations
    • Routes to appropriate handlers
    • Manages parallel execution
    • Aggregates results
  3. MCP Registry (mcp_registry.py):
    • Manages registered MCP servers
    • Provides individual server access
    • Handles group operations
  4. 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:

  1. Native Tools: Add Python function to backend/tools/
  2. Registered MCP: Add to mcp_servers.py
  3. Dynamic MCP: Use external_mcp in 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

  1. 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"
    )
    
  2. Tool Selection (First Impression):
    # vac_service.py line 388-397
    first_response, tools_to_use, conversation_summary, pause_to_confirm = 
        await first_impression(...)
    
  3. Context Creation (tool_orchestrator.py):
    # tool_orchestrator.py line 108
    merged_tools = merge_tool_configs(first_response_tools, tool_configs=toolConfigs)
    
  4. MCP Tool Detection:
    # tool_orchestrator.py line 270-273
    elif is_mcp_tool(tool_name):
        tools_to_use.append(tool_name)
    
  5. 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

  1. User provides emissaryConfig:
    {
      "tools": ["mcp_demo", "google_search"],
      "toolConfigs": {
        "mcp_demo": {"timeout": 30},
        "google_search": {"max_results": 5}
      }
    }
    
  2. Permission filtering removes unauthorized tools

  3. First impression selects which tools to use

  4. Orchestrator merges configs with tool selections

  5. MCP handler routes to appropriate server

Adding New MCP Tools

  1. 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()
    
  2. 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
        }
    }
    
  3. 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

  1. Navigate to http://localhost:1956/test
  2. Click “Load All Tools”
  3. Find your MCP server in the list
  4. Click to expand and see available tools
  5. 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

  1. Connection Pooling: Connections cached for 5 minutes
  2. Tool Discovery Cache: Tool lists cached per server
  3. Parallel Execution: Multiple MCP calls run concurrently
  4. Timeout Configuration: Configurable per context (ui, email, etc.)

Summary

The MCP integration provides three ways to add tools:

  1. Native API Tools: Direct Python functions for tight integration
  2. Registered MCP Servers: Pre-configured servers that appear as named tools
  3. 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

  1. cwd parameter issues: The cwd parameter in Claude Desktop configuration doesn’t work reliably. Use absolute paths or bash wrapper commands instead.

  2. Inline script dependencies: For standalone MCP servers, use UV’s inline script metadata:
    # /// script
    # requires-python = ">=3.10"
    # dependencies = ["fastmcp>=0.1.0"]
    # ///
    
  3. UV command syntax:
    • Use full path: /Users/mark/.local/bin/uv
    • Correct syntax: uv run script.py (not uv run python script.py)
  4. Launcher scripts: The mcp_server_claude_desktop.sh script provides:
    • gcloud authentication setup
    • Client detection for analytics
    • Logging with rotation
    • Python environment management
  5. 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