OpenProgram Docs

MCP 服务器实现计划#

面向 agent worker: 必须使用子技能 superpowers:executing-plans 逐项实现本计划。每项任务使用 superpowers:test-driven-development,在报告完成前使用 superpowers:verification-before-completion。

目标: 添加经过身份验证、仅支持 stdio 的 openprogram mcp serve 端点,恰好公开六个固定 MCP 工具,并将其映射到现有会话、Runtime 工具、审批、取消、进度与审计机制,不授予 owner 权限。

架构: 新增同级 openprogram/mcp/server/ 包,与现有 MCP 客户端包及 Web owner 控制面分离。入口在打开 stdin 前完成身份验证,创建由凭据派生的客户端身份,固定使用 paired 权限和 non-interactive 交互,并在协议基线 2025-11-25 上运行锁定的 MCP 1.29.0 SDK 服务器。六个面向协议的工具委托给服务层;底层 Runtime 工具的公开集合为默认空配置允许列表、实时注册表与固定 paired 能力表的交集。SDK 取消负责 JSON-RPC 取消错误;OpenProgram 仅停止后续副作用、清理请求/问题状态并生成审计证据。

技术栈: Python 3.11+、mcp==1.29.0、MCP 2025-11-25、AnyIO、Pydantic、jsonschema、pytest、stdio JSON-RPC,以及现有 SessionDB/dispatcher/Runtime/事件总线。

全局约束#

  • 从 origin/main@9fb70113fd31dc2acf80a341e5ac51d244e1c35a 创建隔离 worktree 实现。不得修改有未提交更改的 main checkout。
  • 服务器唯一传输方式为 openprogram mcp serve 的 stdio。不得新增 HTTP、SSE、Streamable HTTP、OAuth、Web 路由、socket 或部署监听器。
  • 行为锁定为仓库的 mcp==1.29.0 和协议版本 2025-11-25;不得增加自定义握手字段或扩大依赖版本范围。
  • 恰好公开以下 MCP 工具名:sessions_list、session_get、prompt_send、prompt_cancel、tools_list、tool_call。
  • 使用独立且由凭据派生的 MCP 客户端身份。initialize.clientInfo 仅为诊断元数据,不得影响身份验证、身份、作用域、允许列表或审批。
  • 每个请求使用 source="mcp"、permission_mode="ask"、authority_tier="paired" 和 interaction="non-interactive";调用方均不可配置。
  • mcp_server.exposed_tools 默认为 []。底层 Runtime 工具的发现与执行均使用 configured allowlist ∩ live registry ∩ paired capability。
  • 读取 POSIX 权限为 0600 的 <state>/mcp_server_token,并在进入 SDK stdio 上下文前用 hmac.compare_digest() 与 OPENPROGRAM_MCP_TOKEN 比较。serve 永不生成 token。
  • 不得导入或复用 openprogram.webui.owner_auth、backend_endpoint、Web owner token、cookie 或 owner bearer header。
  • 未知和未公开的底层工具名不可区分,均返回方法级 MCP 错误。已配置工具缺少 paired 作用域、硬约束/审批拒绝或工具执行失败时返回 CallToolResult(isError=True)。
  • 被取消的原始 prompt_send 永不返回应用工具结果。MCP SDK 1.29.0 返回标准 JSON-RPC 取消错误;服务停止后续副作用,清理活跃请求与问题,并记录 mcp.request.cancelled。
  • prompt_cancel 是独立普通工具。它只能取消由同一已验证服务器连接发起的当前活跃轮次,并返回自己的正常执行结果。
  • 普通问题与审批不得等待非交互式 MCP 请求。审批门禁立即拒绝;活跃 MCP 会话的其他 question.asked 事件立即解析为 declined。
  • 不得修改或扩展现有 ACP 服务器。共享 Runtime 类型发生变化时运行 ACP 回归。
  • 每项任务严格遵循 RED → 验证失败 → GREEN → 验证通过 → 提交。不得合并任务提交。

固定协议约定#

全部六个 schema 设置 additionalProperties: false。空输入工具使用 { "type": "object", "properties": {} }。

