docs(P19MCP1): 落地MCP只读工具链基准与校验脚本
这个提交包含在:
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"mcpServers": {
|
||||
"postgres": {
|
||||
"command": "npx",
|
||||
"args": [
|
||||
"-y",
|
||||
"@modelcontextprotocol/server-postgres",
|
||||
"postgresql://<READONLY_USER>:<PASSWORD>@<HOST>:<PORT>/<DB>"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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://<host>/<table>/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://<READONLY_USER>:<PASSWORD>@<HOST>:<PORT>/<DB>`
|
||||
|
||||
### 回滚方式
|
||||
- 删除本机未跟踪的 `.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 的治理基线。
|
||||
@@ -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视为稳定前置事实源
|
||||
|
||||
|
+107
@@ -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 "<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())
|
||||
在新工单中引用
屏蔽一个用户