Skip to content

v3.11.0

This wave corrects the MCP surface an operator actually reads and drives: one tool per question, a per-run cancel, typed results a client can validate, in-band progress on long runs, a remote transport that serves resources and enforces its request headers, and a claim-ledger fold that answers honestly about claims that can never be released.

Behaviour changes (read before upgrading)

  • The MCP tool surface is consolidated; every removed name stays callable for this release as a deprecated alias (#3087). Aliases are never advertised in tools/list; an alias answers with its historical payload under result plus a notice naming its replacement. The aliases are gated by BERNSTEIN_MCP_DEPRECATED_ALIASES (set 0 to drop them now) and are removed in v3.12.0. The mapping:
Removed name Replacement
bernstein_health bernstein_status (the response is the liveness signal)
bernstein_tasks bernstein_status with the status filter
bernstein_cost bernstein_status (cost fields)
bernstein_create_subtask bernstein_run with parent_task_id
bernstein_task_handle bernstein_run_status
bernstein_update bernstein_post_message
bernstein_context bernstein_task_capsule
bernstein_stop bernstein_shutdown_orchestrator
bernstein_scenarios bernstein_scenario with action="list"
bernstein_scenario_status bernstein_scenario with action="status"
verify_chain bernstein_verify_lineage

A scenario run now returns the same pollable handle shape as bernstein_run, so bernstein_run_status polls it.

  • The remote MCP transport now enforces Origin and MCP-Protocol-Version (#3084). A request whose Origin is not in the configured allow list is refused with 403 (the enforcement half of the DNS-rebinding defence the response headers alone never provided), and an unsupported protocol-version header is refused with 400 naming the supported revisions; an absent header is served as 2025-03-26. Both checks ship behind a one-release opt-out, BERNSTEIN_MCP_REMOTE_HEADER_CHECKS=0, for operators whose proxies rewrite these headers. A JSON array request body (the batching removed from the MCP schema two revisions ago) is now rejected.

MCP

  • Per-run cancel (#3078). bernstein_cancel cancels one task and its subtask tree through POST /tasks/{task_id}/cancel and the orchestrator keeps running. It reads the task first: an unknown id or a non-cancellable state is refused with no state-changing request sent, and an already-terminal task is reported with its state rather than as an error. The whole-orchestrator stop now opens its description with its blast radius and points at bernstein_cancel for the single-run case.

  • Structured results with declared output schemas (#3086). bernstein_run, bernstein_status and bernstein_run_status declare an outputSchema and return structuredContent: the run-handle fields (taskId, runId, status, journalHead, chainHead, receiptHash, pollToken) are first-class typed fields. The text content block is byte-for-byte unchanged, including the _meter envelope, and the declared schema describes the envelope exactly as emitted for the live cost-meter state; the handle portion is generated from the wire body so it cannot drift. Nothing in the structured result's hashed region carries a wall clock: two polls of an unchanged journal are byte-identical. Moving _meter out of the result body is tracked separately.

  • Progress notifications on the poll (#3085). A bernstein_run_status call carrying a progressToken receives a notifications/progress tick derived entirely from the chain-computed progress fold: earned steps as progress, declared evidence producers as total, fold counters as the message. A tick that does not strictly advance the previous tick for that run is suppressed, so the notified sequence is monotone by construction; a failed emission never reaches the tool result, and clients that send no token see no change.

  • The remote transport serves resources (#3084). resources/list, resources/templates/list and resources/read now answer over the streamable HTTP transport with the same resources stdio exposes: the runtime capability card, the skill discovery index, and - only when BERNSTEIN_LINEAGE_MCP_ENABLED=1 opts in - the lineage records. Resource reads pass through the same auth checks as tool calls. The initialize result and the capability card now report the protocol revision actually negotiated for the connection instead of a constant.

Audit

  • The claim fold answers honestly for unreleasable claim paths (#3072). Claim receipts minted on the MCP claim path (claim_path="mcp_claim") are recorded against the JSON backlog, a store the task-store release receipts never touch, so they can never be released and the plain fold reported them as held forever. reconstruct_claim_holders gains a claim_paths scope so a verifier folds the release-capable ledger without the acquisition-only rows (or selects exactly them); UNRELEASED_CLAIM_PATHS names the paths with no release half, and release_ledger_boundary locates the first release receipt so the pre-release-ledger region of an old chain is answered as unknown instead of confidently wrong.