# MCP JSON-RPC 2.0 Endpoint — Pattern ## Problem Chat2API projects only expose OpenAI-compatible REST endpoints. MCP (Model Context Protocol) clients like Claude Desktop, Cursor, and other tools need JSON-RPC 2.0 endpoints. ## Solution Add a `/mcp` POST endpoint that implements the MCP JSON-RPC 2.0 protocol, proxying tool calls to the existing ChatGPT API pipeline. ## Protocol MCP uses JSON-RPC 2.0 over HTTP POST: ```json // Request {"jsonrpc": "2.0", "method": "initialize", "params": {...}, "id": 1} // Response {"jsonrpc": "2.0", "id": 1, "result": {...}} ``` ## Required Methods | Method | Purpose | |--------|---------| | `initialize` | Handshake, returns capabilities + serverInfo | | `tools/list` | Returns available tools with JSON schemas | | `tools/call` | Executes a tool by name with arguments | | `resources/list` | Lists available resources (can be empty) | | `prompts/list` | Lists available prompts (can be empty) | | `notifications/initialized` | Acknowledgment (no-op) | ## Implementation ### 1. Define MCP Tools ```python MCP_TOOLS = [ { "name": "chat_completion", "description": "Send a chat completion request to ChatGPT via the web API proxy.", "inputSchema": { "type": "object", "properties": { "messages": {"type": "array", "items": {"type": "object"}}, "model": {"type": "string", "default": "gpt-4o"}, "temperature": {"type": "number", "default": 0.7}, "max_tokens": {"type": "integer", "default": 4096}, "tools": {"type": "array", "items": {"type": "object"}} }, "required": ["messages"] } } ] ``` ### 2. JSON-RPC Helpers ```python def mcp_error(code: int, message: str, id: Any = None) -> dict: return {"jsonrpc": "2.0", "id": id, "error": {"code": code, "message": message}} def mcp_response(result: Any, id: Any = None) -> dict: return {"jsonrpc": "2.0", "id": id, "result": result} ``` ### 3. Initialize Handler ```python if method == "initialize": return JSONResponse(mcp_response({ "protocolVersion": "2024-11-05", "capabilities": {"tools": {}, "resources": {}, "prompts": {}}, "serverInfo": {"name": "ChatGPT2API-MCP", "version": "1.0.0"} }, req_id)) ``` ### 4. Tool Call Handler ```python if method == "tools/call": tool_name = params.get("name") tool_args = params.get("arguments", {}) if tool_name == "chat_completion": # Build request data from MCP tool arguments request_data = { "model": tool_args.get("model", "gpt-4o"), "messages": tool_args.get("messages", []), "temperature": tool_args.get("temperature", 0.7), "max_tokens": tool_args.get("max_tokens", 4096), "stream": False } # Inject tools if provided if tool_args.get("tools"): request_data["messages"] = inject_tools_into_messages( request_data["messages"], tool_args["tools"] ) # Forward to existing ChatService pipeline chat_service = ChatService(req_token) await chat_service.set_dynamic_data(request_data) await chat_service.get_chat_requirements() await chat_service.prepare_send_conversation() res = await chat_service.send_conversation() # Extract text content content = res["choices"][0]["message"]["content"] return JSONResponse(mcp_response( {"content": [{"type": "text", "text": content}]}, req_id )) ``` ### 5. Registration in app.py ```python from api.mcp import register_mcp_endpoints register_mcp_endpoints(app, api_prefix, security_scheme) ``` ## Error Codes | Code | Meaning | |------|---------| | -32700 | Parse error | | -32600 | Invalid Request | | -32601 | Method not found | | -32603 | Internal error | ## Testing ```bash # Initialize curl -X POST http://localhost:5005/mcp \ -H "Content-Type: application/json" \ -H "Authorization: Bearer " \ -d '{"jsonrpc":"2.0","method":"initialize","id":1}' # List tools curl -X POST .../mcp \ -d '{"jsonrpc":"2.0","method":"tools/list","id":2}' # Call tool curl -X POST .../mcp \ -d '{"jsonrpc":"2.0","method":"tools/call","params":{"name":"chat_completion","arguments":{"messages":[{"role":"user","content":"hi"}]}},"id":3}' ``` ## Limitations - **Synchronous only**: tools/call blocks until completion (no streaming via MCP) - **No MCP transport**: Only HTTP POST, no stdio or WebSocket transport - **Tool definitions are static**: Must be updated in code, not dynamic from ChatGPT