Skip to content

fix(sse): recognize MCP-gateway-namespaced CCR retrieve tool names (#13781, #13897) - #14028

Merged
diegosouzapw merged 1 commit into
release/v3.8.51from
fix/13781-ccr-tool-prefix
Sep 18, 2026
Merged

diegosouzapw merged 1 commit into
release/v3.8.51from
fix/13781-ccr-tool-prefix

Conversation

@diegosouzapw

Copy link
Copy Markdown
Collaborator

Root cause

callerSupportsCcrRetrieve() (open-sse/services/compression/engines/ccr/protocolInstruction.ts)
matched the CCR retrieve tool by exact string equality against omniroute_ccr_retrieve. MCP
aggregators/gateways (Docker MCP Toolkit, Claude Code, and similar) rename tools when re-exposing
them to the model to avoid cross-server name collisions — typically as
mcp__<gateway>__<server>__omniroute_ccr_retrieve, or with a dotted/slashed namespace prefix
(omniroute.omniroute_ccr_retrieve). Because the match was exact, a caller that is fully
MCP-capable — it CAN and WILL call the tool, just under a gateway-assigned namespaced name — was
treated as if it had no access at all. Per the #7746 safety guarantee this check gates
apply() itself, not just the injected instruction, so the entire CCR compression engine was
silently skipped for such callers.

Fixes both #13781 and #13897, which report the same root cause from two angles (tool-matching
too strict for MCP environments / protocol instruction never injected when the MCP gateway
namespaces tools).

Fix

Added matchesCcrRetrieveToolName(): true on an exact match, or when the name ends with one of
the separators __, ., /, : immediately followed by omniroute_ccr_retrieve — a
separator-bounded trailing-segment match, never a bare substring/.endsWith() test, so a
near-miss like omniroute_ccr_retrieve_v2 still correctly does not match. Wired into both the
flat and nested ({type:"function",function:{name}}) branches of callerSupportsCcrRetrieve().

Regression test — RED → GREEN

tests/unit/ccr-protocol-instruction.test.ts

RED (confirmed against origin/release/v3.8.51, unfixed protocolInstruction.ts swapped back in
via git checkout -- <file>, never a branch switch — new test cases only):

✖ recognizes MCP-gateway-namespaced tool names (#13781, #13897) (54.472614ms)
  AssertionError [ERR_ASSERTION]: Docker MCP Toolkit style double-prefix name
  false !== true
ℹ tests 11
ℹ pass 10
ℹ fail 1

GREEN (fix restored):

✔ recognizes MCP-gateway-namespaced tool names (#13781, #13897) (4.263963ms)
✔ does NOT loosen matching into a plain substring test (2.320859ms)
ℹ tests 11
ℹ pass 11
ℹ fail 0

Also re-ran the existing suite in full (all 11 cases, including the pre-existing
some_other_tool → NO instruction and no tools → NO instruction cases) and the related
tests/unit/compression/ccr-non-mcp-full-prompt-loss-7746.test.ts (8/8) — both green, no
regression to the #7746 safety guarantee.

Gates run

  • node --import tsx/esm --test tests/unit/ccr-protocol-instruction.test.ts — 11/11 pass
  • node --import tsx/esm --test tests/unit/compression/ccr-non-mcp-full-prompt-loss-7746.test.ts — 8/8 pass
  • node scripts/check/check-file-size.mjs — no violation on touched files
  • node scripts/check/check-complexity.mjs — OK (2850 violations vs baseline 3218)
  • node scripts/check/check-cognitive-complexity.mjs — OK (1289 violations vs baseline 1437)
  • npm run typecheck:core — exit 0
  • npm run check:open-sse-typecheck — OK, 0 pre-existing errors
  • npx eslint --suppressions-location config/quality/eslint-suppressions.json <changed files> — clean
  • node scripts/check/check-changelog-integrity.mjs — OK

Verified the commit message and this PR body carry no AI-generation footer / Co-authored-by trailer.

⚠️ base-red inherited: #14004 — docs env/docs contract (fixed separately in #14022), chatHelpers file-size drift

Closes #13781
Closes #13897

…13781, #13897)

callerSupportsCcrRetrieve() matched the omniroute_ccr_retrieve tool by exact
string equality. MCP aggregators/gateways (Docker MCP Toolkit, Claude Code)
rename re-exposed tools with a namespace prefix (e.g.
mcp__docker__omniroute__omniroute_ccr_retrieve, or a dotted/slashed prefix),
so a fully MCP-capable caller reachable only under such a name was treated as
unable to retrieve at all -- skipping both the protocol-instruction injection
and, per the #7746 safety guarantee, the entire CCR compression engine for
that request.

Add matchesCcrRetrieveToolName(), a separator-bounded trailing-segment match
(__, ., /, :) that accepts a namespaced form of the tool name while still
rejecting a near-miss like omniroute_ccr_retrieve_v2 -- never a bare
substring/endsWith check.
@diegosouzapw

Copy link
Copy Markdown
Collaborator Author

Validated in local merge-train /tmp/mt-train3b.log on 192.168.0.113 @ train tip 408e2128791696a18966681953136fc96aeb99b0 (32 PRs boarded): static gates green; full test:unit 40694 tests, 17 failing — every one reproduces on the pure release tip (base-red sweep list), zero new reds. Merged --admin per merge-gates §4/§7.

@diegosouzapw
diegosouzapw merged commit 5fce23c into release/v3.8.51 Sep 18, 2026
14 of 21 checks passed
muhamadgalihsaputra pushed a commit to niyatna/NiyatnaRoute that referenced this pull request Sep 27, 2026
…heaper-inference#13781, cheaper-inference#13897) (cheaper-inference#14028)

callerSupportsCcrRetrieve() matched the omniroute_ccr_retrieve tool by exact
string equality. MCP aggregators/gateways (Docker MCP Toolkit, Claude Code)
rename re-exposed tools with a namespace prefix (e.g.
mcp__docker__omniroute__omniroute_ccr_retrieve, or a dotted/slashed prefix),
so a fully MCP-capable caller reachable only under such a name was treated as
unable to retrieve at all -- skipping both the protocol-instruction injection
and, per the cheaper-inference#7746 safety guarantee, the entire CCR compression engine for
that request.

Add matchesCcrRetrieveToolName(), a separator-bounded trailing-segment match
(__, ., /, :) that accepts a namespaced form of the tool name while still
rejecting a near-miss like omniroute_ccr_retrieve_v2 -- never a bare
substring/endsWith check.
@diegosouzapw
diegosouzapw deleted the fix/13781-ccr-tool-prefix branch October 18, 2026 23:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant