Files
ewoooc/services/market_intel/mcp_activation_evidence.py
OoO f4ec50cd30
All checks were successful
CD Pipeline / deploy (push) Successful in 1m7s
[V10.358] add market intel MCP activation evidence review
2026-05-21 11:31:28 +08:00

270 lines
9.6 KiB
Python
Raw 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 啟用後的 redacted evidence本模組只做結構化審核
不打 health endpoint、不開 DB、不寫入、不啟動 fetch。
"""
REQUIRED_ENV_VARS = ("MCP_POSTGRES_PASSWORD", "TAVILY_API_KEY", "EXA_API_KEY")
EXPECTED_HEALTH_TARGETS = (
"http://localhost:3001/health",
"http://localhost:3002/health",
"http://localhost:3003/health",
"http://localhost:3004/health",
)
SAFE_LITERAL_VALUES = {"redacted", "***", "set", "present", "true", "yes", "ok"}
PROHIBITED_EVIDENCE_FLAGS = (
"database_write_executed",
"database_commit_executed",
"scheduler_attached",
"crawler_job_started",
"external_fetch_executed",
"manual_fetch_executed",
)
def _truthy(value):
if isinstance(value, bool):
return value
if isinstance(value, (int, float)):
return bool(value)
if isinstance(value, str):
return value.strip().lower() in {"1", "true", "yes", "y", "ok", "pass", "passed", "healthy", "set", "present", "redacted", "***"}
return False
def _looks_like_secret_literal(value):
if not isinstance(value, str):
return False
normalized = value.strip().lower()
if not normalized or normalized in SAFE_LITERAL_VALUES:
return False
return len(value.strip()) > 2
def _evidence_map(evidence, key):
value = evidence.get(key)
return value if isinstance(value, dict) else {}
def _ack(evidence, key):
acknowledgements = _evidence_map(evidence, "operator_acknowledgements")
return _truthy(evidence.get(key)) or _truthy(acknowledgements.get(key))
def _env_statuses(evidence):
env = _evidence_map(evidence, "required_env_vars") or _evidence_map(evidence, "env")
statuses = []
for name in REQUIRED_ENV_VARS:
value = env.get(name)
statuses.append(
{
"name": name,
"present": _truthy(value),
"secret_literal_in_payload": _looks_like_secret_literal(value),
"value_redacted": bool(value) and not _looks_like_secret_literal(value),
}
)
return statuses
def _health_item_ok(item):
if isinstance(item, bool):
return item
if isinstance(item, (int, float)):
return int(item) == 200
if isinstance(item, str):
return item.strip().lower() in {"200", "ok", "pass", "passed", "healthy"}
if isinstance(item, dict):
return (
int(item.get("status_code") or item.get("code") or 0) == 200
or _truthy(item.get("healthy"))
or _truthy(item.get("passed"))
or _truthy(item.get("status"))
)
return False
def _health_item_for_target(health, target):
if isinstance(health, dict):
if target in health:
return health[target]
port = target.rsplit(":", 1)[-1].split("/", 1)[0]
return health.get(port) or health.get(f"localhost:{port}")
if isinstance(health, list):
for item in health:
if not isinstance(item, dict):
continue
value = item.get("url") or item.get("target") or item.get("endpoint")
port = str(item.get("port") or "")
if value == target or (port and port in target):
return item
return None
def _health_statuses(evidence):
health = evidence.get("health_checks") or evidence.get("health_targets") or {}
return [
{
"target": target,
"passed": _health_item_ok(_health_item_for_target(health, target)),
}
for target in EXPECTED_HEALTH_TARGETS
]
def _sample_evidence_template():
return {
"required_env_vars": {
"MCP_POSTGRES_PASSWORD": "redacted",
"TAVILY_API_KEY": "redacted",
"EXA_API_KEY": "redacted",
},
"health_checks": {
"http://localhost:3001/health": 200,
"http://localhost:3002/health": 200,
"http://localhost:3003/health": 200,
"http://localhost:3004/health": 200,
},
"mcp_router_enabled": True,
"market_intel_readonly_smoke_passed": True,
"telemetry": {
"mcp_calls_table_exists": True,
"read_only_query_executed": True,
},
"operator_acknowledgements": {
"no_remove_orphans": True,
"momo_db_preserved": True,
"router_enabled_after_health": True,
"rollback_plan_confirmed": True,
"fetch_gate_left_closed": True,
},
}
def build_mcp_activation_evidence_preview(*, evidence=None, phase=None):
"""審核 redacted MCP runtime evidence不執行任何外部動作。"""
evidence = evidence or {}
payload_received = bool(evidence)
env_statuses = _env_statuses(evidence)
health_statuses = _health_statuses(evidence)
telemetry = _evidence_map(evidence, "telemetry")
prohibited_flags = [
key for key in PROHIBITED_EVIDENCE_FLAGS
if _truthy(evidence.get(key))
]
secret_literal_keys = [
item["name"] for item in env_statuses
if item["secret_literal_in_payload"]
]
gates = [
{
"key": "evidence_payload_received",
"passed": payload_received,
"label": "已提供 redacted MCP runtime evidence payload",
},
{
"key": "no_secret_literals_in_payload",
"passed": not secret_literal_keys,
"label": "payload 只允許 redacted / boolean不接受真實 secret 字串",
},
{
"key": "required_env_vars_acknowledged",
"passed": all(item["present"] for item in env_statuses),
"label": "三個必要 env 已由操作員確認存在",
},
{
"key": "all_health_targets_passed",
"passed": all(item["passed"] for item in health_statuses),
"label": "四個 localhost MCP health target 全部 200 / healthy",
},
{
"key": "mcp_router_enabled_after_health",
"passed": _truthy(evidence.get("mcp_router_enabled"))
and _ack(evidence, "router_enabled_after_health"),
"label": "router 僅在 health 全過後啟用",
},
{
"key": "market_intel_readonly_smoke_passed",
"passed": _truthy(evidence.get("market_intel_readonly_smoke_passed")),
"label": "market_intel MCP read-only smoke 已通過",
},
{
"key": "telemetry_runtime_confirmed",
"passed": _truthy(telemetry.get("mcp_calls_table_exists"))
and _truthy(telemetry.get("read_only_query_executed")),
"label": "mcp_calls telemetry read-only 查詢鏈路已確認",
},
{
"key": "operator_fallback_confirmed",
"passed": _ack(evidence, "rollback_plan_confirmed"),
"label": "router kill switch 與 stop MCP stack fallback 已確認",
},
{
"key": "production_boundaries_acknowledged",
"passed": _ack(evidence, "no_remove_orphans")
and _ack(evidence, "momo_db_preserved"),
"label": "已確認不使用 remove-orphans 且不動 momo-db lifecycle",
},
{
"key": "manual_fetch_gate_left_closed",
"passed": _ack(evidence, "fetch_gate_left_closed"),
"label": "啟用 MCP 後仍未打開人工 fetch gate",
},
{
"key": "no_write_or_scheduler_evidence",
"passed": not prohibited_flags,
"label": "證據未宣告 DB write、scheduler 或 fetch 已執行",
},
]
blocked_reasons = [
gate["key"] for gate in gates
if not gate["passed"]
]
accepted = payload_received and not blocked_reasons
payload = {
"mode": (
"mcp_activation_evidence_review"
if payload_received
else "mcp_activation_evidence_preview"
),
"phase": phase,
"evidence_payload_received": payload_received,
"activation_evidence_accepted": accepted,
"ready_for_runtime_promotion": accepted,
"ready_for_fetch_gate_review": accepted,
"external_mcp_runtime_evidence_complete": accepted,
"internal_mcp_runtime_evidence_complete": accepted,
"gate_count": len(gates),
"passed_gate_count": sum(1 for gate in gates if gate["passed"]),
"blocked_reasons": blocked_reasons,
"gates": gates,
"env_statuses": env_statuses,
"health_statuses": health_statuses,
"secret_literal_keys": secret_literal_keys,
"prohibited_flags": prohibited_flags,
"sample_evidence_template": _sample_evidence_template(),
"next_operator_steps": [
"若本審核通過,再跑 /api/market_intel/mcp_readiness?execute=true&timeout=3 做只讀 runtime smoke",
"確認 completion audit 的 external/internal runtime 缺口被證據補齊",
"人工 fetch gate 仍需另行審核,不可因 MCP runtime ready 自動抓外站",
],
"payload_persisted": False,
"evidence_persisted": False,
"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,
}
return payload