Codex stop-gate (round 2 on the same surface): the previous fix
gave `McpTool::call` access to `&ToolCall` so a well-behaved tool
COULD echo `request.id` — but nothing made it do so. A buggy /
adversarial tool was still free to mint a fake id, and id-mismatch
silently broke JSON-RPC routing on the client side.
Refactor: change the trait return type from `ToolResponse` (id +
payload) to a content-only `ToolOutcome::{ Ok(map) | Err(code,
msg) }`. The registry's `dispatch` wraps the outcome with the
originating `call.id` to produce the on-wire `ToolResponse`. Tools
never see the id; id-mismatch is now structurally impossible.
- New `ToolOutcome` enum sits between tool implementations + the
wire-shape `ToolResponse`.
- `McpTool::call(&self, args: &BTreeMap<String, String>) ->
ToolOutcome` — args-in, outcome-out, id-blind.
- `ToolRegistry::dispatch` constructs `ToolResponse::Ok { id:
call.id, result }` and `ToolResponse::Err { id: call.id, ... }`
from the outcome.
- `EchoTool` updated to the new signature.
- New `LyingTool` fixture deliberately ignores any context the
registry might pass; `registry_forces_id_on_response_regardless_of_tool`
asserts the response still carries `req-honest` even though
LyingTool's `call` returns an empty content map.
Tests: 4 MCP tests pass (3 carried over + 1 new id-stamping
regression). 206 shell-core total. Wasm32 build clean.