MCP 工具 输入 schema 成功 payload
sessions_list 无字段 最多 100 条最近的 {id,title,updated_at},按 SessionDB 顺序排列
session_get 必需的非空字符串 session_id 活跃分支消息 {id,role,content,timestamp}
prompt_send 必需的非空字符串 prompt;可选非空字符串 session_id {session_id,text,assistant_msg_id,failed};缺少 session_id 时创建 mcp_<uuid> 会话,提供未知 id 则无效
prompt_cancel 必需的非空字符串 session_id {session_id,cancelled};仅同一连接的活跃 MCP 轮次使 cancelled 为 true
tools_list 无字段 允许列表/注册表/paired 交集中的底层工具,格式为 {name,description,inputSchema}
tool_call 必需的非空字符串 name;可选对象 arguments,默认为 {} 转换后的底层 AgentToolResult 内容与错误状态

成功的结构化应用 payload 序列化为一个 UTF-8 JSON TextContent;v1 不增加第二种输出表示。tool_call 保留 Runtime 文本/图像内容:文本映射为 MCP 文本内容,图像映射为含媒体类型的 MCP 图像内容。不支持的内容以执行错误拒绝,不转换为字符串。

MCP tools/list 本身始终返回六个固定包装工具。名为 tools_list 的包装工具返回筛选后的底层 Runtime 工具。这是两个不同约定,必须分别断言。


任务 1:固定六个 JSON Schema 与协议验证#

文件:

  • 创建:openprogram/mcp/server/__init__.py
  • 创建:openprogram/mcp/server/contracts.py
  • 创建:tests/unit/test_mcp_server_contracts.py

接口:

MCP_TOOL_SCHEMAS: tuple[mcp.types.Tool, ...]
TOOL_BY_NAME: Mapping[str, mcp.types.Tool]
def validate_tool_call(name: str, arguments: Mapping[str, Any] | None) -> dict[str, Any]

对于六个固定名称之外的包装工具名,validate_tool_call() 必须抛出 mcp.shared.exceptions.McpError(ErrorData(code=METHOD_NOT_FOUND, ...));schema 验证失败时抛出 McpError(... INVALID_PARAMS ...)。它返回复制的 dict,永不修改调用方输入。

RED: 为六个名称的顺序、每个完整输入 schema、additionalProperties: false、必需字段、空/空白 prompt 和 id、非对象 arguments、未知包装工具名和输入不可变性添加严格相等测试。还要断言锁定依赖解析为 mcp==1.29.0,且 LATEST_PROTOCOL_VERSION == "2025-11-25"。

运行:

uv run --locked pytest -q tests/unit/test_mcp_server_contracts.py

预期:FAIL,因为 openprogram.mcp.server.contracts 不存在。

GREEN: 将 schema 定义为不可变模块数据,从中构造 SDK Tool 对象,并使用 jsonschema.Draft202012Validator 验证。仅将省略/None 的 tool_call.arguments 归一化为 {};拒绝所有其他未声明或类型错误的字段。不得添加服务器、传输、身份验证或执行逻辑。

运行相同命令;预期:PASS。

提交:

git add openprogram/mcp/server/__init__.py openprogram/mcp/server/contracts.py tests/unit/test_mcp_server_contracts.py
git commit -m "feat: define MCP server tool contracts"

任务 2:让 Runtime 工具错误在全流程中类型化#

文件:

  • 修改:openprogram/agent/types.py
  • 修改:openprogram/programs/_runtime.py
  • 修改:openprogram/mcp/adapter.py
  • 修改:openprogram/agent/agent_loop.py
  • 修改:openprogram/agent/permissions/approval.py
  • 修改:openprogram/agentic_programming/call_state.py
  • 修改:openprogram/programs/tools/files/bash/bash.py
  • 修改:tests/unit/test_tools_runtime.py
  • 修改:tests/agent/test_loop_options.py
  • 修改:tests/agent/test_tool_gate.py
  • 修改:tests/unit/test_acp_server.py

接口:

class AgentToolResult(BaseModel):
    content: list[TextContent | ImageContent]
    details: Any = None
    is_error: bool = False

RED: 修改测试,对归一化的 ToolReturn 失败、超时、异常、远程 MCP isError、审批拒绝和 bash 失败断言 result.is_error。添加 agent-loop 断言,要求 ToolResultMessage.is_error 等于 AgentToolResult.is_error。断言 details 保留 denied、timeout、reason_code 等诊断字段,但不再传递权威 is_error 标志。

