108 行
4.0 KiB
Python
可执行文件
108 行
4.0 KiB
Python
可执行文件
#!/usr/bin/env python3
|
|
"""Validate MCP baseline documents for P19+MCP-1."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
ROOT = Path(__file__).resolve().parent.parent
|
|
DOC_PATH = ROOT / "docs" / "mcp-tools.md"
|
|
EXAMPLE_PATH = ROOT / ".mcp.json.example"
|
|
AGENTS_PATH = ROOT / "AGENTS.md"
|
|
|
|
|
|
def fail(message: str) -> int:
|
|
print(f"[ERR] {message}")
|
|
return 1
|
|
|
|
|
|
def require(text: str, needle: str, label: str, errors: list[str]) -> None:
|
|
if needle not in text:
|
|
errors.append(f"missing {label}: {needle}")
|
|
|
|
|
|
def main() -> int:
|
|
errors: list[str] = []
|
|
|
|
if not DOC_PATH.exists():
|
|
errors.append(f"missing file: {DOC_PATH}")
|
|
if not EXAMPLE_PATH.exists():
|
|
errors.append(f"missing file: {EXAMPLE_PATH}")
|
|
if not AGENTS_PATH.exists():
|
|
errors.append(f"missing file: {AGENTS_PATH}")
|
|
|
|
if errors:
|
|
for error in errors:
|
|
print(f"[ERR] {error}")
|
|
return 1
|
|
|
|
doc_text = DOC_PATH.read_text(encoding="utf-8")
|
|
agents_text = AGENTS_PATH.read_text(encoding="utf-8")
|
|
|
|
require(doc_text, "## Enabled Servers", "section", errors)
|
|
require(doc_text, "- `postgres`", "enabled postgres", errors)
|
|
require(doc_text, "## Candidate Servers (Not Enabled)", "section", errors)
|
|
require(doc_text, "`github`", "candidate github", errors)
|
|
require(doc_text, "postgres:query", "tool", errors)
|
|
require(doc_text, "READ ONLY", "readonly phrase", errors)
|
|
require(doc_text, "不提交真实密钥/连接串", "secret hygiene", errors)
|
|
require(doc_text, "github deferred", "github deferred", errors)
|
|
|
|
if "docs/mcp-tools.md" not in agents_text:
|
|
errors.append("AGENTS.md missing docs/mcp-tools.md reference")
|
|
|
|
try:
|
|
example = json.loads(EXAMPLE_PATH.read_text(encoding="utf-8"))
|
|
except json.JSONDecodeError as exc:
|
|
errors.append(f"invalid json in .mcp.json.example: {exc}")
|
|
example = {}
|
|
|
|
servers = example.get("mcpServers") if isinstance(example, dict) else None
|
|
if not isinstance(servers, dict):
|
|
errors.append(".mcp.json.example missing mcpServers object")
|
|
else:
|
|
if set(servers.keys()) != {"postgres"}:
|
|
errors.append(f".mcp.json.example must contain only postgres server, got: {sorted(servers.keys())}")
|
|
|
|
postgres = servers.get("postgres")
|
|
if not isinstance(postgres, dict):
|
|
errors.append("postgres server config missing or invalid")
|
|
else:
|
|
command = postgres.get("command")
|
|
args = postgres.get("args")
|
|
if command != "npx":
|
|
errors.append(f"postgres command must be npx, got: {command}")
|
|
if not isinstance(args, list):
|
|
errors.append("postgres args must be a list")
|
|
else:
|
|
flattened = " ".join(str(item) for item in args)
|
|
if "@modelcontextprotocol/server-postgres" not in flattened:
|
|
errors.append("postgres args missing official server package")
|
|
if "<READONLY_USER>" not in flattened:
|
|
errors.append("postgres args missing readonly user placeholder")
|
|
if "<PASSWORD>" not in flattened or "<HOST>" not in flattened or "<PORT>" not in flattened or "<DB>" not in flattened:
|
|
errors.append("postgres args missing connection placeholders")
|
|
|
|
example_text_upper = EXAMPLE_PATH.read_text(encoding="utf-8").upper()
|
|
for forbidden in ["GITHUB", "TOKEN"]:
|
|
if forbidden in example_text_upper:
|
|
errors.append(f".mcp.json.example contains forbidden token: {forbidden}")
|
|
|
|
if "postgresql://localhost/" in EXAMPLE_PATH.read_text(encoding="utf-8"):
|
|
errors.append(".mcp.json.example must not contain a concrete localhost connection string")
|
|
|
|
if errors:
|
|
print("[ERR] MCP tools validation failed")
|
|
for error in errors:
|
|
print(f" - {error}")
|
|
return 1
|
|
|
|
print("[OK] MCP tools baseline validated (docs/mcp-tools.md, .mcp.json.example, AGENTS.md)")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|