Files
A-Team-Security-Infra-Agent…/backend/services/mcp_client.py
nogueiraguh 1135e9d6a9 refactor: decompose monolith — app.py 10,600→143 lines, 30+ modules
Fase 0 complete: extract all business logic, auth, database, and 176
endpoints from monolithic app.py into dedicated modules.

Structure:
- app.py: FastAPI hub (CORS, routers, startup/shutdown)
- models.py: 13 Pydantic request models
- utils.py: shared utilities (embed status, upload validation, process registries)
- config.py: constants, env vars, model catalogs
- auth/: crypto, jwt_auth, oidc, rate_limit
- database/: db(), init_db(), schema DDL
- services/: genai, compliance, chat, terraform, embeddings, cis_reports, mcp
- routes/: 13 APIRouter modules (auth, users, oci_config, oci_explorer,
  genai, mcp, adb, embeddings, reports, chat, terraform, settings, cis_engine)

Also: README updated with OCIR auth instructions for manual docker run.

86 tests passing.
2026-04-06 15:20:10 -03:00

94 lines
4.6 KiB
Python

"""MCP client — tool discovery from MCP servers."""
import json
from config import MCP_DIR, log
from database import db
async def _discover_mcp_tools(mcp_srv: dict) -> list[dict]:
"""Connect to MCP server and list available tools."""
from mcp import ClientSession
if mcp_srv["server_type"] in ("stdio", "module"):
from mcp.client.stdio import stdio_client, StdioServerParameters
cmd = mcp_srv.get("command") or "python3"
args_raw = mcp_srv.get("args")
args = json.loads(args_raw) if isinstance(args_raw, str) else (args_raw or [])
env_raw = mcp_srv.get("env_vars")
env = json.loads(env_raw) if isinstance(env_raw, str) else (env_raw or None)
params = StdioServerParameters(command=cmd, args=args, env=env)
async with stdio_client(params) as streams:
async with ClientSession(*streams) as session:
await session.initialize()
result = await session.list_tools()
return [{"name": t.name, "description": t.description or "",
"input_schema": t.inputSchema if hasattr(t, 'inputSchema') else {}} for t in result.tools]
elif mcp_srv["server_type"] == "sse":
from mcp.client.sse import sse_client
async with sse_client(mcp_srv["url"]) as streams:
async with ClientSession(*streams) as session:
await session.initialize()
result = await session.list_tools()
return [{"name": t.name, "description": t.description or "",
"input_schema": t.inputSchema if hasattr(t, 'inputSchema') else {}} for t in result.tools]
return []
async def _execute_mcp_tool(mcp_srv: dict, tool_name: str, arguments: dict) -> str:
"""Connect to MCP server and execute a specific tool."""
from mcp import ClientSession
if mcp_srv["server_type"] in ("stdio", "module"):
from mcp.client.stdio import stdio_client, StdioServerParameters
cmd = mcp_srv.get("command") or "python3"
args_raw = mcp_srv.get("args")
args = json.loads(args_raw) if isinstance(args_raw, str) else (args_raw or [])
env_raw = mcp_srv.get("env_vars")
env = json.loads(env_raw) if isinstance(env_raw, str) else (env_raw or None)
params = StdioServerParameters(command=cmd, args=args, env=env)
async with stdio_client(params) as streams:
async with ClientSession(*streams) as session:
await session.initialize()
result = await session.call_tool(tool_name, arguments)
parts = []
for c in result.content:
if hasattr(c, 'text'):
parts.append(c.text)
elif hasattr(c, 'data'):
parts.append(str(c.data))
return "\n".join(parts) if parts else "Tool executed successfully (no output)"
elif mcp_srv["server_type"] == "sse":
from mcp.client.sse import sse_client
async with sse_client(mcp_srv["url"]) as streams:
async with ClientSession(*streams) as session:
await session.initialize()
result = await session.call_tool(tool_name, arguments)
parts = []
for c in result.content:
if hasattr(c, 'text'):
parts.append(c.text)
elif hasattr(c, 'data'):
parts.append(str(c.data))
return "\n".join(parts) if parts else "Tool executed successfully (no output)"
raise ValueError(f"Unsupported MCP server type: {mcp_srv['server_type']}")
def _get_active_mcp_tools(user_id: str) -> list[dict]:
"""Get all tools from active MCP servers for a user, with server reference."""
with db() as c:
rows = c.execute(
"SELECT * FROM mcp_servers WHERE is_active=1 AND (user_id=? OR is_global=1)",
(user_id,)
).fetchall()
result = []
for r in rows:
srv = dict(r)
tools_raw = srv.get("tools")
if not tools_raw:
continue
try:
tools = json.loads(tools_raw) if isinstance(tools_raw, str) else tools_raw
except Exception as e:
log.warning(f"Failed to parse tools JSON for MCP server '{srv.get('name', '?')}': {e}")
continue
for t in tools:
if isinstance(t, dict) and t.get("name"):
result.append({"server": srv, "tool": t})
return result
# ── ADB Vector DB Config ─────────────────────────────────────────────────────