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

4.5 KiB

id, name, status, depends_on, scope, risk, impact, level, tags
id name status depends_on scope risk impact level tags
review-001-mcp-tool-fidelity MCP tool-gateway runtime fidelity (PRJ-07, PRJ-08, PRJ-09, PRJ-10, PRJ-13) completed
review-001-schema-internal-visibility
narrow low component implementation
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

  • 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

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 ").

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.