fix(adapters): consumer adapter hygiene (CON-01, CON-03..CON-13)

- CON-01: from_mcp discovery follows tools/list pagination
  (rmcp list_all_tools); three-page paginating-server test
- CON-03: from_wss refuses ws:// with a Bearer token unless
  FromWss::allow_plaintext() is called explicitly (tests: refusal,
  opt-in, token-less passthrough)
- CON-04: audio variant of content_block_union_schema requires
  ["type","data","mimeType"]; jsonschema-validated audio block
- CON-05/07: import-time credential documented on both adapters;
  dead per-call capability read removed
- CON-06: 401 classification typed-first (downcast to rmcp
  StreamableHttpError<reqwest::Error>; AuthRequired/InsufficientScope/
  Client with status 401); a :40101 URL no longer misclassifies (tested)
- CON-11: transport tools/call failures declare MCP_TRANSPORT_ERROR;
  rmcp JSON-RPC errors preserve code (MCP_JRPC_<code>) and data
- CON-12: tool names validated at import (/, whitespace, empty →
  SchemaParse); unit + integration tests
- CON-13: tokens held as alkcall Secret<String> (zeroize, redacted Debug)
- CON-08/09: no close handles; explicit-limitation notes in from_mcp
  module docs, from_wss module docs, and ADR-070
- CON-10: full_surface [[test]] required-features = ["mcp","test-support"];
  cargo test --features mcp now compiles and passes

Verified: cargo test; cargo test --features mcp; cargo test --all-features;
cargo clippy (--all-features) --all-targets -- -D warnings; cargo fmt --check
This commit is contained in:
2026-08-29 10:54:33 +00:00
parent 314472012d
commit 8700ed0fea
7 changed files with 837 additions and 107 deletions
@@ -95,9 +95,29 @@ same-protocol importer, with WSS as the transport instead of QUIC.**
WSS session is the `HttpAdapter` upgrade route
([ADR-067](067-websocket-carries-channels.md)); no separate server
type.
- **`from_wss` over non-TLS `ws://`** — plaintext WS is allowed by the
underlying transport for local/test use but is not the adapter's
documented path.
- **`from_wss` over non-TLS `ws://`** — plaintext WS is refused when a
Bearer token is present unless `FromWss::allow_plaintext` was called
explicitly (review-001 CON-03): a long-lived credential must not ride
an unencrypted connection without an explicit opt-in. Plaintext for
local/test use without a token stays allowed.
### Explicit session-lifetime limitation (v1)
**There is no teardown handle on `FromWss` in v1** (review-001
CON-08/CON-09): `import()` detaches the session fire-and-forget so the
imported handlers keep working off the Arc'd `CallConnection`, and
nothing closes the underlying server-side session when the assembly
layer is done with the import. Consequences, stated explicitly:
- Calling `import()` again (e.g. on a reconnect timer) stacks a second
full WS session over the first: duplicate op names in the registry,
and the original session is never torn down.
- v1 disposition: import **once per process**; treat the imported
surface as live for the process lifetime. A reconnecting assembly
layer should tear down its whole registry and re-import, accepting
the accumulated server-side sessions until the remote times them out.
- A close/teardown handle (and with it, safe reconnect) is future work;
v1 deliberately does not build a reconnect layer (OQ-03).
## Consequences