sarveshpatel commited on
Commit
75f61dd
Β·
verified Β·
1 Parent(s): b271154

Create app/routes/mcp.py

Browse files
Files changed (1) hide show
  1. app/routes/mcp.py +108 -0
app/routes/mcp.py ADDED
@@ -0,0 +1,108 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """MCP protocol endpoints - SSE and JSON-RPC style."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import logging
7
+ from typing import Any
8
+
9
+ from fastapi import APIRouter, Request
10
+ from fastapi.responses import JSONResponse
11
+ from sse_starlette.sse import EventSourceResponse
12
+
13
+ from app.mcp_handler import TOOL_DEFINITIONS, handle_tool_call
14
+ from app.models import MCPListToolsResponse, MCPToolInput, MCPToolResult
15
+
16
+ logger = logging.getLogger(__name__)
17
+
18
+ router = APIRouter(prefix="/mcp", tags=["mcp"])
19
+
20
+
21
+ @router.get("/tools")
22
+ async def list_tools() -> MCPListToolsResponse:
23
+ """List all available MCP tools."""
24
+ return MCPListToolsResponse(tools=TOOL_DEFINITIONS)
25
+
26
+
27
+ @router.post("/execute", response_model=MCPToolResult)
28
+ async def execute_tool(tool_input: MCPToolInput) -> MCPToolResult:
29
+ """Execute an MCP tool call."""
30
+ logger.info("MCP tool call: %s", tool_input.name)
31
+ result = await handle_tool_call(tool_input.name, tool_input.arguments)
32
+ return result
33
+
34
+
35
+ @router.post("/execute/stream")
36
+ async def execute_tool_stream(tool_input: MCPToolInput) -> EventSourceResponse:
37
+ """Execute an MCP tool call with SSE streaming response."""
38
+
39
+ async def event_generator():
40
+ yield {
41
+ "event": "start",
42
+ "data": json.dumps({"tool": tool_input.name, "status": "running"}),
43
+ }
44
+
45
+ result = await handle_tool_call(tool_input.name, tool_input.arguments)
46
+
47
+ yield {
48
+ "event": "result",
49
+ "data": json.dumps(result.model_dump()),
50
+ }
51
+
52
+ yield {
53
+ "event": "done",
54
+ "data": json.dumps({"status": "completed"}),
55
+ }
56
+
57
+ return EventSourceResponse(event_generator())
58
+
59
+
60
+ # ─── JSON-RPC 2.0 Compatible Endpoint ─────────────────────────────
61
+
62
+ @router.post("/jsonrpc")
63
+ async def jsonrpc_handler(request: Request) -> JSONResponse:
64
+ """Handle JSON-RPC 2.0 style MCP requests."""
65
+ try:
66
+ body = await request.json()
67
+ except Exception:
68
+ return JSONResponse(
69
+ status_code=400,
70
+ content={
71
+ "jsonrpc": "2.0",
72
+ "error": {"code": -32700, "message": "Parse error"},
73
+ "id": None,
74
+ },
75
+ )
76
+
77
+ request_id = body.get("id")
78
+ method = body.get("method", "")
79
+ params = body.get("params", {})
80
+
81
+ if method == "tools/list":
82
+ return JSONResponse(
83
+ content={
84
+ "jsonrpc": "2.0",
85
+ "result": {"tools": TOOL_DEFINITIONS},
86
+ "id": request_id,
87
+ }
88
+ )
89
+ elif method == "tools/call":
90
+ tool_name = params.get("name", "")
91
+ tool_args = params.get("arguments", {})
92
+ result = await handle_tool_call(tool_name, tool_args)
93
+ return JSONResponse(
94
+ content={
95
+ "jsonrpc": "2.0",
96
+ "result": result.model_dump(),
97
+ "id": request_id,
98
+ }
99
+ )
100
+ else:
101
+ return JSONResponse(
102
+ status_code=400,
103
+ content={
104
+ "jsonrpc": "2.0",
105
+ "error": {"code": -32601, "message": f"Method not found: {method}"},
106
+ "id": request_id,
107
+ },
108
+ )