From fdf3c43244905ad4bb826bc9a641b8e55e8f2da1 Mon Sep 17 00:00:00 2001 From: shunli Date: Tue, 10 Mar 2026 14:20:59 +0800 Subject: [PATCH] =?UTF-8?q?docs(P19MCP1):=20=E8=90=BD=E5=9C=B0MCP=E5=8F=AA?= =?UTF-8?q?=E8=AF=BB=E5=B7=A5=E5=85=B7=E9=93=BE=E5=9F=BA=E5=87=86=E4=B8=8E?= =?UTF-8?q?=E6=A0=A1=E9=AA=8C=E8=84=9A=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .mcp.json.example | 12 ++ docs/mcp-tools.md | 90 +++++++++++++++ ...0-p19-mcp-1-l4-readonly-tools-baseline.csv | 10 +- scripts/validate-mcp-tools.py | 107 ++++++++++++++++++ 4 files changed, 214 insertions(+), 5 deletions(-) create mode 100644 .mcp.json.example create mode 100644 docs/mcp-tools.md create mode 100755 scripts/validate-mcp-tools.py diff --git a/.mcp.json.example b/.mcp.json.example new file mode 100644 index 00000000..def3e42c --- /dev/null +++ b/.mcp.json.example @@ -0,0 +1,12 @@ +{ + "mcpServers": { + "postgres": { + "command": "npx", + "args": [ + "-y", + "@modelcontextprotocol/server-postgres", + "postgresql://:@:/" + ] + } + } +} diff --git a/docs/mcp-tools.md b/docs/mcp-tools.md new file mode 100644 index 00000000..51e97525 --- /dev/null +++ b/docs/mcp-tools.md @@ -0,0 +1,90 @@ +# MCP Tools (Project) + +Generated: 2026-03-09 +Current Phase: `P19+MCP-1` +Source: 官方 MCP package 文档 + `P19+MCP-1` 执行合同 + 仓库内校验脚本。当前 shell 无法使用 `codex mcp list --json`,因此本目录不以运行时 CLI 枚举作为唯一事实源。 + +## Enabled Servers +- `postgres` + +## Candidate Servers (Not Enabled) +- `github` — github deferred;官方 `@modelcontextprotocol/server-github` 当前暴露读写混合工具集,不满足 `Read-only Tools Baseline`,因此本期不启用,也不纳入 enabled servers。 + +## Tools +- `postgres:query` — 执行数据库只读排障、schema 核对、账本校验与对账查询;所有查询均应运行在 `READ ONLY` 事务语义下。 + +## Resources +- `postgres:////schema` — schema resources,用于读取表结构、字段类型与列定义,不用于业务写入。 + +## Server: postgres +### 用途 +- 数据库只读排障 +- Prisma / SQL 迁移核对 +- 预算、ERP、资产、采购等账本或主数据只读核验 + +### 输入与能力范围 +- 通过连接串连接 PostgreSQL +- 暴露 `postgres:query` 工具 +- 暴露 schema resources 用于表结构读取 +- 允许执行 `SELECT` / 只读检查类 SQL + +### 允许操作 +- 查询表结构与字段定义 +- 查询计数、汇总、余额、绑定关系、Outbox 状态 +- 为本地回归或审计结论提供只读证据 + +### 禁止操作 +- 禁止 `INSERT / UPDATE / DELETE / DDL` +- 禁止修改预算、工作流、资产、采购、报销等核心业务状态 +- 禁止绕过仓库内已有的本地验证命令 +- 禁止把 MCP 当作应用运行时依赖 + +### 凭据要求 +- 必须使用只读数据库账号 +- 必须使用最小权限连接串 +- **不提交真实密钥/连接串**,包括但不限于数据库密码、主机地址、PAT、API Token + +### 示例配置 +- 仓库示例文件:`.mcp.json.example` +- 仅提供 `postgres` 示例,不提供 `github` 启用段 +- 占位连接串固定写为:`postgresql://:@:/` + +### 回滚方式 +- 删除本机未跟踪的 `.mcp.json` +- 停止在本地 CLI 中启用 `postgres` MCP +- 保留 `docs/mcp-tools.md` 作为事实源与治理文档,不影响业务系统运行 + +## Server: github (Deferred) +### 当前不启用原因 +- 官方 GitHub MCP server 当前为读写混合工具集 +- 现阶段无法以官方能力保证“只读基线” +- 若贸然启用,容易突破 `P19+MCP-1` 的只读边界 + +### 本期固定结论 +- `github` 当前不进入 enabled servers +- `github` 当前不纳入“可立即使用工具目录” +- 本期不提交 GitHub MCP 示例启用配置 + +### 未来启用前置条件 +- 需要新的路线合同显式解锁,如 `P19+MCP-2` 或后续治理 phase +- 需要确认只读模式、最小权限 PAT 策略、审计与回滚边界 +- 需要把读写操作边界从 `P19+MCP-1` 的只读基线中拆分出来单独治理 + +## Local Activation Checklist +1. 确认本机支持 MCP 的 CLI 环境可用;若 `codex mcp list --json` 不可用,仍以本文和 `.mcp.json.example` 为唯一仓库事实源。 +2. 将 `.mcp.json.example` 复制为本机未跟踪的 `.mcp.json`。 +3. 将占位连接串替换为**只读数据库账号**连接串。 +4. 确认不会把 `.mcp.json`、密码或其他真实凭据提交进仓库。 +5. 仅启用 `postgres`,不得自行把 `github` 提升为 enabled server。 +6. 使用 MCP 后,仍必须继续执行仓库内本地验证命令与回归脚本。 + +## Safety Rules +- MCP 仅用于只读排障、数据库验证与审计辅助。 +- MCP 不得写数据库,不得修改业务状态,不得代替测试。 +- `docs/mcp-tools.md` 是 MCP 工具目录事实源;若未来配置与本文冲突,以本文与执行合同为准,先修订合同再变更配置。 +- `github deferred` 在本期为硬边界,不得在实现过程中偷偷启用。 + +## Rollback +- 停止使用本机 `.mcp.json` 中的 `postgres` server。 +- 删除本机凭据或改回占位配置。 +- 保留文档、示例配置与校验脚本,作为后续 phase 的治理基线。 diff --git a/issues/2026-03-09_10-25-00-p19-mcp-1-l4-readonly-tools-baseline.csv b/issues/2026-03-09_10-25-00-p19-mcp-1-l4-readonly-tools-baseline.csv index 1a77ff2e..70db68f1 100644 --- a/issues/2026-03-09_10-25-00-p19-mcp-1-l4-readonly-tools-baseline.csv +++ b/issues/2026-03-09_10-25-00-p19-mcp-1-l4-readonly-tools-baseline.csv @@ -1,6 +1,6 @@ ID,Title,Description,Acceptance,Test_Method,Tools,Dev_Status,Review1_Status,Regression_Status,Files,Dependencies,Notes -P19MCP1-0,P19MCP1执行合同建档,创建并校验MCP只读工具基线执行合同CSV,CSV通过校验且字段完整并冻结只读MCP范围,python3 /home/shunli/.codex/skills/plan/scripts/validate_issues_csv.py issues/2026-03-09_10-25-00-p19-mcp-1-l4-readonly-tools-baseline.csv,manual,TODO,TODO,TODO,issues/2026-03-09_10-25-00-p19-mcp-1-l4-readonly-tools-baseline.csv,[],P19MCP1只做只读MCP目录与使用边界 不引入运行时业务能力 -P19MCP1-1,MCP工具目录建档,新增docs/mcp-tools.md并用目录生成流程固化postgres与github的可用工具清单,docs/mcp-tools.md可稳定重建且仅包含postgres github两类只读MCP工具,manual: 使用mcp-tools-catalog生成docs/mcp-tools.md并人工核对内容,manual,TODO,TODO,TODO,docs/mcp-tools.md | issues/2026-03-09_10-25-00-p19-mcp-1-l4-readonly-tools-baseline.csv,"[""P19MCP1-0""]",若工具目录需脚本辅助 只能新增文档生成或校验脚本 -P19MCP1-2,MCP使用边界与回滚策略,补MCP的用途 权限 禁止事项 审计边界与失败退回策略,MCP边界文档与集成边界文档一致且明确禁止写库改状态绕过本地验证,python3 scripts/validate-feature-matrix.py,manual,TODO,TODO,TODO,docs/integration-boundaries.md | docs/mcp-tools.md | issues/2026-03-09_10-25-00-p19-mcp-1-l4-readonly-tools-baseline.csv,"[""P19MCP1-1""]",首次启用仍限定只读排障和PR协作 不接应用运行时 -P19MCP1-3,MCP文档校验与治理脚本,如有必要新增最小校验脚本以验证mcp工具目录存在且格式可复建,仓库内存在可复用的MCP目录校验流程且不改变业务接口,manual: 运行新增校验脚本或文档生成命令并确认无错误,manual,TODO,TODO,TODO,scripts | docs/mcp-tools.md | issues/2026-03-09_10-25-00-p19-mcp-1-l4-readonly-tools-baseline.csv,"[""P19MCP1-2""]",禁止为MCP引入新的服务端运行时依赖 -P19MCP1-4,P19MCP1文档矩阵与验收收口,完成MCP工具目录 集成边界 Feature Matrix与回归说明的统一收口,文档收口完成且无业务回归并可作为后续AI phase前置条件,python3 /home/shunli/.codex/skills/plan/scripts/validate_issues_csv.py issues/2026-03-09_10-25-00-p19-mcp-1-l4-readonly-tools-baseline.csv && python3 scripts/validate-feature-matrix.py,manual,TODO,TODO,TODO,docs/mcp-tools.md | docs/integration-boundaries.md | docs/feature-matrix.md | scripts/validate-feature-matrix.py | issues/2026-03-09_10-25-00-p19-mcp-1-l4-readonly-tools-baseline.csv,"[""P19MCP1-3""]",P19MCP1完成后才允许按合同讨论真实MCP使用 +P19MCP1-0,P19MCP1执行合同建档,创建并校验MCP只读工具基线执行合同CSV,CSV通过校验且字段完整并冻结示例配置优先与只读MCP范围,python3 /home/shunli/.codex/skills/plan/scripts/validate_issues_csv.py issues/2026-03-09_10-25-00-p19-mcp-1-l4-readonly-tools-baseline.csv,manual,DONE,DONE,DONE,issues/2026-03-09_10-25-00-p19-mcp-1-l4-readonly-tools-baseline.csv,[],P19MCP1只做治理文档 示例配置与校验脚本 不引入运行时业务能力 +P19MCP1-1,MCP工具目录建档,新增docs/mcp-tools.md并固化只读工具目录与候选server边界,docs/mcp-tools.md可稳定重建且enabled servers仅postgres github仅作为deferred candidate记录,python3 scripts/validate-mcp-tools.py,manual,DONE,DONE,DONE,docs/mcp-tools.md | issues/2026-03-09_10-25-00-p19-mcp-1-l4-readonly-tools-baseline.csv,"[""P19MCP1-0""]",当前shell中codex mcp list不可作为唯一事实源 因此以官方文档和仓库校验脚本共同收口 +P19MCP1-2,MCP使用边界与回滚策略,补MCP的用途 禁止事项 凭据边界 GitHub deferred原因与本机回滚策略,MCP边界文档与集成边界文档一致且明确只读边界以postgres为唯一真实启用server,python3 scripts/validate-mcp-tools.py && python3 scripts/validate-feature-matrix.py,manual,DONE,DONE,DONE,docs/integration-boundaries.md | docs/mcp-tools.md | issues/2026-03-09_10-25-00-p19-mcp-1-l4-readonly-tools-baseline.csv,"[""P19MCP1-1""]",GitHub官方server当前为读写混合工具集 本期不启用 +P19MCP1-3,MCP文档校验与治理脚本,新增示例配置与最小校验脚本以验证目录文档和本地启用基线闭环,docs mcp-tools .mcp.json.example validate-mcp-tools.py AGENTS.md形成可重复校验闭环且不改变业务接口,python3 scripts/validate-mcp-tools.py,manual,DONE,DONE,DONE,.mcp.json.example | scripts/validate-mcp-tools.py | AGENTS.md | docs/mcp-tools.md | issues/2026-03-09_10-25-00-p19-mcp-1-l4-readonly-tools-baseline.csv,"[""P19MCP1-2""]",不提交真实.mcp.json 不提交任何PAT token或数据库连接串 +P19MCP1-4,P19MCP1文档矩阵与验收收口,完成MCP工具目录 集成边界 Feature Matrix与事实源校验的统一收口,Feature Matrix更新为P5-P19+IM-1+ERP-3+R10+MCP-1且总数为230并通过全部校验,python3 /home/shunli/.codex/skills/plan/scripts/validate_issues_csv.py issues/2026-03-09_10-25-00-p19-mcp-1-l4-readonly-tools-baseline.csv && python3 scripts/validate-mcp-tools.py && python3 scripts/validate-feature-matrix.py && git diff --check,manual,DONE,DONE,DONE,docs/mcp-tools.md | docs/integration-boundaries.md | docs/feature-matrix.md | scripts/validate-feature-matrix.py | scripts/validate-mcp-tools.py | issues/2026-03-09_10-25-00-p19-mcp-1-l4-readonly-tools-baseline.csv,"[""P19MCP1-3""]",P19MCP1完成后P19+AI-1可将docs/mcp-tools.md视为稳定前置事实源 diff --git a/scripts/validate-mcp-tools.py b/scripts/validate-mcp-tools.py new file mode 100755 index 00000000..5f4039c5 --- /dev/null +++ b/scripts/validate-mcp-tools.py @@ -0,0 +1,107 @@ +#!/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 "" not in flattened: + errors.append("postgres args missing readonly user placeholder") + if "" not in flattened or "" not in flattened or "" not in flattened or "" 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())