Summary
CallMcpWithQuery is intended as a fallback for narrowing an oversized structured MCP result before it enters model context. Its current model-facing and execution semantics instead make it an attractive general-purpose MCP caller, and—more importantly—treat every addressed MCP target as read-only and floor-allowed regardless of the target tool's own annotations or permission behavior.
The wrapper can address any tool exposed by the MCP manager. It unconditionally returns ReadOnly() == true, is registered whenever any MCP tool exists, and has a built-in default Allow. The authority evaluator correctly checks the addressed mcp__<server>__<tool> capability, but permission evaluation and dispatch classification still use the wrapper itself. The remote call occurs before jq filtering, so a remotely mutating target can perform its mutation even though the wrapper was dispatched and permitted as read-only.
Why this matters
A direct mutating MCP tool and the same tool reached through CallMcpWithQuery do not inherit the same safety behavior:
- A direct MCP tool without an allow rule defaults to an interactive approval.
CallMcpWithQuery is floor-allowed.
- A direct tool without
readOnlyHint: true is serialized conservatively.
CallMcpWithQuery is always classified as read-only and can be read-parallel dispatched.
- The wrapper does not limit target selection to MCP tools advertising
readOnlyHint: true.
This creates a permission and concurrency bypass for remote side effects. Capability authorization alone does not close the gap when the session legitimately carries the target capability.
Session evidence
Observed in local session 50028801afa1b8a77186921f8e171d66 using mecated v0.0.38-57-g7cf4e5dfd and gpt-5.6-terra:
- The model used ten
CallMcpWithQuery calls for GitHub pull_request_read rather than the directly exposed GitHub MCP tool.
- Every call used
jq_filter: ".", so none narrowed its result. The wrapper added no value over the direct call.
- The calls ran without permission prompts because
CallMcpWithQuery is floor-allowed.
- Later direct
mcp__github__add_issue_comment calls each produced permission.ask with no permission rule matched mcp__github__add_issue_comment; client approval required.
We did not invoke add_issue_comment through the wrapper, because that would exercise a real remote mutation. The implementation path is sufficient to show that the wrapper accepts the target and calls it before attempting to filter its result.
Contributing usability behavior
The description already says to use the wrapper when a tool returns a large JSON structure and to prefer the remote tool's own pagination/filter parameters. However:
- The generic name
CallMcpWithQuery presents it as a normal MCP calling path.
- It is disclosed eagerly alongside direct MCP tools rather than after an oversized-result failure.
- It accepts identity/no-op jq filters such as
..
- Its floor-allow and read-parallel behavior make it operationally easier than direct calls.
The observed model behavior therefore appears to be encouraged by more than wording alone. Description clarification would help, but would not fix the target-semantics defect.
Expected behavior
Reaching an MCP tool through CallMcpWithQuery must not weaken that target's permission or dispatch semantics. The wrapper should remain a bounded result-projection mechanism, not an alternate authority or mutation path.
Possible design directions:
- Restrict
CallMcpWithQuery to targets that explicitly advertise readOnlyHint: true; fail closed for missing or false hints.
- If mutating targets must be supported, resolve permission and dispatch behavior against the addressed target so the same call asks/denies and serializes exactly as its direct form.
- Reject identity/no-op filters and make the error direct the model to the native MCP tool.
- Strengthen the description to say: use only after a direct MCP call returns an oversized structured-result error, never for a first attempt.
- Consider disclosing the fallback only after the oversized-result path recommends it.
Suggested acceptance coverage
- A wrapper call to an explicit read-only target remains usable and bounded.
- A target with
readOnlyHint false or absent cannot execute under unconditional read-only/floor-allow semantics.
- A mutating target, if supported, receives the same permission decision and serial dispatch as its direct namespaced tool.
- Target-specific configured Ask/Deny rules cannot be bypassed through the wrapper.
- The remote
CallTool operation is never reached when target semantics reject the call.
- A no-op jq filter such as
. is rejected or otherwise cannot serve as a general direct-call substitute.
Summary
CallMcpWithQueryis intended as a fallback for narrowing an oversized structured MCP result before it enters model context. Its current model-facing and execution semantics instead make it an attractive general-purpose MCP caller, and—more importantly—treat every addressed MCP target as read-only and floor-allowed regardless of the target tool's own annotations or permission behavior.The wrapper can address any tool exposed by the MCP manager. It unconditionally returns
ReadOnly() == true, is registered whenever any MCP tool exists, and has a built-in defaultAllow. The authority evaluator correctly checks the addressedmcp__<server>__<tool>capability, but permission evaluation and dispatch classification still use the wrapper itself. The remote call occurs before jq filtering, so a remotely mutating target can perform its mutation even though the wrapper was dispatched and permitted as read-only.Why this matters
A direct mutating MCP tool and the same tool reached through
CallMcpWithQuerydo not inherit the same safety behavior:CallMcpWithQueryis floor-allowed.readOnlyHint: trueis serialized conservatively.CallMcpWithQueryis always classified as read-only and can be read-parallel dispatched.readOnlyHint: true.This creates a permission and concurrency bypass for remote side effects. Capability authorization alone does not close the gap when the session legitimately carries the target capability.
Session evidence
Observed in local session
50028801afa1b8a77186921f8e171d66usingmecated v0.0.38-57-g7cf4e5dfdandgpt-5.6-terra:CallMcpWithQuerycalls for GitHubpull_request_readrather than the directly exposed GitHub MCP tool.jq_filter: ".", so none narrowed its result. The wrapper added no value over the direct call.CallMcpWithQueryis floor-allowed.mcp__github__add_issue_commentcalls each producedpermission.askwithno permission rule matched mcp__github__add_issue_comment; client approval required.We did not invoke
add_issue_commentthrough the wrapper, because that would exercise a real remote mutation. The implementation path is sufficient to show that the wrapper accepts the target and calls it before attempting to filter its result.Contributing usability behavior
The description already says to use the wrapper when a tool returns a large JSON structure and to prefer the remote tool's own pagination/filter parameters. However:
CallMcpWithQuerypresents it as a normal MCP calling path...The observed model behavior therefore appears to be encouraged by more than wording alone. Description clarification would help, but would not fix the target-semantics defect.
Expected behavior
Reaching an MCP tool through
CallMcpWithQuerymust not weaken that target's permission or dispatch semantics. The wrapper should remain a bounded result-projection mechanism, not an alternate authority or mutation path.Possible design directions:
CallMcpWithQueryto targets that explicitly advertisereadOnlyHint: true; fail closed for missing or false hints.Suggested acceptance coverage
readOnlyHintfalse or absent cannot execute under unconditional read-only/floor-allow semantics.CallTooloperation is never reached when target semantics reject the call..is rejected or otherwise cannot serve as a general direct-call substitute.