4.6 KiB
4.6 KiB
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:
// 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
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
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
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
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
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
# Initialize
curl -X POST http://localhost:5005/mcp \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <token>" \
-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