Repository navigation
fix(connectors): 内部 MCP 的 GET/DELETE 也要拒绝已停用的连接器实例 - #1915
Open
lihongyuan99 wants to merge 1 commit into
Open
lihongyuan99 wants to merge 1 commit into
lihongyuan99 wants to merge 1 commit into
Conversation
lihongyuan99
force-pushed
the
fix/internal-mcp-active-only
branch
2 times, most recently
from
October 15, 2026 00:05
c873954 to
5cd85f8
Compare
lihongyuan99
force-pushed
the
fix/internal-mcp-active-only
branch
from
October 16, 2026 00:06
5cd85f8 to
6f1061d
Compare
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
src/octop/api/routers/internal_mcp.py有三个内部 MCP 端点,同一个「实例是否存在 / kind 是否匹配 / 是否处于 active」的判定在 POST 里写全了,但 GET 和 DELETE 漏了status这一项::59):if inst is None or inst.kind != kind or inst.status != "active":109,修复前):if inst is None or inst.kind != kind:145,修复前):if inst is None or inst.kind != kind于是把连接器实例停用之后(
PATCH /connector-instances/{id}传status="disabled",落到repo.update_status),它已经不再被挂载进 agent 的工具集(infra/connectors/builder.py:520,616,669都以inst.status != "active"为准),verify_internal_token也不区分状态(infra/connectors/service.py:633只比对存下来的internal_token),但持有该实例 internal token 的客户端仍然可以:text/event-stream,一路挂着 keepalive);只有 POST 会正确地返回
CONNECTOR_NOT_FOUND。改动就是把 GET/DELETE 的判定补齐成和 POST 一致,停用实例在这三个入口上的行为统一为CONNECTOR_NOT_FOUND。Target branch
develop(feature / fix — default)main(release/*orhotfix/*only)Type of change
影响面
「停用」是连接器实例在 UI 上就能做的操作,不是删除:配置和
internal_token都还在库里,token 校验照样通过。停用后 POST 建不了新会话,但停用之前已经拿到 session id 的客户端可以用 GET 把 SSE 通道重新连起来,长挂一条到asyncio.sleep(3600)的 keepalive 连接,DELETE 也仍然受理——也就是「停用之后这个连接器彻底不服务」这个预期在这条路径上是不成立的。修复只加两个短路条件,不改任何合法路径的行为。internal_mcp.py在 develop 上的测试覆盖只有一个 POST 用例(tests/integration/test_connectors_api.py:762-803:错 token → 401,active 实例tools/list→ 200)加一个纯客户端的 URL 拼接用例(tests/unit/connectors/test_internal_mcp_url.py),GET / DELETE 两个入口和「实例已停用」这个状态在测试里一次都没出现过——这也正是漏掉status判定能长期存着的原因。新增tests/unit/api/test_internal_mcp_active_only.py把三个入口的停用分支、active 分支的 GET(StreamingResponse/ 无 session header 时 405)和 DELETE(204)都固定下来。与 #1845 的关系(同文件、不同函数)
#1845(cy1990,2026-10-09 开,base 也是
develop)改的是同一个文件里的_service(),只加了一行user_repo=server.services.repos.user_repo,;本 PR 的两个 hunk 在internal_mcp_get(:109)和internal_mcp_delete(:145),与它的 hunk 区间不重叠,两侧都能独立合并、互不冲突。两个 PR 修的也不是同一件事:#1845 是给_service()多传一个依赖,本 PR 是补status判定。这里主动声明是同文件撞车披露,不是重复提交。撞车核对范围:370 个 open PR 里 369 个的改动文件清单逐个拉过(剩下一个 #1205 是我自己正在 rebase 的分支,改动文件来自本地 diff),
src/octop/api/routers/internal_mcp.py只有 #1845 碰;365 条 open issue 的标题全扫了一遍,与 MCP 内部端点 / 连接器停用沾边的只有 #1854(MCP Apps 渲染)、#1827(streamable_http 收到 401 应走 OAuth)和 #1614(第二次调用某连接器报 stream_error),都跟「实例停用后内部端点是否还受理」无关,所以这条没有对应的 issue,是读代码发现的。Test plan
新增 8 条用例(
tests/unit/api/test_internal_mcp_active_only.py),直接调用 router 函数 +SimpleNamespace桩 server,沿用tests/unit/api/test_harness_tabs.py的写法:OctopError(CONNECTOR_NOT_FOUND);AUTH_FAILED(说明新增的短路没有把鉴权分支吃掉);Mcp-Session-Id:GET 返回StreamingResponse;无 session header:GET 返回 405JSONResponse;CONNECTOR_NOT_FOUND。改动前(develop@
97d6e7ff4,只把测试文件放进工作树)是红的,红的正好是 disabled 的 GET 和 DELETE 两条:改动后同一文件
8 passed in 2.45s。make all没有跑(会构建 dashboard 前端,本地无node_modules);跑的是与后端门禁等价的四条命令(直接用uv sync出来的 venv,避免uv run改写uv.lock):(
-n 2而不是-n auto:同一台机器上同时在跑本分支 rebase 的门禁,为了不打断它们只用了 2 个 worker,用例数与结果不受影响。)同一 develop sha(
97d6e7ff4)的基线全量结果是4627 passed, 26 skipped, 0 failed in 194.09s,本 PR 的差集正好是新增的 8 条用例(4627 + 8 = 4635)。make allpasses locally(见上,未跑;已跑等价的 ruff / mypy / pytest)Checklist
CHANGELOG.md(if user-facing)Rebased onto
develop@87a8d52c2(2026-10-14);head9b16a31e6->c8739541b.CHANGELOG.mdis again the only conflict.develop's file is kept verbatim and this PR's ownbullet is re-inserted into the current
## [Unreleased]list, which is now flat (no###subsections), so the diff against the new base is
1 added / 0 deletedthere and the2 code/test file(s) are byte-identical before and after (hunk headers aside) — checked
mechanically, not by eye. Gates re-run by hand on the new head (
make allstill does not apply:no
dashboard/node_modulesin this clone; the venv belongs to this worktree, so/Users/macos/Documents/Qoder/octop-pr-mining/repos/wt-new-1012/src/octop/__init__.pyis this branch's own code):RC ruff=0 format=0 mypy=0 targeted=0 full=08 passed in 7.33s4687 passed, 26 skipped, 41 warnings in 232.56s (0:03:52), againstdevelop@87a8d52c2measured in the same environment today: 4679 passed, 26 skipped, 0 failedAll checks passed!;1186 files already formatted;Success: no issues found in 571 source filesRebased onto
develop@99e479a6b(2026-10-16); head5cd85f862->6f1061deb.CHANGELOG.mdwas again the only conflict:develop's file is kept verbatim and this PR's own bullet is re-inserted under its matching###subsection of the current## [Unreleased]block, so the diff against the new base is1 added / 0 deletedthere and the2code/test file(s) are byte-identical before and after the rebase (checked mechanically on the patch hash, not by eye). Gates re-run by hand on the new head (make allstill does not apply — nodashboard/node_modulesin this clone; the venv belongs to this worktree, so the checkout under test is this branch's own code):RC ruff=0 format=0 mypy=0 targeted=0 full=08 passed in 9.06s4788 passed, 32 skipped, 41 warnings in 487.34s (0:08:07), againstdevelop@99e479a6bmeasured today in its own per-worktree venv on the same machine: 4780 passed, 32 skipped, 0 failed in 495.68sAll checks passed!;1204 files already formatted;Success: no issues found in 581 source files