运行:

uv run --locked pytest -q tests/unit/test_tools_runtime.py tests/agent/test_loop_options.py tests/agent/test_tool_gate.py tests/unit/test_acp_server.py

预期:FAIL,因为 AgentToolResult 没有独立错误字段,使用方仍检查 details。

GREEN: 添加带默认值的字段;在 _normalize_result()、MCP 客户端转换、审批 _denied()、超时/异常路径和显式失败工具中设置它。将缓存及 agent-loop 转换中的 details.get("is_error") 替换为 result.is_error。保留默认 false,使无关构造器保持源码兼容。不得改变 ACP 协议结构。

运行相同命令;预期:PASS。

提交:

git add openprogram/agent/types.py openprogram/programs/_runtime.py openprogram/mcp/adapter.py openprogram/agent/agent_loop.py openprogram/agent/permissions/approval.py openprogram/agentic_programming/call_state.py openprogram/programs/tools/files/bash/bash.py tests/unit/test_tools_runtime.py tests/agent/test_loop_options.py tests/agent/test_tool_gate.py tests/unit/test_acp_server.py
git commit -m "refactor: type runtime tool error results"

任务 3:添加默认空公开集合、paired MCP 身份与非交互门禁#

文件:

  • 修改:openprogram/config_schema.py
  • 修改:openprogram/agent/authority.py
  • 修改:openprogram/agent/permissions/approval.py
  • 修改:tests/unit/test_config_schema.py
  • 修改:tests/unit/test_authority_scope.py
  • 修改:tests/unit/test_permission_rules.py
  • 修改:tests/unit/test_spawn_hard_constraints.py
  • 创建:tests/unit/test_mcp_server_security.py

接口:

def mcp_client_authority(client_id: str) -> dict[str, Any]

SettingSpec(
    key="mcp_server.exposed_tools",
    path=("mcp_server", "exposed_tools"),
    default=[],
    apply=APPLY_NEXT_START,
    ...,
)

权限映射固定为 speaker_kind="client"、speaker_id=f"mcp/{client_id}"、speaker_display="MCP client"、principal_id=owner_principal_id()、authority_tier="paired"、interaction="non-interactive"。拒绝空或格式错误的客户端 id,不合成身份。

RED: 断言缺失设置读取为 [];验证拒绝非列表及非字符串条目;归一化 MCP 权限稳定且仅拥有 paired 能力。添加 source="mcp" 用例,证明 _NON_INTERACTIVE_SOURCES 立即拒绝审批,且 _hard_constraint_violation() 在规则、允许列表或 bypass 授权之前拒绝 _RISKY_TOOLS、worktree 工具和工作目录之外的 write/patch 路径。断言未注册任何问题。

运行:

uv run --locked pytest -q tests/unit/test_config_schema.py tests/unit/test_authority_scope.py tests/unit/test_permission_rules.py tests/unit/test_spawn_hard_constraints.py tests/unit/test_mcp_server_security.py

预期:FAIL,因为设置、权限构造器和 MCP source 门禁均缺失。

GREEN: 添加 JSON 列表设置验证器、mcp_client_authority()、将 "mcp" 加入 _NON_INTERACTIVE_SOURCES,并在 _hard_constraint_violation() 中显式处理 MCP。硬约束保持在权限规则及所有 bypass 之前。不得扩展 _PAIRED_CAPABILITIES。

运行相同命令;预期:PASS。

提交:

git add openprogram/config_schema.py openprogram/agent/authority.py openprogram/agent/permissions/approval.py tests/unit/test_config_schema.py tests/unit/test_authority_scope.py tests/unit/test_permission_rules.py tests/unit/test_spawn_hard_constraints.py tests/unit/test_mcp_server_security.py
git commit -m "feat: add MCP server security boundary"

任务 4:实现独立的 0600 token 生命周期#

文件:

  • 创建:openprogram/mcp/server/auth.py
  • 修改:openprogram/_cli_cmds/mcp.py
  • 修改:openprogram/cli.py
  • 创建:tests/unit/test_mcp_server_auth.py
  • 创建:tests/unit/test_mcp_server_cli.py

