Files
alknet/tasks/http/review-mcp.md

8.9 KiB

id, name, status, depends_on, scope, risk, impact, level
id name status depends_on scope risk impact level
http/review-mcp Review MCP adapters for ADR-037/041 conformance (streamable HTTP, 4-tool gateway, output handling) completed
http/adapters/from-mcp
http/adapters/to-mcp
moderate low phase review

Description

Review the MCP adapters (from_mcp, to_mcp) for spec conformance, pattern consistency, and correctness. This is the quality checkpoint for the feature-gated MCP work.

Review Checklist

  1. from_mcp conformance (http-mcp.md):

    • FromMCP struct with endpoint, auth_token, namespace
    • OperationAdapter impl (async fn import)
    • Connects via rmcp StreamableHttpClientTransport::from_uri(endpoint)
    • Connection failure → AdapterError::DiscoveryFailed
    • 401 → AdapterError::Unauthorized
    • Calls tools/list → MCP tools
    • For each tool: HandlerRegistration with FromMCP provenance
    • spec.name = tool name (or namespace/tool_name)
    • spec.op_type = Mutation (MCP tools are call/response)
    • spec.visibility = Internal (ADR-015)
    • spec.input_schema = tool's inputSchema
    • spec.output_schema = declared outputSchema if present, else ContentBlock union
    • provenance = FromMCP, composition_authority: None, scoped_env: None (ADR-022)
    • capabilities = bearer token (injected at registration)
  2. from_mcp output handling (http-mcp.md §"Output handling"):

    • structured_content present → use as result (validated against output_schema)
    • structured_content absent → map content: Vec<ContentBlock> to ContentBlock union
    • No heuristic JSON.parse of text blocks (carry as ContentBlock)
    • isError: trueCallError (not the output handling path)
    • rmcp client connection maintained for registration lifetime
  3. from_mcp no-env-vars (ADR-014):

    • Handler reads context.capabilities, never std::env::var
    • Bearer token from Capabilities, not env vars
  4. to_mcp conformance (http-mcp.md, ADR-041):

    • Implements rmcp ServerHandler trait (call_tool, list_tools)
    • tools/list returns 4 fixed gateway tools (search, schema, call, batch)
    • tools/list does NOT return registry's operations (discovered via search)
    • search → dispatches services/list via GatewayDispatch::invoke
    • search results are AccessControl::check(identity)-filtered
    • Subscription ops filtered out of search results (ADR-041 §2)
    • schema → dispatches services/schema via GatewayDispatch::invoke
    • call → dispatches via GatewayDispatch::invoke (shared spine)
    • call result → CallToolResult::structured(value) for Ok
    • call error → CallToolResult::structured_error(details) for Err(CallError)
    • batch → loop over invoke, returns array
    • to_mcp is a pure projection (consumes registry, does not produce entries)
  5. to_mcp auth (http-mcp.md, research §4.4):

    • Bearer auth via shared bearer_auth_middleware (applied around nest_service)
    • Identity read from RequestContext.extensions inside call_tool
    • Identity survives rmcp framing (research §6 #2 — confirmed by spike)
    • MCP client has no PeerId (not an alknet peer, ADR-034 §4)
    • AccessControl gates search results and call dispatch
  6. to_mcp rmcp integration (http-mcp.md, research §4):

    • StreamableHttpService nested into axum Router at /mcp
    • ServerHandler impl is a gateway service (4 fixed tools)
    • rmcp owns JSON-RPC framing, session management, SSE priming
    • to_mcp owns call_tool/list_tools dispatch + CallToolResult mapping
  7. Streamable HTTP only (ADR-037):

    • stdio transport NOT built (not a dependency, not optional, not behind a feature)
    • mcp feature pulls in rmcp with streamable HTTP transport features only
    • transport-child-process is not a dependency
  8. Feature gate isolation:

    • from_mcp/to_mcp compile only with mcp feature
    • cargo check -p alknet-http (no mcp) succeeds — MCP code not compiled
    • cargo check -p alknet-http --features mcp succeeds
  9. Shared dispatch spine (research §5.1):

    • to_mcp call uses GatewayDispatch::invoke (same spine as to_openapi)
    • ResponseEnvelopeCallToolResult mapping is to_mcp-specific
    • No GatewayDispatch trait (concrete struct)
    • Identity resolution, context construction, invoke are shared (security axis)
    • Wire framing, discovery, streaming are per-gateway (presentation axis)
  10. Error fidelity (ADR-023):

    • from_mcp error_schemas from MCP tool error descriptions
    • to_mcp call error → CallToolResult::structured_error with typed details
    • No collision with protocol-level codes
  11. Security constraints:

    • No-env-vars invariant (from_mcp reads context.capabilities, ADR-014)
    • MCP clients are not peers (no PeerId, ADR-034 §4)
    • AccessControl gates search results and call dispatch
    • No secret material in CallToolResult
  12. Test coverage:

    • Unit tests for from_mcp (import, outputSchema present/absent, isError)
    • Unit tests for to_mcp (tools/list returns 4, search/schema/call/batch)
    • Integration test: MCP client → search → schema → call round-trip
    • Integration test: Bearer auth gates to_mcp service
    • Integration test: no-env-vars (no std::env::var in from_mcp)
    • Feature gate: cargo check without mcp succeeds

Acceptance Criteria

  • from_mcp matches http-mcp.md (OperationAdapter, tools/list, outputSchema handling)
  • from_mcp output handling: structuredContent-preferred, ContentBlock fallback, no JSON.parse
  • from_mcp no-env-vars: reads context.capabilities, never std::env::var
  • to_mcp matches ADR-041 (4-tool gateway, not one tool per operation)
  • to_mcp Subscription excluded from search results
  • to_mcp uses GatewayDispatch::invoke (shared spine)
  • to_mcp CallToolResult mapping correct (structured/structured_error)
  • to_mcp auth: shared middleware, Identity survives rmcp framing
  • Streamable HTTP only (ADR-037 — stdio NOT built)
  • Feature gate isolation (mcp code not compiled without mcp feature)
  • No GatewayDispatch trait (concrete struct, research recommendation)
  • Error fidelity (ADR-023 — no collision with protocol codes)
  • MCP clients are not peers (no PeerId, ADR-034 §4)
  • AccessControl gates search and call
  • No secret material in CallToolResult
  • Test coverage adequate for all MCP functionality
  • cargo fmt --check -p alknet-http --features mcp passes
  • cargo clippy -p alknet-http --features mcp passes with no warnings
  • cargo test -p alknet-http --features mcp passes
  • cargo check -p alknet-http (no mcp) succeeds

References

  • docs/architecture/crates/http/http-mcp.md — full MCP spec
  • docs/research/alknet-http-gateway-factoring/findings.md — §4 (rmcp constraints), §4.4 (auth sharing)
  • docs/architecture/decisions/037-mcp-stdio-transport-exclusion.md — ADR-037
  • docs/architecture/decisions/041-mcp-tool-gateway-pattern.md — ADR-041
  • docs/architecture/decisions/017-call-protocol-client-and-adapter-contract.md — ADR-017 §5
  • docs/architecture/decisions/023-operation-error-schemas.md — ADR-023
  • docs/architecture/decisions/014-secret-material-flow-and-capability-injection.md — ADR-014
  • docs/architecture/decisions/034-outgoing-only-x509-and-three-peer-roles.md — ADR-034 §4
  • /workspace/rust-sdk/ — rmcp SDK (ServerHandler, StreamableHttpService, CallToolResult)

Notes

The MCP adapters are feature-gated and the most SDK-coupled part of the crate (rmcp integration). The review should verify: (1) streamable HTTP only (no stdio, ADR-037), (2) the 4-tool gateway pattern (ADR-041, not one tool per operation), (3) the output handling (structuredContent-preferred, ContentBlock fallback, no JSON.parse), (4) the shared dispatch spine is used for to_mcp's call (same spine as to_openapi), (5) the auth middleware is shared (Identity survives rmcp framing — research §6 #2), (6) the no-env-vars invariant holds, (7) feature gate isolation (MCP code not compiled without mcp). If deviations are found, document and fix before considering the MCP adapters complete.

Summary

MCP adapters reviewed against all 12 checklist items. All conformance criteria pass: from_mcp (OperationAdapter, tools/list, structuredContent-preferred output handling, no JSON.parse, isError→CallError, no-env-vars ADR-014), to_mcp (4-tool gateway ADR-041, ServerHandler, Subscription excluded, GatewayDispatch::invoke shared spine, CallToolResult mapping, Identity survives rmcp framing research §6 #2, no PeerId ADR-034 §4), streamable HTTP only (ADR-037), feature gate isolation, GatewayDispatch concrete struct (not trait), error fidelity (ADR-023), test coverage (223 mcp tests + 5 integration tests). One formatting fix applied to from_mcp/mod.rs. cargo fmt --check, clippy --features mcp --all-targets, test --features mcp, and cargo check (no mcp) all pass.