Skip to content

feat: expose responseLevel over MCP — Phase 4#944

Merged
thymikee merged 2 commits into
mainfrom
feat/phase4-mcp-response-level
Jun 30, 2026
Merged

feat: expose responseLevel over MCP — Phase 4#944
thymikee merged 2 commits into
mainfrom
feat/phase4-mcp-response-level

Conversation

@thymikee

Copy link
Copy Markdown
Member

What

Phase 4: let MCP agents opt into the leveled response views that #942 added, mirroring how includeCost is exposed at the MCP boundary.

src/mcp/command-tools.ts:

  • readClientConfig reads a responseLevel tool arg, validates it against RESPONSE_LEVELS ('digest' | 'default' | 'full'), and sets client.responseLevel only for valid values; rejects other non-undefined values with a clear error. (Extracted a small readResponseLevel helper to keep readClientConfig under the fallow complexity threshold.)
  • stripMcpConfigFields strips responseLevel so it never leaks into the command input.
  • withMcpConfigSchema advertises responseLevel: { type: 'string', enum: ['digest','default','full'], … } in every tool's inputSchema.

Default-off is byte-identical

With responseLevel absent, client.responseLevel is never set and the request shape is unchanged — same additive, MCP-boundary-only pattern as includeCost. Covered by a test asserting the absent path yields an empty config.

Verification

  • tsc --noEmit 0; oxfmt + oxlint --deny-warnings clean; fallow audit --base origin/main clean; vitest run src/mcp 25 pass; Layering Guard empty.

Built by a delegated worker; rebased onto main after #942 merged. Tests: digest→config + arg stripped; absent→byte-identical; invalid rejected; inputSchema advertises the enum.

@github-actions

github-actions Bot commented Jun 30, 2026

Copy link
Copy Markdown

Size Report

Metric Base Current Diff
JS raw 1.4 MB 1.4 MB +552 B
JS gzip 455.5 kB 455.7 kB +171 B
npm tarball 561.0 kB 561.2 kB +190 B
npm unpacked 2.0 MB 2.0 MB +552 B

Startup median (7 runs, lower is better):

Scenario Base Current Diff
CLI --version 28.1 ms 28.0 ms -0.1 ms
CLI --help 49.3 ms 49.1 ms -0.2 ms

Top changed chunks:

Chunk Raw diff Gzip diff
dist/src/server.js +9.3 kB +3.7 kB

@thymikee

Copy link
Copy Markdown
Member Author

Blocking this one for the MCP optimized text path.

When an MCP caller sets responseLevel: digest, structuredContent receives the digest payload from the daemon, but renderToolText() still sends that same digest object through formatCliOutput() for optimized text. For snapshot, the CLI formatter expects nodes; the digest view intentionally drops nodes and returns nodeCount/refs, so formatSnapshotText() falls back to an empty array and prints Snapshot: 0 nodes. That makes the default MCP content[0].text contradict the actual digest payload whenever the screen is non-empty.

Please add a shipped-path MCP test for snapshot with mcpOutputFormat: optimized plus responseLevel: digest, and either render non-default response views as JSON text or teach the snapshot formatter how to render the digest shape. Until then agents using the optimized MCP text get misleading state even though the structured content is correct.

When an MCP caller sets responseLevel:digest/full, structuredContent carries the
leveled (e.g. snapshot digest) payload, but renderToolText still ran it through
the optimized CLI formatters, which assume the default shape — the snapshot
formatter expects `nodes` (the digest drops them) and printed 'Snapshot: 0
nodes', contradicting structuredContent. Bypass the optimized formatters for any
non-default responseLevel and emit the leveled payload verbatim as JSON. Adds the
shipped-path test (snapshot, mcpOutputFormat:optimized, responseLevel:digest).
@thymikee

Copy link
Copy Markdown
Member Author

Fixed (pushed). You're right — the optimized text path mis-rendered the digest. renderToolText now bypasses the optimized CLI formatters whenever responseLevel is non-default (digest/full) and emits the leveled payload verbatim as JSON, so the text matches structuredContent instead of printing 'Snapshot: 0 nodes'. Added the requested shipped-path test: snapshot with mcpOutputFormat: optimized + responseLevel: digestcontent[0].text is the JSON digest (and asserts it never emits the '0 nodes' line). tsc/oxlint/fallow clean, 26 MCP tests pass.

@thymikee

Copy link
Copy Markdown
Member Author

Re-reviewed the fix at 7437029.

The previous MCP optimized-text blocker is addressed: responseLevel is validated/stripped at the MCP boundary, passed into the client config, and renderToolText() now emits JSON for non-default response levels instead of passing digest/full payloads through CLI formatters that expect default shapes. The added shipped-path test covers snapshot with mcpOutputFormat: optimized + responseLevel: digest and asserts the text is the JSON digest, not Snapshot: 0 nodes.

All 21 checks are green. I do not see an actionable blocker now.

@thymikee thymikee added the ready-for-human Valid work that needs human implementation, judgment, or maintainer merge label Jun 30, 2026
@thymikee thymikee merged commit 1e395c5 into main Jun 30, 2026
21 checks passed
@thymikee thymikee deleted the feat/phase4-mcp-response-level branch June 30, 2026 06:58
@github-actions

Copy link
Copy Markdown
PR Preview Action v1.8.1
Preview removed because the pull request was closed.
2026-06-30 06:59 UTC

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready-for-human Valid work that needs human implementation, judgment, or maintainer merge

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant