feat(aiops): add mcp agent loop foundation
Some checks failed
CD Pipeline / tests (push) Successful in 1m59s
Code Review / ai-code-review (push) Successful in 28s
run-migration / migrate (push) Failing after 24s
CD Pipeline / post-deploy-checks (push) Has been cancelled
CD Pipeline / build-and-deploy (push) Has been cancelled
Some checks failed
CD Pipeline / tests (push) Successful in 1m59s
Code Review / ai-code-review (push) Successful in 28s
run-migration / migrate (push) Failing after 24s
CD Pipeline / post-deploy-checks (push) Has been cancelled
CD Pipeline / build-and-deploy (push) Has been cancelled
This commit is contained in:
@@ -12,11 +12,65 @@ Provider 註冊中心,實現依賴注入 (DI) 模式:
|
||||
|
||||
import structlog
|
||||
|
||||
from src.plugins.mcp.interfaces import MCPToolProvider
|
||||
from src.plugins.mcp.interfaces import MCPTool, MCPToolProvider, MCPToolResult
|
||||
|
||||
logger = structlog.get_logger(__name__)
|
||||
|
||||
|
||||
class AuditedMCPToolProvider(MCPToolProvider):
|
||||
"""Provider wrapper that writes every MCP tool call to the audit subsystem."""
|
||||
|
||||
def __init__(self, provider: MCPToolProvider) -> None:
|
||||
self._provider = provider
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
return self._provider.name
|
||||
|
||||
@property
|
||||
def enabled(self) -> bool:
|
||||
return self._provider.enabled
|
||||
|
||||
async def list_tools(self) -> list[MCPTool]:
|
||||
return await self._provider.list_tools()
|
||||
|
||||
async def execute(
|
||||
self,
|
||||
tool_name: str,
|
||||
parameters: dict,
|
||||
) -> MCPToolResult:
|
||||
from src.services.mcp_audit_service import monotonic_ms, record_mcp_call
|
||||
|
||||
audit_context = parameters.get("_mcp_audit") if isinstance(parameters, dict) else None
|
||||
provider_parameters = {
|
||||
key: value for key, value in parameters.items()
|
||||
if key != "_mcp_audit"
|
||||
}
|
||||
started = monotonic_ms()
|
||||
result: MCPToolResult | None = None
|
||||
try:
|
||||
result = await self._provider.execute(tool_name, provider_parameters)
|
||||
return result
|
||||
finally:
|
||||
duration_ms = monotonic_ms() - started
|
||||
await record_mcp_call(
|
||||
mcp_server=self.name,
|
||||
tool_name=tool_name,
|
||||
input_params=parameters,
|
||||
output_result=result.output if result else None,
|
||||
duration_ms=duration_ms,
|
||||
success=bool(result.success) if result else False,
|
||||
error_message=result.error if result else "provider_exception",
|
||||
session_id=audit_context.get("session_id") if isinstance(audit_context, dict) else None,
|
||||
flywheel_node=audit_context.get("flywheel_node") if isinstance(audit_context, dict) else None,
|
||||
incident_id=audit_context.get("incident_id") if isinstance(audit_context, dict) else None,
|
||||
agent_role=audit_context.get("agent_role") if isinstance(audit_context, dict) else None,
|
||||
)
|
||||
|
||||
async def health_check(self) -> bool:
|
||||
return await self._provider.health_check()
|
||||
|
||||
|
||||
class ProviderRegistry:
|
||||
"""
|
||||
MCP Tool Provider 註冊中心
|
||||
@@ -52,7 +106,7 @@ class ProviderRegistry:
|
||||
if provider.name in self._providers:
|
||||
raise ValueError(f"Provider '{provider.name}' already registered")
|
||||
|
||||
self._providers[provider.name] = provider
|
||||
self._providers[provider.name] = AuditedMCPToolProvider(provider)
|
||||
logger.info(
|
||||
"provider_registered",
|
||||
name=provider.name,
|
||||
|
||||
Reference in New Issue
Block a user