Model Context Protocol (MCP) Integration
This document explains how to integrate Sunholo VACs (Virtual Agent Computers) with Claude Desktop and Claude Code using the Model Context Protocol (MCP). Sunholo provides a flexible, extensible MCP integration system that allows you to expose VAC functionality and add custom tools for AI applications.
Overview
Model Context Protocol (MCP) is a standard that allows AI applications to interact with external systems through a set of tools. Sunholo's MCP integration provides:
- Easy Integration: Simple setup for Claude Desktop and Claude Code
- Built-in VAC Tools: Automatic access to all Sunholo VAC functionality
- Custom Tools: Add your own tools using decorators or programmatically
- Multiple Deployment Options: Standalone servers, FastAPI integration, or remote servers
Quick Start
1. Basic FastAPI MCP Server
The simplest way to get started is using the create_app_with_mcp helper:
from sunholo.agents.fastapi import VACRoutesFastAPI
async def my_interpreter(question, vector_name, chat_history, callback, **kwargs):
"""Your VAC interpreter logic here."""
return {"answer": f"Response to: {question}", "source_documents": []}
# One line setup with MCP server
app, vac_routes = VACRoutesFastAPI.create_app_with_mcp(
title="My VAC App",
stream_interpreter=my_interpreter
)
# Add custom tools
@vac_routes.add_mcp_tool
async def my_custom_tool(param: str) -> str:
return f"Processed: {param}"
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)
This gives you immediate access to:
vac_stream- Stream responses from VACsvac_query- Query VACs (non-streaming)list_available_vacs- List all configured VACsget_vac_info- Get VAC configuration details
2. Adding Custom Tools with Decorators
Create your own MCP server with custom tools:
#!/usr/bin/env python3
from sunholo.mcp.extensible_mcp_server import create_mcp_server, mcp_tool
# Global tool registration using decorators
@mcp_tool("get_weather", "Get weather information")
async def get_weather(city: str) -> str:
"""Get weather for a city."""
# Your weather logic here
return f"Weather in {city}: Sunny, 22°C"
@mcp_tool("calculate", "Perform calculations")
async def calculate(expression: str) -> str:
"""Safely evaluate math expressions."""
try:
result = eval(expression) # Use a safer parser in production
return f"{expression} = {result}"
except:
return "Invalid expression"
# Create server with built-in VAC tools + custom tools
server = create_mcp_server("my-custom-server", include_vac_tools=True)
if __name__ == "__main__":
server.run()
Install with:
fastmcp install claude-desktop my_custom_server.py --with sunholo[anthropic]
2. FastAPI Integration with Custom Tools
Add MCP tools to your existing FastAPI application:
from sunholo.agents.fastapi import VACRoutesFastAPI
from fastapi import FastAPI
async def my_interpreter(question, vector_name, chat_history, callback, **kwargs):
"""Your VAC interpreter logic."""
return {"answer": f"Response: {question}", "source_documents": []}
# Method 1: Use the helper method (recommended)
app, vac_routes = VACRoutesFastAPI.create_app_with_mcp(
title="My App",
stream_interpreter=my_interpreter
)
# Method 2: Manual setup with existing FastAPI app
# app = FastAPI()
# vac_routes = VACRoutesFastAPI(
# app,
# stream_interpreter=my_interpreter,
# enable_mcp_server=True
# )
# Add tools using decorators
@vac_routes.add_mcp_tool
async def get_server_stats() -> dict:
"""Get server statistics."""
return {"uptime": 3600, "requests": 1234}
@vac_routes.add_mcp_tool("word_count", "Count words in text")
async def count_words(text: str) -> dict:
"""Count words and characters in text."""
return {
"words": len(text.split()),
"characters": len(text)
}
# Add tools programmatically
async def custom_tool(param: str) -> str:
return f"Processing: {param}"
vac_routes.add_mcp_tool(custom_tool, "process_data", "Process data")
# Your app is now available at /mcp endpoint for Claude Desktop remote integration
Available MCP Tools
Once connected, Claude Desktop and Claude Code will have access to these built-in tools:
vac_stream
Stream responses from a Sunholo VAC (asynchronous streaming interface).
Parameters:
vector_name(string): Name of the VAC to interact withuser_input(string): The user's question or inputchat_history(array, optional): Previous conversation historystream_wait_time(float, optional): Time to wait between stream chunks (default: 7)stream_timeout(float, optional): Maximum time to wait for response (default: 120)
vac_query
Query a Sunholo VAC (non-streaming, same as vac_stream but different name for compatibility).
Parameters:
vector_name(string): Name of the VAC to interact withuser_input(string): The user's question or inputchat_history(array, optional): Previous conversation history
list_available_vacs
List all available VAC configurations.
Parameters: None Returns: Array of available VAC names
get_vac_info
Get detailed information about a specific VAC configuration.
Parameters:
vector_name(string): Name of the VAC to get information for
Returns: Dictionary with VAC configuration details (name, LLM, model, etc.)
Installation Options
Option 1: FastMCP CLI (Recommended)
For Claude Desktop:
# Install FastMCP if not already installed
pip install fastmcp
# Navigate to the examples directory
cd examples/
# Install the Sunholo MCP server
fastmcp install claude-desktop sunholo_mcp_server.py --with sunholo[anthropic]
For Claude Code:
# Install the Sunholo MCP server for Claude Code
fastmcp install claude-code sunholo_mcp_server.py --with sunholo[anthropic]
Option 2: Manual Configuration
For Claude Desktop:
-
Install dependencies:
pip install sunholo[anthropic] fastmcp -
Configure Claude Desktop: Edit your configuration file:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json
{
"mcpServers": {
"sunholo-vac": {
"command": "python",
"args": ["/path/to/sunholo-py/examples/sunholo_mcp_server.py"],
"env": {
"VAC_CONFIG_FOLDER": "/path/to/your/vac/config"
}
}
}
} - macOS:
For Claude Code:
Follow the same manual configuration approach as Claude Desktop.
Option 3: Remote MCP Server
For remote deployments, you can run Sunholo as an HTTP MCP server:
1. Deploy Your FastAPI VAC Server
from sunholo.agents.fastapi import VACRoutesFastAPI
routes = VACRoutesFastAPI(enable_mcp_server=True)
# Deploy to your cloud provider
app = routes.app
2. Configure Claude Desktop for Remote MCP
Note: Remote MCP servers require Claude Pro, Team, or Enterprise plans.
- Open Claude Desktop
- Go to Settings > Connectors
- Click "Add custom connector"
- Enter your server URL:
https://your-domain.com/mcp - Complete the authentication flow
- Click "Add"
Advanced Usage
New Features
create_app_with_mcp Helper Method
The create_app_with_mcp class method simplifies MCP setup by handling lifespan management automatically:
from sunholo.agents.fastapi import VACRoutesFastAPI
# This method automatically:
# - Creates FastAPI app with proper lifespan
# - Enables MCP server at /mcp endpoint
# - Registers built-in VAC tools
# - Handles MCP server mounting
app, vac_routes = VACRoutesFastAPI.create_app_with_mcp(
title="My VAC Application",
stream_interpreter=my_stream_interpreter,
vac_interpreter=my_vac_interpreter, # Optional
app_lifespan=my_lifespan # Optional custom lifespan
)
# Add custom tools after creation
@vac_routes.add_mcp_tool
async def my_tool(param: str) -> str:
return f"Result: {param}"
Debug Endpoint
When MCP is enabled, a debug endpoint is automatically available at /debug/mcp that shows:
{
"mcp_enabled": true,
"has_mcp_server": true,
"mcp_tools_count": 9,
"mcp_tools": ["vac_query", "list_available_vacs", "get_vac_info", "demo_reverse_text", "..."],
"tool_details": [{"name": "vac_query", "description": "Query a Sunholo VAC..."}],
"pending_tools": 0,
"message": "MCP server is available at /mcp endpoint with 9 tools"
}
MCP Server Management Methods
The VACRoutesFastAPI class provides methods for managing MCP tools:
# List registered tools and resources
tools = vac_routes.list_mcp_tools()
resources = vac_routes.list_mcp_resources()
# Get the MCP server instance for advanced usage
mcp_server = vac_routes.get_mcp_server()
# Add resources (data sources)
@vac_routes.add_mcp_resource
async def my_resource(uri: str) -> dict:
"""Provide resource data."""
return {"data": f"Resource for {uri}"}
Custom Tool Registration
There are multiple ways to register tools:
1. Global Decorator Registration
from sunholo.mcp.extensible_mcp_server import mcp_tool
@mcp_tool("tool_name", "Tool description")
async def my_tool(param: str) -> str:
return f"Result: {param}"
2. Direct Server Registration
server = create_mcp_server("my-server")
@server.add_tool
async def another_tool(data: str) -> str:
return f"Processed: {data}"
3. FastAPI Integration
routes = VACRoutesFastAPI(enable_mcp_server=True)
@routes.add_mcp_tool
async def fastapi_tool(input_data: str) -> str:
return f"FastAPI processed: {input_data}"
4. Programmatic Registration
async def my_function(text: str) -> str:
return text.upper()
# Add to server
server.add_tool(my_function, "uppercase_text", "Convert text to uppercase")
# Or add to FastAPI routes
routes.add_mcp_tool(my_function, "uppercase_text", "Convert text to uppercase")
Resource Registration
MCP also supports resources (data sources):
from sunholo.mcp.extensible_mcp_server import mcp_resource
@mcp_resource("system_info", "Get system information")
async def get_system_info(resource_uri: str) -> dict:
"""Get system information resource."""
return {
"system": "Linux",
"python_version": "3.11",
"resource_uri": resource_uri
}
Environment Configuration
Configure the MCP server with environment variables:
# Default VAC to use
export DEFAULT_VAC_NAME="my-chatbot"
# Path to VAC configuration files
export VAC_CONFIG_FOLDER="/path/to/configs"
# Logging level
export LOG_LEVEL="INFO"
Or set in Claude Desktop configuration:
{
"mcpServers": {
"my-sunholo-server": {
"command": "python",
"args": ["/path/to/my_server.py"],
"env": {
"DEFAULT_VAC_NAME": "my-chatbot",
"VAC_CONFIG_FOLDER": "/Users/me/vac-configs"
}
}
}
}
Deployment Options
Option 1: Local Claude Desktop Integration
# Install directly with FastMCP
fastmcp install claude-desktop my_server.py --with sunholo[anthropic]
# Or manual configuration in claude_desktop_config.json
{
"mcpServers": {
"my-server": {
"command": "python",
"args": ["/path/to/my_server.py"]
}
}
}
Option 2: Claude Code Integration
fastmcp install claude-code my_server.py --with sunholo[anthropic]
Option 3: Remote FastAPI Server
# Deploy your FastAPI app with MCP enabled
routes = VACRoutesFastAPI(enable_mcp_server=True)
app = routes.app
# Deploy to cloud provider, then configure Claude Desktop:
# Settings > Connectors > Add custom connector
# URL: https://your-domain.com/mcp
Testing the Integration
1. Test FastAPI MCP Server
Test the MCP server directly:
# Run the simple example
python examples/fastapi_vac_mcp_simple.py
# In another terminal, test the MCP endpoint
curl -X POST http://localhost:8000/mcp/mcp \
-H "Content-Type: application/json" \
-d '{"jsonrpc": "2.0", "id": 1, "method": "tools/list"}'
# Test the debug endpoint (if using fastapi_vac_demo.py)
curl http://localhost:8000/debug/mcp
The server provides both HTTP endpoints and MCP tools.
2. Test with Claude Desktop/Code
Once configured using FastMCP CLI or manual configuration:
In Claude Desktop:
- Start a new conversation
- Type: "Use the vac_stream tool to ask 'What is machine learning?' to the 'demo' VAC"
- Claude will automatically use the MCP tool
In Claude Code:
- The tools will be available automatically
- Use them in conversation: "List available VACs and then query the demo VAC about Python programming"
3. Test Available Tools
Try these example prompts in Claude Desktop or Claude Code:
- "List all available VACs using the list_available_vacs tool"
- "Get information about the demo VAC using get_vac_info"
- "Use vac_stream to ask the demo VAC: 'Explain quantum computing'"
- "Query the demo VAC about 'best practices for API design'"
4. Verify Installation
Check that the MCP server is properly installed:
# For Claude Desktop
fastmcp list claude-desktop
# For Claude Code
fastmcp list claude-code
Example Use Cases
Example 1: Business Logic Integration
@mcp_tool("search_customers", "Search customer database")
async def search_customers(query: str) -> list:
"""Search customers by name or email."""
# Your database logic here
return [
{"name": "John Doe", "email": "john@example.com"},
{"name": "Jane Smith", "email": "jane@example.com"}
]
@mcp_tool("get_order_status", "Get order status by ID")
async def get_order_status(order_id: str) -> dict:
"""Get status of an order."""
# Your order system logic here
return {
"order_id": order_id,
"status": "shipped",
"tracking": "1234567890"
}
Example 2: External API Integration
import httpx
@mcp_tool("translate_text", "Translate text between languages")
async def translate_text(text: str, target_lang: str = "es") -> str:
"""Translate text to target language."""
# Integration with translation service
async with httpx.AsyncClient() as client:
# Your translation API call here
return f"Translated '{text}' to {target_lang}: [translation result]"
Example 3: File System Operations
from pathlib import Path
@mcp_tool("list_project_files", "List files in project directory")
async def list_project_files(directory: str = ".") -> list:
"""List files in a project directory."""
try:
path = Path(directory)
return [f.name for f in path.iterdir() if f.is_file()]
except Exception as e:
return [f"Error: {str(e)}"]
@mcp_tool("read_config_file", "Read application configuration")
async def read_config_file(config_name: str) -> dict:
"""Read configuration file."""
config_path = Path(f"configs/{config_name}.json")
if config_path.exists():
import json
return json.loads(config_path.read_text())
return {"error": "Config file not found"}
Best Practices
1. Tool Design
- Clear Descriptions: Provide detailed docstrings and descriptions
- Type Hints: Use proper type hints for parameters and return values
- Error Handling: Always handle exceptions gracefully
- Parameter Validation: Validate inputs before processing
2. Security Considerations
- Input Sanitization: Always validate and sanitize user inputs
- Resource Limits: Implement timeouts and resource limits
- Authentication: Use proper authentication for remote servers
- Sensitive Data: Never expose secrets or sensitive information
3. Performance
- Async Operations: Use async/await for I/O operations
- Caching: Implement caching for expensive operations
- Resource Management: Properly manage database connections and API clients
- Error Recovery: Implement retry logic for external services
4. Development Workflow
# Development server with auto-reload
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("--dev", action="store_true", help="Development mode")
args = parser.parse_args()
if args.dev:
print("Development mode - tools may have debug output")
server.run()
Troubleshooting
Common Issues
-
Import Errors: Make sure all dependencies are installed
pip install sunholo[anthropic] fastmcp -
FastMCP Not Found: Install FastMCP separately if needed
pip install fastmcp>=2.12.0 -
VAC Tools Not Working: Check environment variables and VAC configuration
export DEFAULT_VAC_NAME="demo"
export VAC_CONFIG_FOLDER="/path/to/configs" -
Claude Desktop Connection Issues:
- Check MCP server is running
- Verify configuration file syntax
- Check server logs for errors
-
Tools Not Appearing:
- Ensure tools are properly registered
- Check for import errors
- Verify FastMCP server startup
MCP Server Not Available
- Check that
enable_mcp_server=Trueis set - Verify FastMCP is installed:
pip install fastmcp>=2.12.0 - Check server logs for mounting errors
Connection Issues
- Ensure the correct port and URL in Claude Code config
- Verify the server is running and accessible
- Check firewall settings if running on different machines
Tool Execution Errors
- Verify
vector_nameexists in your VAC configuration - Check interpreter functions are properly configured
- Review server logs for detailed error messages
Debug Mode
# Enable debug logging
import logging
logging.basicConfig(level=logging.DEBUG)
# List registered tools
server = create_mcp_server("debug-server")
print(f"Registered tools: {server.list_registered_tools()}")
print(f"Registered resources: {server.list_registered_resources()}")
Multiple VACs
You can run multiple VAC servers on different ports and configure Claude to access multiple MCP servers:
{
"mcpServers": {
"vac-chatbot": {
"transport": "http",
"url": "http://localhost:8000/mcp"
},
"vac-analysis": {
"transport": "http",
"url": "http://localhost:8001/mcp"
}
}
}
Production Deployment
For production use:
- Use HTTPS endpoints
- Configure proper authentication
- Set appropriate timeouts
- Monitor MCP tool usage
- Consider rate limiting
Security Considerations for Remote MCP
- Only connect to trusted servers
- Review requested permissions carefully
- Monitor for unexpected behavior
- Use HTTPS for production deployments
- Implement proper authentication and rate limiting
Migration from Old MCP Integration
If you're upgrading from the old MCP integration:
Before (Old System)
from sunholo.mcp.vac_mcp_server_fastmcp import VACMCPServer
server = VACMCPServer(stream_interpreter=my_func)
After (New System)
from sunholo.mcp.extensible_mcp_server import create_mcp_server
# Built-in VAC tools are included automatically
server = create_mcp_server("my-server", include_vac_tools=True)
# Add custom tools easily
@server.add_tool
async def my_custom_tool(param: str) -> str:
return f"Result: {param}"
The new system is backward compatible, but we recommend migrating to the extensible system for better flexibility and easier tool management.
Support
For questions or issues:
- Check the examples in
/examples/ - Review this integration guide
- File issues at: https://github.com/sunholo-data/sunholo-py/issues
The extensible MCP system makes it easy to create powerful Claude Desktop and Claude Code integrations with both built-in VAC functionality and your own custom tools!
MCP Tool Discovery
The sunholo.mcp.discovery module provides dynamic discovery and registration of tools from external MCP servers. This is useful when your application needs to connect to multiple MCP servers and discover their available tools at runtime.
MCPDiscovery
from sunholo.mcp.discovery import MCPDiscovery
discovery = MCPDiscovery()
# Register MCP servers
discovery.register_server(
"search",
name="Search Server",
url="http://localhost:8080/mcp",
tags=["search", "retrieval"],
)
discovery.register_server(
"email",
name="Email Server",
command="python",
args=["-m", "email_mcp_server"],
)
# Discover tools on all servers
tools = await discovery.discover_all()
# Get info about a specific tool
info = discovery.get_tool_info("mcp_search_query")
# List all discovered tools
all_tools = discovery.list_discovered_tools()
Tool ID Convention
Discovered tools follow the naming convention mcp_{server_id}_{tool_name}:
mcp_search- Server-level tool IDmcp_search_query- Specific tool on the search serverexternal_mcp- Generic external MCP tool identifier
Adding to Available Tools
Integrate discovered MCP tools with your permission system:
from sunholo.mcp.discovery import MCPDiscovery, is_mcp_tool
discovery = MCPDiscovery()
discovery.register_server("search", url="http://search-server/mcp")
# Add MCP tools to an available tools list
available = discovery.add_to_available_tools(["existing_tool_1", "existing_tool_2"])
# ["existing_tool_1", "existing_tool_2", "external_mcp", "mcp_search"]
# Check if a tool ID is an MCP tool
is_mcp_tool("mcp_search") # True
is_mcp_tool("external_mcp") # True
is_mcp_tool("regular_tool") # False
Server Configuration
Servers can connect via HTTP/SSE or stdio transport:
from sunholo.mcp.discovery import MCPServerConfig
# HTTP/SSE server
http_server = MCPServerConfig(
server_id="remote",
name="Remote MCP Server",
url="https://mcp.example.com/sse",
auth="bearer_token",
)
# Stdio server (local process)
stdio_server = MCPServerConfig(
server_id="local",
name="Local MCP Server",
command="python",
args=["-m", "my_mcp_server"],
)
print(http_server.tool_id) # "mcp_remote"
print(http_server.to_dict()) # Serializable config