接口:

MCP_TOKEN_ENV = "OPENPROGRAM_MCP_TOKEN"
def token_path() -> Path
def create_token(path: Path | None = None) -> str
def authenticate_from_environment(
    environ: Mapping[str, str] = os.environ,
    path: Path | None = None,
) -> str  # returns credential fingerprint/client id
def _cmd_mcp_token_create() -> int

指纹为 sha256(stored_token.encode()).hexdigest()[:16];它是稳定客户端 id,绝不包含 token 文本。create_token() 使用 secrets.token_urlsafe(32),拒绝已存在目标,以 0600 写入独占临时文件,执行 flush 和 fsync(),然后通过不覆盖操作安装(POSIX 使用 os.link(temp, target) 后删除临时链接;其他平台使用经过同等测试的禁止替换原语)。它重新应用仅用户可访问权限,验证权限,并返回 token 供 CLI 仅打印一次。不得在预检查后使用 os.replace(),因为它可能覆盖并发进程创建的 token。

RED: 覆盖创建、长度/熵形式、已有文件不覆盖并拒绝、父目录创建、POSIX 0600、不可读/权限错误 token 拒绝、缺失环境变量、不匹配、使用 hmac.compare_digest、稳定指纹、异常/日志无 token,以及 mcp token create 解析器/退出行为。对 _require_backend_endpoint 打补丁,使其被调用即失败,以证明 token 创建在本地完成。

运行:

uv run --locked pytest -q tests/unit/test_mcp_server_auth.py tests/unit/test_mcp_server_cli.py

预期:FAIL,因为 token 身份验证和嵌套 mcp token create 命令不存在。

GREEN: 在 mcp/server/auth.py 实现独立写入器;不得导入 Web 身份验证辅助函数。为现有 mcp 解析器扩展 token create,保留所有管理命令。在任何后端 HTTP 辅助函数之前直接分发 token 创建。

运行相同命令;预期:PASS。

提交:

git add openprogram/mcp/server/auth.py openprogram/_cli_cmds/mcp.py openprogram/cli.py tests/unit/test_mcp_server_auth.py tests/unit/test_mcp_server_cli.py
git commit -m "feat: add MCP server token lifecycle"

任务 5:实现会话读取和筛选后的 Runtime 发现#

文件:

  • 创建:openprogram/mcp/server/service.py
  • 创建:openprogram/mcp/server/tools.py
  • 创建:tests/unit/test_mcp_server_tools.py

接口:

@dataclass(frozen=True)
class MCPClientContext:
    client_id: str
    authority: Mapping[str, Any]

class MCPService:
    def sessions_list(self) -> AgentToolResult: ...
    def session_get(self, session_id: str) -> AgentToolResult: ...
    def tools_list(self) -> AgentToolResult: ...
    def exposed_runtime_tools(self) -> tuple[AgentTool, ...]: ...

def json_result(payload: Any, *, is_error: bool = False) -> AgentToolResult: ...

RED: 使用伪造 SessionDB 和注册表,断言 sessions_list 调用 list_sessions(limit=100) 且仅输出 id/title/updated_at;session_get 拒绝未知 id,通过 get_branch(session_id) 读取当前活跃分支,且仅输出 id/role/content/timestamp。断言 tools_list 默认为空,忽略已配置但未注册名称,排除缺少 paired 能力的已注册名称,去重后保留配置顺序,并返回精确的 name/description/inputSchema 键。断言它从不读取 clientInfo 或 Web 身份验证。

运行:

uv run --locked pytest -q tests/unit/test_mcp_server_tools.py -k "sessions or session_get or tools_list"

预期:FAIL,因为服务与转换模块不存在。

GREEN: 向 MCPService 注入 SessionDB/config/registry 访问器,以实现确定性测试。使用 functions._runtime.get()/注册表公开机制和 decide_tool_authority() 求交集。返回确定性 JSON 文本。本任务不实现 tool_call、prompt 执行、传输或 owner 回退。

运行相同命令;预期:PASS。

提交:

git add openprogram/mcp/server/service.py openprogram/mcp/server/tools.py tests/unit/test_mcp_server_tools.py
git commit -m "feat: add MCP session and tool discovery"

