Skip to content

Commit 2bd24f9

Browse files
authored
Update main.py
1 parent 519c244 commit 2bd24f9

1 file changed

Lines changed: 76 additions & 71 deletions

File tree

main/api/mcp/main.py

Lines changed: 76 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,101 +1,106 @@
1-
from fastapi import FastAPI, HTTPException, WebSocket
2-
from pydantic import BaseModel
3-
from typing import Dict, Any
4-
import uvicorn
5-
from tools.auth_tool import AuthenticationTool
1+
from fastapi import FastAPI, Request, HTTPException
2+
from fastapi.middleware.cors import CORSMiddleware
3+
from tools.auth_tool import AuthTool
64
from tools.vial_management import VialManagementTool
75
from tools.health import HealthTool
86
from tools.blockchain import BlockchainTool
97
from tools.claude_tool import ClaudeTool
108
from tools.wallet import WalletTool
11-
from lib.mcp_transport import MCPTransport
9+
from config.config import DatabaseConfig, ServerConfig, limiter, batch_sync_limiter
1210
from lib.notifications import NotificationHandler
13-
from config.config import DatabaseConfig
14-
import logging
11+
from fastapi.responses import JSONResponse
12+
import uvicorn
13+
import os
14+
from typing import Dict
1515

1616
app = FastAPI()
17-
logger = logging.getLogger("mcp")
18-
logger.setLevel(logging.INFO)
19-
handler = logging.StreamHandler()
20-
handler.setFormatter(logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s'))
21-
logger.addHandler(handler)
22-
23-
class MCPRequest(BaseModel):
24-
jsonrpc: str = "2.0"
25-
method: str
26-
params: Dict[str, Any]
27-
id: int
17+
app.state.config = ServerConfig()
18+
app.state.db = DatabaseConfig()
19+
app.state.notification_handler = NotificationHandler()
2820

29-
class MCPResponse(BaseModel):
30-
jsonrpc: str = "2.0"
31-
result: Any = None
32-
error: Any = None
33-
id: int
21+
app.add_middleware(
22+
CORSMiddleware,
23+
allow_origins=["*"],
24+
allow_credentials=True,
25+
allow_methods=["*"],
26+
allow_headers=["*"],
27+
)
3428

3529
class MCPServer:
3630
def __init__(self):
37-
self.db = DatabaseConfig()
38-
self.notification_handler = NotificationHandler()
3931
self.tools = {
40-
"authentication": AuthenticationTool(self.db),
41-
"vial-management": VialManagementTool(self.db),
42-
"health": HealthTool(self.db, self.tools),
43-
"blockchain": BlockchainTool(self.db),
44-
"claude": ClaudeTool(self.db),
45-
"wallet": WalletTool(self.db)
32+
"authentication": AuthTool(app.state.config),
33+
"vial_management": VialManagementTool(app.state.db),
34+
"health": HealthTool(),
35+
"blockchain": BlockchainTool(app.state.db),
36+
"claude": ClaudeTool(app.state.db),
37+
"wallet": WalletTool(app.state.db),
4638
}
47-
self.transport = MCPTransport(self.handle_request)
4839

49-
async def start(self):
50-
await self.db.connect()
51-
logger.info("MCP Server started on port 8000")
52-
53-
async def handle_request(self, request: MCPRequest) -> MCPResponse:
40+
async def execute(self, request: Dict) -> Dict:
5441
try:
55-
tool_name, *method_parts = request.method.split(".")
56-
if tool_name not in self.tools:
57-
raise HTTPException(404, f"Unknown tool: {tool_name}")
58-
tool = self.tools[tool_name]
59-
result = await tool.execute(request.params)
60-
# Send notification for wallet and Claude operations
61-
if tool_name in ["claude", "wallet"]:
62-
notification_method = f"{tool_name}.{method_parts[0] if method_parts else 'operationComplete'}"
63-
await self.notification_handler.send_notification(
64-
request.params.get("user_id", "default"),
65-
{"jsonrpc": "2.0", "method": notification_method, "params": result}
42+
if request.get("jsonrpc") != "2.0":
43+
raise HTTPException(400, "Invalid JSON-RPC request")
44+
45+
method = request.get("method")
46+
if not method:
47+
raise HTTPException(400, "Method not specified")
48+
49+
tool_name, method_name = method.split(".", 1) if "." in method else (method, "")
50+
tool = self.tools.get(tool_name)
51+
if not tool:
52+
raise HTTPException(400, "Invalid tool")
53+
54+
params = request.get("params", {})
55+
params["method"] = method_name
56+
result = await tool.execute(params)
57+
58+
# Send notification for wallet operations
59+
if tool_name == "wallet":
60+
await app.state.notification_handler.send_notification(
61+
params.get("user_id"), {"method": method, "params": result.dict()}
6662
)
67-
return MCPResponse(id=request.id, result=result, error=None)
63+
64+
return {"jsonrpc": "2.0", "result": result.dict(), "id": request.get("id")}
6865
except Exception as e:
69-
logger.error(f"Request error: {str(e)}")
70-
return MCPResponse(
71-
id=request.id,
72-
error={"code": -32000, "message": str(e)},
73-
result=None
74-
)
66+
return {"jsonrpc": "2.0", "error": {"message": str(e)}, "id": request.get("id")}
7567

7668
server = MCPServer()
7769

78-
@app.on_event("startup")
79-
async def startup_event():
80-
await server.start()
70+
@app.get("/mcp/health")
71+
async def health_check(request: Request):
72+
if request.headers.get("X-Forwarded-Proto", "http") != "https":
73+
raise HTTPException(400, "HTTPS required")
74+
return {"status": "healthy"}
8175

8276
@app.post("/mcp/execute")
83-
async def execute(request: MCPRequest):
84-
return await server.transport.handle(request)
77+
@limiter.limit("10/minute") # General rate limit
78+
async def execute(request: Request, body: Dict):
79+
if request.headers.get("X-Forwarded-Proto", "http") != "https":
80+
raise HTTPException(400, "HTTPS required")
81+
return await server.execute(body)
8582

86-
@app.get("/mcp/health")
87-
async def health_check():
88-
return await server.tools["health"].execute({})
83+
@app.post("/mcp/execute/wallet.batchSync")
84+
@batch_sync_limiter
85+
async def batch_sync(request: Request, body: Dict):
86+
if request.headers.get("X-Forwarded-Proto", "http") != "https":
87+
raise HTTPException(400, "HTTPS required")
88+
return await server.execute(body)
8989

9090
@app.websocket("/mcp/notifications")
91-
async def websocket_endpoint(websocket: WebSocket, client_id: str):
92-
await server.notification_handler.connect(websocket, client_id)
91+
async def websocket_endpoint(websocket, client_id: str):
92+
await app.state.notification_handler.connect(websocket, client_id)
9393
try:
9494
while True:
95-
await websocket.receive_text() # Keep connection alive
96-
except Exception as e:
97-
logger.error(f"WebSocket error: {str(e)}")
98-
await server.notification_handler.disconnect(client_id)
95+
await websocket.receive_text()
96+
except Exception:
97+
await app.state.notification_handler.disconnect(client_id)
9998

10099
if __name__ == "__main__":
101-
uvicorn.run(app, host="0.0.0.0", port=8000)
100+
uvicorn.run(
101+
app,
102+
host=app.state.config.host,
103+
port=app.state.config.port,
104+
ssl_keyfile=app.state.config.ssl_key_path,
105+
ssl_certfile=app.state.config.ssl_cert_path
106+
)

0 commit comments

Comments
 (0)