Files
alkhttp/tasks/adapters/review-001-mcp-tool-fidelity.md
T
glm-5.3-flash 6fa2d4c036 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
2026-08-29 12:53:03 +00:00

97 lines
4.5 KiB
Markdown

---
id: review-001-mcp-tool-fidelity
name: MCP tool-gateway runtime fidelity (PRJ-07, PRJ-08, PRJ-09, PRJ-10, PRJ-13)
status: completed
depends_on: [review-001-schema-internal-visibility]
scope: narrow
risk: low
impact: component
level: implementation
tags: [adapters, review-001, mcp]
---
## Description
Review 001 MCP projection findings in `src/adapters/to_mcp.rs` (the
schema-ACL half of the MCP story is review-001-schema-internal-visibility;
this task covers the rest of the tool surface):
- **PRJ-07**: the `search` tool advertises an optional substring `query`
filter but `call_tool` drops `arguments` entirely (`:382`) — an LLM
passing `{"query": "fs"}` gets the full unfiltered listing (context
waste/overflow in big registries). Honor the filter.
- **PRJ-08**: `search` excludes only `Sub`, not `Pub` (`:268-274`),
contradicting ADR-068 ("excludes both"). A discovered Pub op can never
be invoked via the `call` tool — the advertised discovery surface is a
lie for the whole Pub class. (The `"subscription"`/`"Sub"` match arms
are dead — `op_type_str` only emits lowercase.)
- **PRJ-09**: batch item shape `{"isError", "output"|"error"}` contradicts
the tool description ("each shaped like a `call` result") — fix the
description or the shape.
- **PRJ-10**: `structuredContent` passes output through verbatim, so
string/array/null outputs produce non-object `structuredContent`
(strict MCP clients may reject; batch returns a top-level array). Wrap
or document.
- **PRJ-13**: hand-rolled argument errors omit `retryable` (required by
the OpenAPI error schemas, always present on `CallError`), and a
non-string `operation` reports the misleading "missing required field".
Build argument errors in the `CallError` wire shape.
## Acceptance Criteria
- [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
- docs/reviews/001-initial-implementation-review.md (Part F, PRJ-07..PRJ-10, PRJ-13)
- docs/architecture/decisions/041-mcp-tool-gateway-pattern.md
- docs/architecture/decisions/068-gateway-publish-endpoint.md
## Notes
> Agent fills during implementation. Depends on the visibility task
> because both rework the `schema` tool in `to_mcp.rs`.
## Summary
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.