feat(api): Phase 13.2 #81 PostgreSQL MCP Tool 整合

整合 Approval/Incident/Timeline 查詢到 MCP Bridge:
- list_approvals: 列出授權請求 (可依狀態篩選)
- get_approval: 取得單一授權詳情
- list_incidents: 列出 Incident (可依狀態篩選)
- list_timeline: 列出最近時間軸事件

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
OG T
2026-03-25 12:46:52 +08:00
parent 23b753dbec
commit 7a8f869104

View File

@@ -367,15 +367,49 @@ class MCPBridge:
],
"database": [
MCPTool(
name="query",
description="Execute SQL query",
name="list_approvals",
description="List approval requests with optional status filter",
input_schema={
"type": "object",
"properties": {
"sql": {"type": "string"},
"params": {"type": "array"},
"status": {"type": "string", "description": "Filter by status: pending, approved, rejected, expired"},
"limit": {"type": "integer", "description": "Max results (default: 20)"},
},
},
server_name=server.name,
),
MCPTool(
name="get_approval",
description="Get a single approval by ID",
input_schema={
"type": "object",
"properties": {
"approval_id": {"type": "string", "description": "Approval UUID"},
},
"required": ["approval_id"],
},
server_name=server.name,
),
MCPTool(
name="list_incidents",
description="List incidents with optional status filter",
input_schema={
"type": "object",
"properties": {
"status": {"type": "string", "description": "Filter by status: active, resolved, escalated"},
"limit": {"type": "integer", "description": "Max results (default: 20)"},
},
},
server_name=server.name,
),
MCPTool(
name="list_timeline",
description="List recent timeline events",
input_schema={
"type": "object",
"properties": {
"limit": {"type": "integer", "description": "Max results (default: 50)"},
},
"required": ["sql"],
},
server_name=server.name,
),
@@ -694,10 +728,120 @@ class MCPBridge:
# Database: 查詢 incident/approval 歷史 (Phase 13.2 #81)
# =============================================
elif server.name == "database":
if tool_name == "query":
# TODO: 整合真實 PostgreSQL 查詢
logger.info(f"[TODO] Database query: {parameters.get('sql', '')[:50]}")
return {"rows": [], "affected": 0, "note": "Phase 13.2 #81 待實作"}
from uuid import UUID
from src.models.approval import ApprovalStatus
from src.services.approval_db import (
get_approval_service,
get_timeline_service,
)
from src.services.incident_service import get_incident_service
if tool_name == "list_approvals":
# 列出 Approval 請求
approval_svc = get_approval_service()
status_str = parameters.get("status")
limit = parameters.get("limit", 20)
status_filter = None
if status_str:
try:
status_filter = ApprovalStatus(status_str.lower())
except ValueError:
return {"error": f"Invalid status: {status_str}. Valid: pending, approved, rejected, expired"}
approvals = await approval_svc.get_all_approvals(
status=status_filter,
limit=limit,
)
return {
"count": len(approvals),
"approvals": [
{
"id": str(a.id),
"action": a.action[:80] if a.action else "",
"status": a.status.value if hasattr(a.status, 'value') else str(a.status),
"risk_level": a.risk_level.value if hasattr(a.risk_level, 'value') else str(a.risk_level),
"signatures": f"{a.current_signatures}/{a.required_signatures}",
"created_at": a.created_at.isoformat() if a.created_at else None,
}
for a in approvals
],
}
elif tool_name == "get_approval":
# 取得單一 Approval
approval_id = parameters.get("approval_id")
if not approval_id:
return {"error": "Missing 'approval_id' parameter"}
approval_svc = get_approval_service()
try:
approval = await approval_svc.get_approval_by_id(UUID(approval_id))
except ValueError:
return {"error": f"Invalid UUID format: {approval_id}"}
if not approval:
return {"error": f"Approval not found: {approval_id}"}
return {
"id": str(approval.id),
"action": approval.action,
"description": approval.description,
"status": approval.status.value if hasattr(approval.status, 'value') else str(approval.status),
"risk_level": approval.risk_level.value if hasattr(approval.risk_level, 'value') else str(approval.risk_level),
"required_signatures": approval.required_signatures,
"current_signatures": approval.current_signatures,
"signatures": [
{"signer": s.signer_name, "timestamp": s.timestamp.isoformat()}
for s in (approval.signatures or [])
],
"created_at": approval.created_at.isoformat() if approval.created_at else None,
"resolved_at": approval.resolved_at.isoformat() if approval.resolved_at else None,
}
elif tool_name == "list_incidents":
# 列出 Incidents
incident_svc = get_incident_service()
status_filter = parameters.get("status")
limit = parameters.get("limit", 20)
incidents = await incident_svc.get_active_incidents()
# 狀態過濾
if status_filter:
incidents = [i for i in incidents if i.status.value == status_filter.lower()]
incidents = incidents[:limit]
return {
"count": len(incidents),
"incidents": [
{
"id": str(i.id),
"title": i.title[:80] if i.title else "",
"severity": i.severity.value if hasattr(i.severity, 'value') else str(i.severity),
"status": i.status.value if hasattr(i.status, 'value') else str(i.status),
"source": i.source,
"created_at": i.created_at.isoformat() if i.created_at else None,
}
for i in incidents
],
}
elif tool_name == "list_timeline":
# 列出 Timeline 事件
timeline_svc = get_timeline_service()
limit = parameters.get("limit", 50)
events = await timeline_svc.get_events(limit=limit)
return {
"count": len(events),
"events": events,
}
else:
return {"error": f"Unknown database tool: {tool_name}"}