任务 6:实现失败时拒绝的 tool_call 与进度映射#

文件:

  • 修改:openprogram/mcp/server/service.py
  • 修改:openprogram/mcp/server/tools.py
  • 修改:tests/unit/test_mcp_server_tools.py

接口:

async def MCPService.tool_call(
    self,
    name: str,
    arguments: Mapping[str, Any],
    *,
    call_id: str,
    cancel_event: asyncio.Event,
    on_progress: Callable[[str], None] | None,
) -> AgentToolResult: ...

def to_mcp_content(result: AgentToolResult) -> list[mcp.types.ContentBlock]: ...

RED: 添加表驱动测试:未知名称与已知但未公开名称返回相同 METHOD_NOT_FOUND 错误;已公开名称缺少 paired 能力时返回 is_error=True 并说明缺失能力;硬约束、策略和不可用审批的拒绝返回类型化错误;无效底层参数返回 INVALID_PARAMS;成功文本/图像转换;底层异常/失败转换;每种拒绝均不执行调用。验证执行使用固定 source="mcp"、permission_mode="ask"、paired 权限且无请求可控覆盖的 TurnRequest。验证更新回调保序,缺少 on_progress 时忽略。

运行:

uv run --locked pytest -q tests/unit/test_mcp_server_tools.py -k tool_call

预期:FAIL,因为 tool_call() 缺失。

GREEN: 先解析公开范围且不泄露注册表成员关系,验证所选 Runtime 工具 schema,检查 decide_tool_authority(),通过 wrap_with_approval() 包装,再调用 AgentTool.execute(call_id, arguments, cancel_event, on_update)。仅转换支持的内容。保留 AgentToolResult.is_error;不得从自由文本推断失败。

进度约定:服务仅报告有序更新消息。任务 8 的传输层将其转换为单调递增的 MCP 进度值,且仅在请求元数据包含 progressToken 时发送。

运行相同命令;预期:PASS。

提交:

git add openprogram/mcp/server/service.py openprogram/mcp/server/tools.py tests/unit/test_mcp_server_tools.py
git commit -m "feat: add scoped MCP runtime tool calls"

任务 7:实现 prompt 归属、非交互问题和取消清理#

文件:

  • 修改:openprogram/mcp/server/service.py
  • 修改:openprogram/mcp/server/tools.py
  • 创建:tests/unit/test_mcp_server_turns.py

接口:

@dataclass
class ActiveMCPRequest:
    request_id: str
    session_id: str
    client_id: str
    thread_cancel: threading.Event
    tool_cancel: asyncio.Event

async def MCPService.prompt_send(..., request_id: str, ...) -> AgentToolResult: ...
def MCPService.prompt_cancel(session_id: str) -> AgentToolResult: ...
def MCPService.cancel_request(request_id: str, *, reason: str) -> None: ...
def MCPService.close() -> None: ...

RED: 断言省略 session id 时使用 agent main 和 source mcp 创建 mcp_<uuid>;提供已有会话时保留存储的 agent id;提供未知 id 返回 INVALID_PARAMS;process_user_turn() 接收固定安全字段和已注册线程事件。断言并发请求拥有请求级记录。prompt_cancel 仅对同一客户端、同一连接的活跃轮次成功;未知、已完成或外部轮次均拒绝,且不设置其事件。

订阅测试事件总线,为活跃及无关会话发出 question.asked。活跃 MCP 问题必须立即解析为 declined;无关问题不得受影响。取消时断言 thread_cancel.set()、tool_cancel.set()、mark_cancelled(session_id)、kill_active_subprocess(session_id)、kill_active_runtime(session_id)、QuestionRegistry.cancel_session(session_id)、unregister_cancel_event(session_id, exact_event)、移除活跃映射,以及包含 request/session/client 指纹但不含 prompt/token 的 mcp.request.cancelled 审计。重复取消和清理以证明幂等性。

运行:

uv run --locked pytest -q tests/unit/test_mcp_server_turns.py

预期:FAIL,因为 prompt 生命周期及活跃请求归属缺失。

