fix(adapters): MCP tool-gateway fidelity (PRJ-07..10, PRJ-13)

- search honors the optional query substring filter (PRJ-07)
- search excludes both Sub andPub ops per ADR-041/ADR-068; dead match
  arms removed (PRJ-08)
- batch returns {"results": [...]} with {isError, output|error} items;
  tool description states the shape (PRJ-09)
- structuredContent is always an object: non-object outputs wrapped as
  {"result": <output>} (PRJ-10)
- argument errors are CallError values (retryable always present);
  non-string operation no longer reports 'missing required field'
  (PRJ-13)

Verified: cargo test --all-features, cargo test, clippy -D warnings,
fmt --check
This commit is contained in:
2026-08-29 12:53:03 +00:00
parent a9d17405a7
commit 6fa2d4c036
2 changed files with 329 additions and 64 deletions
+43 -7
View File
@@ -1,7 +1,7 @@
---
id: review-001-mcp-tool-fidelity
name: MCP tool-gateway runtime fidelity (PRJ-07, PRJ-08, PRJ-09, PRJ-10, PRJ-13)
status: pending
status: completed
depends_on: [review-001-schema-internal-visibility]
scope: narrow
risk: low
@@ -39,11 +39,11 @@ this task covers the rest of the tool surface):
## Acceptance Criteria
- [ ] `search` respects a `query` argument (test); `Pub` ops excluded from results (test)
- [ ] Batch item doc matches the emitted shape (or shape changed to match — one way or the other, tested)
- [ ] Non-object outputs wrapped into objects or documented as an exception; batch shape consistent
- [ ] MCP argument errors carry `retryable` and truthful messages
- [ ] `cargo test --all-features` passes
- [x] `search` respects a `query` argument (test); `Pub` ops excluded from results (test)
- [x] Batch item doc matches the emitted shape (or shape changed to match — one way or the other, tested)
- [x] Non-object outputs wrapped into objects or documented as an exception; batch shape consistent
- [x] MCP argument errors carry `retryable` and truthful messages
- [x] `cargo test --all-features` passes
## References
@@ -58,4 +58,40 @@ this task covers the rest of the tool surface):
## Summary
> Filled on completion.
All five findings fixed in `src/adapters/to_mcp.rs`:
- **PRJ-07**: `call_tool` extracts the optional `query` argument and
`handle_search` filters the listing by substring match on the
operation name (after the visibility/ACL filter — the query narrows
what the caller is allowed to see, never widens it).
- **PRJ-08**: exclusion now covers both `"sub"` and `"pub"` (ADR-068 §1
and ADR-041 §2); the dead `"subscription"`/`"Sub"` match arms are
removed. A `search_excludes_pub_ops` test asserts the Pub op is
invisible to MCP discovery.
- **PRJ-09**: shape decided over doc — `batch` now returns
`{"results": [...]}` where each entry is
`{"isError": bool, "output": ...}` on success or
`{"isError": true, "error": <CallError>}` on failure; the tool
description states exactly that (multiple dispatch-path failures in
one batch let the other items run to completion, so the old
"shaped like a `call` result" description was the wrong side to
keep).
- **PRJ-10**: module doc documents the object guarantee: object outputs
pass through verbatim, non-object outputs are wrapped as
`{"result": <output>}` (`object_result`), applied consistently to
`call`'s `structuredContent` and to batch item `output` — so every
`structuredContent` is a JSON object and batch is never a top-level
array.
- **PRJ-13**: all argument errors (`call`, `batch`, `schema`, and the
batch per-item errors) are `CallError` values serialized through the
same `call_error_to_structured_error` path, so `retryable` is always
present. `parse_call_arguments` now distinguishes a missing
`operation` ("missing required field") from a non-string one
("must be a string, got <type>").
Tests: `search_honors_query_substring_filter`,
`search_excludes_pub_ops`, `call_wraps_non_object_output_into_object_
structured_content`, `call_argument_errors_carry_retryable_and_truthy_
messages`, `batch_returns_object_with_result_entries` (retryable +
INVALID_INPUT per-item check); existing tests updated to the new
shapes.