Files
ewoooc/services/market_intel/mcp_completion_audit.py
OoO 8bffd0307d
All checks were successful
CD Pipeline / deploy (push) Successful in 1m3s
[V10.357] add market intel MCP completion audit
2026-05-21 11:16:10 +08:00

320 lines
13 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""市場情報 MCP 完整度稽核 preview。
本模組只彙整既有 MCP readiness、tool contract、deploy preflight、activation
runbook 與 fetch gate不呼叫 MCP server、不執行 docker/SSH、不連 DB、不寫入。
"""
from services.market_intel.mcp_activation_runbook import (
build_mcp_activation_runbook_preview,
)
from services.market_intel.mcp_contract import build_mcp_tool_contract_preview
from services.market_intel.mcp_deploy_preflight import build_mcp_deploy_preflight_plan
from services.market_intel.mcp_fetch_gate import build_mcp_fetch_gate_preview
from services.market_intel.mcp_readiness import (
EXPECTED_EXTERNAL_SERVERS,
build_mcp_readiness_plan,
)
def _blocked_reasons(gates):
return [item["key"] for item in gates if not item["passed"]]
def _none_executed(payload, keys):
return all(not payload.get(key) for key in keys)
def build_mcp_completion_audit_preview(
*,
runtime_status,
readiness=None,
tool_contract=None,
deploy_preflight=None,
activation_runbook=None,
fetch_gate=None,
):
"""建立 MCP 外部/內部完整度稽核;預設全程 read-only planned preview。"""
readiness = readiness or build_mcp_readiness_plan(execute_requested=False)
tool_contract = tool_contract or build_mcp_tool_contract_preview()
deploy_preflight = deploy_preflight or build_mcp_deploy_preflight_plan()
activation_runbook = activation_runbook or build_mcp_activation_runbook_preview(
preflight=deploy_preflight,
readiness=readiness,
)
fetch_gate = fetch_gate or build_mcp_fetch_gate_preview(
runtime_status,
fetch_requested=False,
execute_readiness=False,
readiness=readiness,
)
external_design_checks = {
"expected_servers_declared": set(EXPECTED_EXTERNAL_SERVERS)
<= {item.get("server") for item in readiness.get("server_statuses", [])},
"compose_file_present": bool(
deploy_preflight.get("checks", {}).get("compose_file_present")
),
"expected_services_declared": bool(
deploy_preflight.get("checks", {}).get("all_expected_services_declared")
),
"expected_containers_declared": bool(
deploy_preflight.get("checks", {}).get("all_expected_containers_declared")
),
"localhost_ports_only": bool(
deploy_preflight.get("checks", {}).get(
"all_public_mcp_ports_localhost_only"
)
),
"readonly_postgres_design": bool(
deploy_preflight.get("checks", {}).get("postgres_readonly_role_required")
and deploy_preflight.get("checks", {}).get(
"postgres_allowed_tables_limited"
)
),
"readonly_filesystem_mounts": bool(
deploy_preflight.get("checks", {}).get("filesystem_mounts_read_only")
),
}
external_design_complete = all(external_design_checks.values())
external_runtime_complete = bool(readiness.get("external_mcp_complete"))
internal_contract_complete = bool(tool_contract.get("contract_ready"))
internal_runtime_complete = bool(readiness.get("internal_mcp_complete"))
market_intel_mcp_integrated = bool(readiness.get("market_intel_mcp_integrated"))
activation_runbook_present = bool(
activation_runbook.get("mode") == "mcp_activation_runbook_preview"
and activation_runbook.get("stages")
)
fetch_gate_present = bool(fetch_gate.get("mode") == "mcp_fetch_gate_planned")
preview_side_effect_free = bool(
_none_executed(
readiness,
(
"database_session_created",
"database_write_executed",
"database_commit_executed",
"external_network_executed",
"scheduler_attached",
"writes_executed",
"would_write_database",
),
)
and _none_executed(
tool_contract,
(
"database_session_created",
"database_write_executed",
"database_commit_executed",
"external_network_executed",
"scheduler_attached",
"writes_executed",
"would_write_database",
),
)
and _none_executed(
deploy_preflight,
(
"deployment_actions_executed",
"docker_command_executed",
"ssh_command_executed",
"database_session_created",
"database_write_executed",
"database_commit_executed",
"external_network_executed",
"scheduler_attached",
"writes_executed",
"would_write_database",
),
)
and _none_executed(
activation_runbook,
(
"deployment_actions_executed",
"docker_command_executed",
"ssh_command_executed",
"database_session_created",
"database_write_executed",
"database_commit_executed",
"external_network_executed",
"scheduler_attached",
"writes_executed",
"would_write_database",
),
)
and _none_executed(
fetch_gate,
(
"network_request_allowed",
"database_session_created",
"database_write_executed",
"database_commit_executed",
"external_network_executed",
"scheduler_attached",
"writes_executed",
"would_write_database",
),
)
)
gates = [
{
"key": "external_mcp_design_complete",
"passed": external_design_complete,
"label": "外部 MCP compose、localhost port、唯讀邊界與 server 清單已定義",
},
{
"key": "external_mcp_runtime_complete",
"passed": external_runtime_complete,
"label": "外部 MCP health 已執行且 router 已啟用",
},
{
"key": "internal_mcp_contract_complete",
"passed": internal_contract_complete,
"label": "market_intel 內部 MCP tool contract 已被 router 白名單覆蓋",
},
{
"key": "internal_mcp_runtime_complete",
"passed": internal_runtime_complete,
"label": "mcp_calls telemetry / read-only 查詢鏈路已在執行態確認",
},
{
"key": "market_intel_mcp_integrated",
"passed": market_intel_mcp_integrated,
"label": "市場情報呼叫端已納入 MCP registry",
},
{
"key": "activation_runbook_present",
"passed": activation_runbook_present,
"label": "外部 MCP 啟用順序、health gate 與 fallback 已建立",
},
{
"key": "manual_fetch_gate_present",
"passed": fetch_gate_present,
"label": "人工 fetch 安全閘門已建立且預設關閉",
},
{
"key": "preview_side_effect_free",
"passed": preview_side_effect_free,
"label": "本 API 不啟動 MCP、不連 DB、不寫入、不抓外站",
},
]
passed_gate_count = sum(1 for item in gates if item["passed"])
return {
"mode": "mcp_completion_audit_preview",
"audit_ready_for_operator_review": True,
"audit_preview_safe": preview_side_effect_free,
"overall_mcp_completion_percent": round(passed_gate_count / len(gates) * 100),
"external_mcp_complete": external_runtime_complete,
"external_mcp_design_complete": external_design_complete,
"external_mcp_runtime_complete": external_runtime_complete,
"internal_mcp_complete": internal_runtime_complete,
"internal_mcp_contract_complete": internal_contract_complete,
"internal_mcp_runtime_complete": internal_runtime_complete,
"market_intel_mcp_integrated": market_intel_mcp_integrated,
"ready_for_external_mcp_activation": bool(
activation_runbook.get("ready_for_operator_activation")
),
"ready_for_internal_mcp_use": bool(
internal_contract_complete and market_intel_mcp_integrated
),
"ready_for_manual_fetch": bool(fetch_gate.get("manual_fetch_gate_open")),
"gates": gates,
"blocked_reasons": _blocked_reasons(gates),
"external_mcp_summary": {
"expected_servers": list(EXPECTED_EXTERNAL_SERVERS),
"configured_servers": [
item["server"]
for item in readiness.get("server_statuses", [])
if item.get("configured")
],
"health_checked": bool(readiness.get("execute_requested")),
"router_enabled": bool(readiness.get("router_enabled")),
"design_checks": external_design_checks,
"runtime_blocked_reasons": readiness.get("blocked_reasons", []),
},
"internal_mcp_summary": {
"caller": tool_contract.get("caller"),
"tool_count": int(tool_contract.get("tool_count") or 0),
"expected_tool_names": tool_contract.get("expected_tool_names", []),
"contract_ready": internal_contract_complete,
"telemetry_mode": readiness.get("telemetry", {}).get("mode"),
"telemetry_table_exists": bool(
readiness.get("telemetry", {}).get("table_exists")
),
"read_only_query_executed": bool(
readiness.get("telemetry", {}).get("read_only_query_executed")
),
},
"activation_summary": {
"mode": activation_runbook.get("mode"),
"ready_for_operator_activation": bool(
activation_runbook.get("ready_for_operator_activation")
),
"stage_count": len(activation_runbook.get("stages", [])),
"blocked_reasons": activation_runbook.get("blocked_reasons", []),
"fallback_count": len(activation_runbook.get("fallback_plan", [])),
},
"fetch_gate_summary": {
"mode": fetch_gate.get("mode"),
"manual_fetch_gate_open": bool(fetch_gate.get("manual_fetch_gate_open")),
"network_request_allowed": bool(fetch_gate.get("network_request_allowed")),
"blocked_reasons": fetch_gate.get("blocked_reasons", []),
},
"next_operator_steps": [
"補齊外部 MCP 必要 env 後,再由操作員依 runbook 啟動 docker-compose.mcp.yml",
"四個 localhost health endpoint 全部 200 後,才允許開 MCP_ROUTER_ENABLED",
"router 開啟後以 execute=true 跑 read-only MCP readiness smoke",
"人工 fetch 仍需先通過 MCP fetch gate且不得寫 DB 或掛 scheduler",
],
"mcp_readiness": readiness,
"mcp_tool_contract": tool_contract,
"mcp_deploy_preflight": deploy_preflight,
"mcp_activation_runbook": activation_runbook,
"mcp_fetch_gate": fetch_gate,
"api_executes_health_check": False,
"api_executes_docker": False,
"api_executes_ssh": False,
"api_opens_database_connection": False,
"api_writes_database": False,
"api_uses_external_network": False,
"database_session_created": False,
"database_write_executed": False,
"database_commit_executed": False,
"external_network_executed": False,
"scheduler_attached": False,
"writes_executed": False,
"would_write_database": False,
}
def build_mcp_completion_audit_for_runtime(*, runtime_status, phase):
"""以 service runtime 狀態組裝完整度稽核,並在子 payload 標記 phase。"""
readiness = build_mcp_readiness_plan(execute_requested=False)
readiness["phase"] = phase
tool_contract = build_mcp_tool_contract_preview()
tool_contract["phase"] = phase
deploy_preflight = build_mcp_deploy_preflight_plan()
deploy_preflight["phase"] = phase
activation_runbook = build_mcp_activation_runbook_preview(
preflight=deploy_preflight,
readiness=readiness,
)
activation_runbook["phase"] = phase
fetch_gate = build_mcp_fetch_gate_preview(
runtime_status,
fetch_requested=False,
execute_readiness=False,
readiness=readiness,
)
fetch_gate["phase"] = phase
audit = build_mcp_completion_audit_preview(
runtime_status=runtime_status,
readiness=readiness,
tool_contract=tool_contract,
deploy_preflight=deploy_preflight,
activation_runbook=activation_runbook,
fetch_gate=fetch_gate,
)
audit["phase"] = phase
return audit