GREEN: 使用 anyio.to_thread.run_sync(..., abandon_on_cancel=True) 运行同步 process_user_turn(),并始终在 finally 中注销精确的取消事件。问题总线订阅限定于服务生命周期,在 close() 中取消订阅。在请求边界捕获取消,调用 cancel_request() 后重新抛出,让 SDK 1.29.0 生成标准 JSON-RPC 取消错误。该取消路径绝不构造 AgentToolResult。保护完成操作,防止被放弃的 worker 后续发布结果或修改活跃请求记录。

运行相同命令;预期:PASS。

提交:

git add openprogram/mcp/server/service.py openprogram/mcp/server/tools.py tests/unit/test_mcp_server_turns.py
git commit -m "feat: add MCP prompt lifecycle and cancellation"

任务 8:连接 MCP 1.29.0 stdio 并添加端到端协议测试#

文件:

  • 创建:openprogram/mcp/server/server.py
  • 修改:openprogram/mcp/server/__init__.py
  • 修改:openprogram/_cli_cmds/mcp.py
  • 修改:openprogram/cli.py
  • 修改:tests/unit/test_mcp_server_cli.py
  • 创建:tests/integration/test_mcp_server.py

接口:

def build_server(context: MCPClientContext) -> mcp.server.Server: ...
async def serve_stdio(context: MCPClientContext) -> None: ...
def serve() -> int: ...
def _cmd_mcp_serve() -> int: ...

serve() 在进入 mcp.server.stdio.stdio_server() 前调用 authenticate_from_environment() 并构造 MCPClientContext。身份验证错误向 stderr 打印脱敏文本,返回非零值,保持 stdout 为空,且从不读取 stdin。

使用 Server.run(read, write, server.create_initialization_options())。在 server.request_handlers 中显式注册 CallToolRequest 处理器,不能仅依赖将全部异常转换为 isError 的装饰器;包装工具名和无效参数错误必须保留为 MCP 错误。从 server.request_context 取得 request id、progressToken 和 session。仅为脱敏诊断读取 client_params.clientInfo。

RED: 先扩展 CLI 单元测试:mcp serve 在本地分发且永不调用 _require_backend_endpoint;身份验证发生在 stdio 上下文之前。再添加 SDK 客户端子进程测试,覆盖:

  1. 缺失 token 文件、错误权限、缺失环境变量和环境变量不匹配均在 initialize 前退出,状态非零、stdout 为空、stderr 脱敏;
  2. 有效 token 完成 2025-11-25 initialize,更改 clientInfo.name/version 不影响身份、工具列表或结果;
  3. MCP tools/list 恰好返回有序的六个固定工具,tools_list 包装工具仅返回允许列表/注册表/paired 交集;
  4. 六种映射均返回固定结构;未知包装工具名和无效参数为 MCP 错误;
  5. 未知/未公开底层名称不可区分,作用域/硬约束/审批失败为 isError,且被拒绝工具均不执行;
  6. 审批或普通问题永不阻塞 MCP 调用;
  7. 当且仅当请求提供 progressToken 时,进度通知按单调顺序到达;
  8. 取消进行中的 prompt_send 为原请求产生 SDK 1.29.0 标准 JSON-RPC 取消错误,绝不产生工具结果/isError;后续副作用停止,活跃请求/问题状态清除,且存在审计事件;
  9. prompt_cancel 保持普通工具调用,不能取消外部/已完成轮次;
  10. stdout 仅包含 JSON-RPC;诊断和审计文本绝不写入其中。

运行:

uv run --locked pytest -q tests/unit/test_mcp_server_cli.py tests/integration/test_mcp_server.py

预期:FAIL,因为 stdio 服务及 mcp serve 缺失。

GREEN: 仅实现 stdio 服务器和本地 CLI 分发。通过内存对象流或 loop.call_soon_threadsafe 将 worker 线程回调的进度转交 AnyIO 循环;仅在存在 token 时调用 ServerSession.send_progress_notification()。请求处理器捕获普通执行失败并按固定矩阵转换,但重新抛出取消。确保关闭时执行 MCPService.close()。

运行相同命令;预期:PASS。

同时运行共享回归:

uv run --locked pytest -q tests/unit/test_authority_scope.py tests/unit/test_permission_rules.py tests/unit/test_spawn_hard_constraints.py tests/agent/test_questions.py tests/unit/test_acp_server.py tests/unit/test_tools_runtime.py

