Skip to main content
Glama
mcp_server.py12.4 kB
#!/usr/bin/env python3 """ MCP Server for Cursor IDE Integration Exposes the router as an MCP server so Cursor can use it for intelligent model routing. """ import asyncio import json import os import sys from pathlib import Path from typing import Any, Optional, Sequence # Add parent directory to path sys.path.insert(0, str(Path(__file__).parent.parent)) try: from mcp.server import Server from mcp.server.stdio import stdio_server from mcp.types import Tool, TextContent HAS_MCP = True except ImportError: # Fallback if MCP SDK structure is different try: from mcp import Server, types from mcp.server.stdio import stdio_server HAS_MCP = True except ImportError: HAS_MCP = False print("Warning: MCP SDK not installed. Install with: pip install mcp") from src.router import MCPRouter, QueryContext, TaskType, Complexity # Note: We don't need MCPRouterClient since Cursor will handle API calls class CursorMCPRouter: """MCP Server wrapper for the router.""" def __init__(self): """Initialize the MCP router server.""" if not HAS_MCP: raise ImportError( "MCP SDK required. Install with: pip install mcp" ) # Router doesn't need API keys - it just recommends models self.router = MCPRouter() # Initialize server try: self.server = Server("mcp-router") except: # Fallback initialization self.server = Server() # Register handlers self._register_handlers() def _register_handlers(self): """Register MCP handlers.""" @self.server.list_tools() async def list_tools() -> list[Tool]: """List available tools.""" return [ Tool( name="route_query", description="Route a query to the best model based on query characteristics and routing strategy", inputSchema={ "type": "object", "properties": { "query": { "type": "string", "description": "The query/prompt to route" }, "strategy": { "type": "string", "enum": ["balanced", "cost", "speed", "quality"], "description": "Routing strategy", "default": "balanced" }, "system_prompt": { "type": "string", "description": "Optional system prompt for context" } }, "required": ["query"] } ), Tool( name="get_model_recommendation", description="Get model recommendation for Cursor to use (Cursor handles API calls with its own keys)", inputSchema={ "type": "object", "properties": { "query": { "type": "string", "description": "The query/prompt to route" }, "strategy": { "type": "string", "enum": ["balanced", "cost", "speed", "quality"], "description": "Routing strategy", "default": "balanced" } }, "required": ["query"] } ), Tool( name="list_models", description="List all available models in the router", inputSchema={ "type": "object", "properties": {} } ), Tool( name="get_routing_stats", description="Get statistics about routing decisions", inputSchema={ "type": "object", "properties": {} } ), Tool( name="analyze_query", description="Analyze a query to determine its characteristics", inputSchema={ "type": "object", "properties": { "query": { "type": "string", "description": "The query to analyze" } }, "required": ["query"] } ) ] @self.server.call_tool() async def call_tool(name: str, arguments: dict) -> list[TextContent]: """Handle tool calls.""" try: if name == "route_query": query = arguments.get("query", "") strategy = arguments.get("strategy", "balanced") # Analyze query first to get context context = self.router.analyzer.analyze(query) decision = self.router.route(query, context=context, strategy=strategy) result = { "selected_model": { "name": decision.selected_model.name, "model_id": decision.selected_model.model_id, "provider": decision.selected_model.provider }, "confidence": decision.confidence, "reasoning": decision.reasoning, "estimated_cost": decision.estimated_cost, "estimated_latency_ms": decision.estimated_latency_ms, "alternatives": [ { "name": alt.name, "model_id": alt.model_id, "provider": alt.provider } for alt in decision.alternatives[:3] ] } return [TextContent( type="text", text=json.dumps(result, indent=2) )] elif name == "get_model_recommendation": query = arguments.get("query", "") strategy = arguments.get("strategy", "balanced") # Analyze query first to get context context = self.router.analyzer.analyze(query) # Route query to get best model decision = self.router.route(query, context=context, strategy=strategy) # Return model recommendation for Cursor to use # Cursor will handle the actual API call with its own keys result = { "recommended_model": { "model_id": decision.selected_model.model_id, "provider": decision.selected_model.provider, "name": decision.selected_model.name }, "confidence": decision.confidence, "reasoning": decision.reasoning, "estimated_cost": decision.estimated_cost, "estimated_latency_ms": decision.estimated_latency_ms, "alternatives": [ { "model_id": alt.model_id, "provider": alt.provider, "name": alt.name } for alt in decision.alternatives[:3] ], "query_analysis": { "task_type": context.task_type.value if context.task_type else None, "complexity": context.complexity.value if context.complexity else None, "estimated_tokens": context.estimated_tokens } } return [TextContent( type="text", text=json.dumps(result, indent=2) )] elif name == "list_models": models = [] for model in self.router.models.values(): models.append({ "name": model.name, "model_id": model.model_id, "provider": model.provider, "context_window": model.context_window, "cost_per_1k_input": model.cost_per_1k_tokens_input, "cost_per_1k_output": model.cost_per_1k_tokens_output, "avg_latency_ms": model.avg_latency_ms, "reasoning_quality": model.reasoning_quality, "code_quality": model.code_quality }) return [TextContent( type="text", text=json.dumps({"models": models}, indent=2) )] elif name == "get_routing_stats": stats = self.router.get_routing_stats() return [TextContent( type="text", text=json.dumps(stats, indent=2) )] elif name == "analyze_query": query = arguments.get("query", "") context = self.router.analyzer.analyze(query) return [TextContent( type="text", text=json.dumps({ "query": query, "task_type": context.task_type.value if context.task_type else None, "complexity": context.complexity.value if context.complexity else None, "estimated_tokens": context.estimated_tokens, "requires_streaming": context.requires_streaming, "requires_multimodal": context.requires_multimodal, "requires_embeddings": context.requires_embeddings }, indent=2) )] else: return [TextContent( type="text", text=json.dumps({"error": f"Unknown tool: {name}"}) )] except Exception as e: return [TextContent( type="text", text=json.dumps({"error": str(e)}) )] async def run(self): """Run the MCP server.""" async with stdio_server() as (read_stream, write_stream): await self.server.run( read_stream, write_stream, self.server.create_initialization_options() ) async def main(): """Main entry point.""" if not HAS_MCP: print("Error: MCP SDK not installed.", file=sys.stderr) print("Install with: pip install mcp", file=sys.stderr) sys.exit(1) try: router_server = CursorMCPRouter() await router_server.run() except Exception as e: print(f"Error starting MCP server: {e}", file=sys.stderr) sys.exit(1) if __name__ == "__main__": asyncio.run(main())

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/AI-Castle-Labs/mcp-router'

If you have feedback or need assistance with the MCP directory API, please join our Discord server