155 lines
4.6 KiB
Markdown
155 lines
4.6 KiB
Markdown
# 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 <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 |