merge: chore/cleanup-remove-bloat-and-secrets into main
This commit is contained in:
commit
bde2f3a97d
1173 changed files with 437609 additions and 0 deletions
4
app/api/v1/mcp/__init__.py
Normal file
4
app/api/v1/mcp/__init__.py
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
"""MCP v1 routes."""
|
||||
from .router import router
|
||||
|
||||
__all__ = ["router"]
|
||||
164
app/api/v1/mcp/router.py
Normal file
164
app/api/v1/mcp/router.py
Normal file
|
|
@ -0,0 +1,164 @@
|
|||
"""T33 MCP Server — HTTP wrapper for SSE transport.
|
||||
|
||||
Per v4.0 §T33. Endpoints:
|
||||
POST /mcp JSON-RPC 2.0 endpoint
|
||||
GET /mcp/tools Tool catalog
|
||||
POST /mcp/call/{tool_id} Direct tool execution (no JSON-RPC)
|
||||
|
||||
The server speaks the Model Context Protocol natively. Claude Desktop
|
||||
and Cursor connect via:
|
||||
{"mcpServers": {"rugmunch": {"url": "https://mcp.rugmunch.io/mcp", "transport": "sse"}}}
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
from fastapi import APIRouter, Request
|
||||
from pydantic import BaseModel
|
||||
|
||||
from app.mcp.server import (
|
||||
MCP_PROTOCOL_VERSION,
|
||||
MCP_SERVER_VERSION,
|
||||
TOOL_CATALOG,
|
||||
TOOL_DEPRECATED,
|
||||
TOOL_SUCCESSORS,
|
||||
TOOL_VERSIONS,
|
||||
call_tool,
|
||||
)
|
||||
|
||||
router = APIRouter(prefix="/mcp", tags=["mcp"])
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class JsonRpcRequest(BaseModel):
|
||||
jsonrpc: str = "2.0"
|
||||
method: str
|
||||
params: dict[str, Any] = {}
|
||||
id: int | str | None = None
|
||||
|
||||
|
||||
class JsonRpcResponse(BaseModel):
|
||||
jsonrpc: str = "2.0"
|
||||
result: Any | None = None
|
||||
error: dict | None = None
|
||||
id: int | str | None = None
|
||||
|
||||
|
||||
@router.post("")
|
||||
async def jsonrpc_handler(req: JsonRpcRequest) -> dict:
|
||||
"""JSON-RPC 2.0 endpoint for MCP clients.
|
||||
|
||||
Methods:
|
||||
- initialize → returns server info
|
||||
- tools/list → returns tool catalog
|
||||
- tools/call → dispatches to backend
|
||||
- resources/list → empty
|
||||
- prompts/list → empty
|
||||
"""
|
||||
if req.jsonrpc != "2.0":
|
||||
return {"jsonrpc": "2.0", "error": {"code": -32600, "message": "invalid jsonrpc version"}, "id": req.id}
|
||||
|
||||
if req.method == "initialize":
|
||||
return {
|
||||
"jsonrpc": "2.0",
|
||||
"result": {
|
||||
"protocolVersion": "2024-11-05",
|
||||
"serverInfo": {
|
||||
"name": "rugmunch-intelligence",
|
||||
"version": MCP_SERVER_VERSION,
|
||||
"description": "Crypto intelligence platform — 13+ chains, 8 MCP tools, x402 paid tier",
|
||||
},
|
||||
"capabilities": {"tools": {}, "resources": {}, "prompts": {}},
|
||||
},
|
||||
"id": req.id,
|
||||
}
|
||||
|
||||
if req.method == "tools/list":
|
||||
return {
|
||||
"jsonrpc": "2.0",
|
||||
"result": {"tools": TOOL_CATALOG},
|
||||
"id": req.id,
|
||||
}
|
||||
|
||||
if req.method == "tools/call":
|
||||
name = req.params.get("name", "")
|
||||
arguments = req.params.get("arguments", {})
|
||||
if not name:
|
||||
return {"jsonrpc": "2.0", "error": {"code": -32602, "message": "tool name required"}, "id": req.id}
|
||||
result = await call_tool(name, arguments)
|
||||
return {
|
||||
"jsonrpc": "2.0",
|
||||
"result": {
|
||||
"content": [{"type": "text", "text": json.dumps(result, default=str)[:50000]}],
|
||||
"isError": "error" in result,
|
||||
},
|
||||
"id": req.id,
|
||||
}
|
||||
|
||||
if req.method == "resources/list":
|
||||
return {"jsonrpc": "2.0", "result": {"resources": []}, "id": req.id}
|
||||
|
||||
if req.method == "prompts/list":
|
||||
return {"jsonrpc": "2.0", "result": {"prompts": []}, "id": req.id}
|
||||
|
||||
if req.method == "notifications/initialized":
|
||||
return {"jsonrpc": "2.0", "result": {}, "id": req.id}
|
||||
|
||||
return {
|
||||
"jsonrpc": "2.0",
|
||||
"error": {"code": -32601, "message": f"method not found: {req.method}"},
|
||||
"id": req.id,
|
||||
}
|
||||
|
||||
|
||||
@router.get("/tools")
|
||||
async def list_tools() -> dict:
|
||||
"""Plain JSON endpoint (for direct integration, no JSON-RPC)."""
|
||||
return {"server": "rugmunch-intelligence", "version": MCP_SERVER_VERSION, "tools": TOOL_CATALOG}
|
||||
|
||||
|
||||
@router.get("/info")
|
||||
async def server_info() -> dict:
|
||||
"""Server metadata: version, protocol, tool count, capabilities.
|
||||
|
||||
Use this to discover the MCP server's capabilities without listing all tools.
|
||||
Equivalent to MCP initialize handshake.
|
||||
"""
|
||||
return {
|
||||
"server": "rugmunch-intelligence",
|
||||
"server_version": MCP_SERVER_VERSION,
|
||||
"protocol_version": MCP_PROTOCOL_VERSION,
|
||||
"tool_count": len(TOOL_CATALOG),
|
||||
"tools_versioned": sum(1 for t in TOOL_CATALOG if t["name"] in TOOL_VERSIONS),
|
||||
"tools_deprecated": list(TOOL_DEPRECATED),
|
||||
"tools_with_successors": list(TOOL_SUCCESSORS.keys()),
|
||||
"capabilities": ["tools", "resources", "prompts"],
|
||||
"endpoints": {
|
||||
"jsonrpc": "/mcp",
|
||||
"tools_list": "/mcp/tools",
|
||||
"server_info": "/mcp/info",
|
||||
"direct_call": "/mcp/call/{tool_id}",
|
||||
},
|
||||
"auth": {
|
||||
"free_tier_daily": 5,
|
||||
"pro_tier": "x402 micropayment per call",
|
||||
"x402_endpoint": "https://x402.rugmunch.io",
|
||||
},
|
||||
"links": {
|
||||
"homepage": "https://rugmunch.io",
|
||||
"mcp_endpoint": "https://mcp.rugmunch.io/mcp",
|
||||
"status": "https://status.rugmunch.io",
|
||||
"docs": "https://github.com/Rug-Munch-Media-LLC/rmi-docs",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@router.post("/call/{tool_id}")
|
||||
async def direct_call(tool_id: str, request: Request) -> dict:
|
||||
"""Direct tool execution (no JSON-RPC). For curl/scripts."""
|
||||
body = await request.json() if request.headers.get("content-type", "").startswith("application/json") else {}
|
||||
arguments = body.get("arguments", body) if isinstance(body, dict) else {}
|
||||
result = await call_tool(tool_id, arguments)
|
||||
return result
|
||||
Loading…
Add table
Add a link
Reference in a new issue