预期:PASS。

提交:

git add openprogram/mcp/server/server.py openprogram/mcp/server/__init__.py openprogram/_cli_cmds/mcp.py openprogram/cli.py tests/unit/test_mcp_server_cli.py tests/integration/test_mcp_server.py
git commit -m "feat: serve authenticated MCP over stdio"

附录:当前实现状态#

本计划描述的 MCP 服务器已存在于当前源码。 实现位于 openprogram/mcp/server/(auth.py、contracts.py、 server.py、service.py 和 tools.py),CLI 分发位于 openprogram/_cli_cmds/mcp.py 和 openprogram/cli.py。当前服务器 在进入 stdio 前完成身份验证,公开固定包装工具,应用 paired 非交互权限,并负责取消清理。锁定的 依赖和协议基线仍为 mcp==1.29.0 与 MCP 2025-11-25。

下面的计划记录历史实现顺序,保留它是为了 说明约定和审查顺序,不是未实现工作列表。 当前完成证据及剩余的已安装/运行时验收 边界由权威 MCP 服务器设计页及其测试维护。本次 审计未运行测试或文档门禁,因此不声称新增的 通过数量或发布验收。

历史任务 9:记录实现证据并运行完整发布门禁#

文件:

  • 修改:docs/reference/design/integrations/mcp-server.html
  • 修改:docs/reference/design/plans/mcp-server-implementation.md,仅在执行路径或命令明显不同时修改
  • 更新:.superpowers/sdd/2026-08-12-mcp-server/ledger.md (已 gitignore;不得提交)

RED: 编辑状态前运行完整门禁,在台账中记录精确命令/输出/提交证据:

uv run --locked pytest -q tests/unit/test_mcp_server_contracts.py tests/unit/test_mcp_server_auth.py tests/unit/test_mcp_server_cli.py tests/unit/test_mcp_server_security.py tests/unit/test_mcp_server_tools.py tests/unit/test_mcp_server_turns.py tests/integration/test_mcp_server.py
uv run --locked pytest -q tests/unit/test_authority_scope.py tests/unit/test_permission_rules.py tests/unit/test_spawn_hard_constraints.py tests/agent/test_questions.py tests/unit/test_acp_server.py tests/unit/test_tools_runtime.py
uv run --locked --with mdit-py-plugins python -m scripts.docs_site.build
uv run --locked --with mdit-py-plugins python -m scripts.docs_site.checklinks
git diff --check

预期:全部测试和文档命令通过。若任一命令失败,保持 HTML 实现状态不变,返回对应任务;如果缺少覆盖,则新增 RED 测试并在该任务修复。

GREEN: 仅在完整门禁通过后,使用实际文件、测试、SDK/协议版本和观测数量更新 HTML 实现状态表与证据单元格。保留文档顺序:当前状态 → 竞品设计 → 后续计划 → 实现证据。不得声称 HTTP/OAuth/Web 路由、ACP 修改、SDK 扩展或门禁未证实的结果。取消措辞保持明确:SDK JSON-RPC 取消错误、无应用工具结果、OpenProgram 仅清理/审计。

文档编辑后重新运行上述五个命令。预期:PASS 且 diff 检查无问题。

提交:

git add docs/reference/design/integrations/mcp-server.html docs/reference/design/plans/mcp-server-implementation.md
git commit -m "docs: record MCP server implementation evidence"

审查检查点#

  • 继续实现前审查任务 1:schema 是公开 v1 接口,后续任务不得附带修改。
  • 合并审查任务 3–4 的安全性:不复用 owner 凭据,不以 clientInfo 授权,空列表不回退为全部工具,请求不能控制权限字段。
  • 合并审查任务 6–7 的失败拒绝行为:每次拒绝发生在调用前,问题不等待,取消后不留下后续结果或副作用。
  • 使用 SDK 客户端审查任务 8,不使用手写 JSON-RPC 帧。SDK 实测取消行为属于验收基线。
  • 原任务 9 是记录 Layer 3 证据的历史步骤;当前实现状态由权威 MCP 服务器设计页维护。
Last updated · 2026-10-10