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:
@@ -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}"}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user