fix(mcp): make every tool reply client-usable — structuredContent, pipelining, config get (#1522) - #1523
Merged
Merged
Conversation
DeusData
force-pushed
the
fix/1522-silent-empty-results
branch
from
August 10, 2026 22:52
47b778f to
2675ba5
Compare
…pelining, config get (#1522) Three regressions shipped in 0.10.0 share one failure shape: an empty result with a success status, indistinguishable from "nothing found" for the LLM clients that are cbm's primary consumers. 1) structuredContent {} on the whole tree-format surface (#1522 bug 1). #1488 replaced the duplicated payload with an EMPTY structuredContent object while every tool still declared a blanket permissive outputSchema. Spec-honoring clients (Claude Code among them) treat structuredContent as THE result when a schema is declared, so search_graph, trace_path, query_graph, get_architecture, search_code, and detect_changes all rendered as literally "{}" on their DEFAULT format, on every platform. The corrected contract: no tool declares an outputSchema (tool output is format-parameter-polymorphic — no static schema is truthful), JSON-object payloads keep their parsed structuredContent, errors keep structuredContent.error, and text-shaped payloads carry NO structuredContent key at all — which also preserves #1375's no-duplication win. 2) Frontend queue overflow killed the session (found by the #1522 sweep). Any 7+ requests pipelined in one stdin burst — an agent issuing parallel tool calls does exactly this — overflowed the 8-frame frontend queue, which failed the whole session: rc=1 with ZERO bytes of output, every buffered response lost. A full queue is now backpressure: the stdin reader blocks until the worker drains (bounded by the same stop/fail flags every teardown path already sets); only a single frame larger than the entire 12 MiB byte budget — which could never be admitted — remains a hard failure. 3) config get printed "" with exit 0 for every unset and every unknown key (#1522 bug 2). list printed stored-or-DEFAULT while get printed stored-or-EMPTY, and no subcommand validated key names, so a typo was indistinguishable from a correctly-read setting. One config-key table now drives help, list, get, set, and reset: get prints the stored value or the key's real default (the same fallback the runtime readers use), and unknown keys error with exit 1 on get, set, and reset alike. Tests — each RED on the pre-fix tree and RED again on revert: * test_mcp.c: text results carry no structuredContent key; tools/list declares no outputSchema; the tool-table guard now binds all three branches (absent / parsed-object / error) for every registered tool. * test_daemon_frontend.c: the over-capacity contract flips from "session fails" to "backpressure without loss" — the held first request plus all 32 over-capacity frames are answered and the run closes cleanly. * test_cli.c: the config command contract — defaults, round-trip, reset, and unknown-key rejection on all three subcommands. * smoke-test.sh: Phase 3z rewritten to the corrected structuredContent contract, new Phase 3z1 (default-format replies usable in schema-honoring clients, no outputSchema advertised), 3z2 (24 pipelined calls all answered), 3z3 (config defaults + unknown-key rejection) — all asserted against the SHIPPED artifact, where #1488's smoke phase previously pinned the empty-object behavior as correct. Verified end-to-end on the locally built production binary: all 15 tools declare no schema; text tools return ABSENT structuredContent, object tools populated, error envelopes intact; 7/24/64-deep pipelined bursts all answered with rc=0; config get prints real defaults and exits 1 on unknown keys. hook_augment (structuredContent.projects) and index_resilience (structuredContent.status) consume object payloads and are unaffected. Fixes #1522. Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
DeusData
force-pushed
the
fix/1522-silent-empty-results
branch
from
August 10, 2026 23:13
2675ba5 to
f59d24f
Compare
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.
Fixes #1522 — and a third, adjacent regression the verification sweep surfaced. All three shipped in 0.10.0 and share one failure shape: an empty result with a success status, which an LLM client cannot distinguish from "nothing found".
Bug 1 — the entire tree-format surface rendered as `{}` in MCP clients (default format, all platforms)
#1488 emptied `structuredContent` while every tool still declared a blanket permissive `outputSchema`. Spec-honoring clients (Claude Code among them) read `structuredContent` as the result when a schema is declared — so `search_graph`, `trace_path`, `query_graph`, `get_architecture`, `search_code`, and `detect_changes` all returned visibly-empty `{}` on their default format. CLI looked fine (it prints content text), which is why the reporter's Windows setup was the first to see it.
Corrected contract: no tool declares an `outputSchema` (output is format-parameter-polymorphic — no static schema is truthful); object payloads keep parsed `structuredContent`; errors keep `structuredContent.error`; text payloads carry no key at all — preserving #1375's no-duplication win.
Bug 2 — ≥7 pipelined requests killed the server with zero output
The 8-frame frontend queue failed the whole session on overflow: any 7+ tool calls written in one stdin burst (agents issuing parallel tool calls pipeline exactly like this) died rc=1 with every buffered response lost. A full queue is now backpressure — the stdin reader blocks until the worker drains, bounded by the same stop/fail flags every teardown path already sets. Only a single frame exceeding the entire 12 MiB budget remains a hard failure.
Bug 3 — `config get` printed empty + exit 0 for every unset and unknown key
`list` printed stored-or-DEFAULT, `get` printed stored-or-EMPTY, nothing validated key names. One config-key table now drives help/list/get/set/reset; `get` prints the stored value or the key's real runtime default; unknown keys exit 1 with the known-key list on stderr.
Tests (each RED pre-fix and RED on revert)
End-to-end verification (locally built production binary)
0/15 tools declare a schema; text tools ABSENT / object tools POPULATED / error envelopes intact; pipelined bursts of 7, 24, and 64 all answered with rc=0; config prints real defaults and exits 1 on typos. `hook_augment` (`structuredContent.projects`) and `index_resilience` (`structuredContent.status`) consume object payloads — unaffected.
Ships in v0.10.1 immediately after merge.