# Changelog

## 2.8.0 - 2026-10-19

- Wrap every HTTP authentication in the `auth/authenticate` telemetry span.
  Its `stop` event carries the elapsed `duration`, a bounded `outcome` (for
  example `:missing_credentials`, `:token_expired`, `:invalid_audience`,
  `:sender_constraint_mismatch`, `:dpop_replay`, `:dpop_nonce_required`,
  `:step_up_required`, `:verifier_unavailable`, `:verifier_failure`,
  `:principal_binding_failure`), the refusal's HTTP `status`, and the
  authenticated `binding`. The outcome AttestoMCP records on the connection is
  used when present; otherwise it is classified from the captured OAuth error
  envelope, so the span works with every supported AttestoMCP release.
  `auth/refusal` and `auth/policy_failure` are unchanged.

## 2.7.2 - 2026-10-18

- Fail the generated principal admission index migration before any DDL when
  `attesto_mcp_sessions` does not exist, instead of returning successfully.
  Ecto records a migration as applied once it returns, so the 2.7.1 skip left
  the index missing with no later run to add it. The version is not recorded on
  failure and the migration runs again once the sessions migration has been
  applied. A Postgres-backed regression test covers both outcomes.

## 2.7.1 - 2026-10-18

- Accept `--help` in `mix attesto_mcp_server.gen.migration`; it prints the
  usage and the accepted options instead of failing with "invalid options".
- Add `--skip-url-elicitations` to the migration generator so a host that does
  not use URL elicitation can rerun the task for later additions, such as the
  principal admission index, without the optional table coming back.
- End generated migrations with a newline so they pass
  `mix format --check-formatted` unchanged.
- Make the generated migrations idempotent: tables and indexes are created only
  when absent and dropped only when present, and the principal admission index
  migration skips itself with a warning when `attesto_mcp_sessions` does not
  exist yet instead of failing.

## 2.7.0 - 2026-10-18

- Keep request-path rate-limit decisions constant-time by storing buckets per
  traffic category, pruning expired buckets during periodic cleanup, and
  evicting one member of the largest category without scanning or sorting the
  10,000 live keys. Group authentication failures from one IPv6 `/64`. Once an
  authentication-failure bucket is exhausted, reject later attempts before
  token-signature verification. A separate refundable `auth_checks` bucket
  bounds concurrent verification without charging successful authentication
  against the failure rate; failures are recorded after verification. A full
  table reclaims one bucket from its most populous traffic category so one
  category cannot globally deny admission to new sources.
- Bind modern subscriptions and session-bound streams to the scopes captured
  when they open, on every transport. Delivery now checks generic catalog
  scopes and resource-definition scope alternatives before any optional host
  reauthorization callback. Transport-neutral callers must pass granted scopes
  when opening a subscription or stream; compatibility arities capture none
  and therefore receive only notifications with no scope requirement.
- Limit active session-bound sessions to 128 per principal and live legacy
  streams to eight per session and server process by default. Add
  `max_sessions_per_principal` and `max_legacy_streams_per_session`, exact
  active-session counts for the bundled stores, atomic admission across shared
  bundled stores, deterministic durable principal encoding across nodes, and
  namespace-aware optional count/admission callbacks for custom stores. Ecto
  updates now share the admission lock so an in-flight expiry extension cannot
  exceed the principal cap. Existing Ecto hosts should rerun
  `mix attesto_mcp_server.gen.migration` and apply the generated concurrent
  principal-admission index migration.
- Apply configured `default_scopes` to `completion/complete` before its
  prompt/resource fallback scopes.
- Replace full-catalog snapshots on selected tool, exact resource, and
  registered-header request paths with indexed registry lookups. Registered
  argument mirrors now follow a tool's wire-level name when it differs from
  its registry identity.
- Split URI-template matching, multi-round retry handling, store setup,
  startup option validation, request-event delivery, handler invocation,
  protocol negotiation, and scope policy into focused internal modules. Reuse
  the same Ecto adapter checks, legacy-session lookup, telemetry correlation,
  scope-set evaluation, and bounded JSON option parsing across their callers.
  These internal moves preserve the public API and transport message protocol.
- Consolidate test HTTP clients, polling, and supervised server startup in
  `test/support` so transport and concurrency regressions exercise the same
  helpers.
- Retain the hidden cacheable-method and icon-limit getters as callable 2.x
  compatibility entry points.
- Correct the multi-mount Phoenix example so every protected-resource metadata
  route names its trusted authorization-server issuer.
- Translate the server's legacy `:resource` endpoint-path option to MCP 2.1's
  unambiguous `:resource_path` boundary without forwarding both keys.
- Preserve the explicitly local `allow_dynamic_origin` mode by translating it
  to MCP 2.1's pinned callback contract, and have the installer emit
  `allow_http: true` only for an accepted HTTP loopback origin.
- Give bounded schema compilation and instance evaluation five seconds on a
  loaded host. Schema and JSON node, depth, and byte limits continue to bound
  the work below the server's 30-second request deadline.
- Verify the frozen 2.6 conformance fingerprint against its recorded release
  commit so later release work does not invalidate historical evidence.

## 2.6.0 - 2026-10-17

- Make the server-side HTTP authorization gate use the same per-method scope
  defaults and params-dependent completion/subscription rules as the Plug
  boundary. The Plug records its resolved policy so the direct server gate does
  not apply `default_scopes` twice. The local stdio adapter continues to require
  no fallback scopes unless `scope_map` or `default_scopes` is configured.
- Apply one header-name policy during both tool registration and HTTP request
  handling, including identical token syntax and length limits for every
  `x-mcp-header` suffix and its generated `Mcp-Param-*` wire header.
- Expand the supported `attesto_mcp` range to `>= 1.3.0 and < 3.0.0`, retaining
  the original minimum while allowing the coordinated AttestoMCP 2.0 release.
- Exercise the retained JSV 0.24.0 floor in an explicit CI lane, and label the
  archived 2.3.0 dependency inventory as historical release evidence.

## 2.5.0 - 2026-10-15

- Extend automatic AttestoPhoenix installation through the 4.x line while
  retaining the 2.14 floor and 3.x compatibility. The coordinated source and
  published-package lanes exercise the new major-version boundary.
- Make multi-round input requests all-or-nothing when the client lacks any
  required capability. Tool and prompt retries now validate their original
  arguments before merging typed input responses, reject response keys that
  collide with declared arguments, and leave signed request state unconsumed
  when the client can repair the request.
- Encode RequestState digests deterministically so clustered nodes agree on
  map-backed operation and caller bindings. This changes RequestState
  signatures: a mixed 2.4/2.5 rolling deployment can reject an in-flight
  multi-round request issued by the other version. RequestState lifetime is
  capped at 120 seconds, after which clients can begin a fresh round.
- Re-run the configured authentication `:principal` callback before each
  matching subscription event is delivered. This refreshes revocation and
  principal data while preserving the stream's original actor, binding, and
  tenant. Hosts should keep callback lookups indexed and bounded because their
  cost now applies once per candidate event and subscriber; rejection or
  callback failure suppresses that delivery.
- Reject `javascript:`, `data:`, and `vbscript:` resource identifiers and
  links. URI checks remain syntactic; hosts that dereference accepted resources
  must continue to enforce their own destination policy.
- Bound the built-in URL elicitation store to 10,000 records and 64 MiB by
  default. Add `url_elicitation_max_entries` and
  `url_elicitation_max_bytes` for configuring those limits. Admission reclaims
  enough expired records to avoid a false capacity failure and never evicts a
  live approval. `UrlElicitationStore.ETS.put/2` now returns
  `{:error, :already_exists}` for every duplicate key, including consumed
  records, and `{:error, :capacity}` when no safe space remains.
- Treat only Plug's exact `%{"_json" => list}` parsed-body wrapper as a
  top-level JSON array. A JSON-RPC object may use `_json` as an extension key
  without being mistaken for an unsupported batch.
- Scope one-time RequestState and cursor secret initialization locks to the
  local node, and treat an explicitly null token `client_id` like an absent
  client identifier during stream ownership checks.
- Temporarily pin the repository's dev/test-only Bandit dependency to upstream
  commit `d88b21953c65061f21136ea76a89a4f95a2ac7a1`, which contains the official
  fix for GHSA-69m6-wcv4-rwcf. No patched Hex release exists yet; replace this
  pin with the first patched release when Bandit publishes one. Runtime
  consumers do not inherit this repository-only dependency.

## 2.4.0 - 2026-10-06

- Expose an immutable, bounded snapshot of each request's `_meta` object to
  handlers and per-request callbacks as `context.request_meta`, for HTTP,
  stdio, and direct test dispatch. The snapshot is untrusted input kept apart
  from verified identity, scopes, tenant, and host context. Metadata above
  `max_request_meta_bytes` (65,536 bytes by default) is rejected, not
  truncated. `AttestoMCP.Server.RequestMeta` documents the contract.
- Bind multi-round retry state to operation parameters and request metadata,
  except `traceparent`, `tracestate`, `baggage`, `progressToken`, and
  `io.modelcontextprotocol/clientInfo`. Tracing, progress, and display values
  may change on retry; application metadata and protocol settings remain
  bound to the original request. Denied direct tool retries do not consume
  their signed state. Completed tool retries discard authored cache hints.
- Add `instructions_provider` for caller-specific guidance in modern discovery
  and session-bound initialization, and `tool_presentation` for caller-specific
  titles, descriptions, icons, and application `_meta` on visible tools.
  Failures return controlled errors, never fall back to static text, and leave
  a session-bound initialization retryable. Customized catalogs are part of
  the pagination fingerprint.
- Add `cache_policy` for cache hints by operation, resource definition, and an
  optional resolver, with handler hints that can only shorten or restrict.
  Definition policies apply without a server-level policy. Personalized,
  filtered, paginated, and retried results stay private; resource result
  metadata participates in privacy selection, and continuation cursors retain
  their caller binding. See the migration runbook for the corrections that
  apply without a policy.
- Add `server_icons` to the server implementation identity in discovery,
  modern result metadata, and `2025-11-25` initialization, with full icon
  validation. Preserve valid handler-authored identity, including empty
  optional text/icon arrays and HTTP icons, through bounded wire validation.
  Reject trailing control characters in configured icon MIME types and sizes.
- Add opt-in `export_schema_dialect` to declare the default JSON Schema 2020-12
  dialect on exported tool schema roots without changing validation.
- Extend `AttestoMCP.Server.Test` with resource, prompt, completion,
  discovery, catalog, and generic request helpers that share one request
  builder and support metadata, cursors, and multi-round retry fields. Reject
  invalid helper options and conflicting reserved request fields consistently.
- Initialize the fixed embedded JSON Schema meta-schemas at application
  startup, so the first request does not wait on their initialization lock.
  The three fixed roots share a ten-second startup deadline; user-supplied
  schema compilation and evaluation retain their one-second deadlines.

## 2.3.1 - 2026-10-04

- Support JSV 0.24.x as well as 0.25.x. Both retain the existing bounded
  validation and format policy. The 0.24.0 floor passes the server suite and
  pinned JSON Schema corpus with the same explicit exclusions.

## 2.3.0 - 2026-10-03

- Use JSV for bounded JSON Schema 2020-12 and draft-07 evaluation, including
  reference scope, numeric equality, and unevaluated annotations. Network
  resolution and casting remain disabled. Tool input and structured output
  retain format assertions by default, with `schema_formats: false` selecting
  annotation semantics.
- Reuse successfully compiled schemas across request processes through a
  bounded application cache. Schema and instance limits, format policy and
  validation timeouts still apply; cache restarts fall back to compilation.
  Large or output-heavy workloads can remain slower than the previous limited
  validator; see the [migration runbook](docs/migration.md#json-schema-evaluation-and-format-policy)
  for measured costs and cache storage limits.
- Enforce regex limits on compiled reference targets while preserving unused
  annotations and literal data.
- Gate the official required draft2020-12 corpus in CI with a frozen revision
  and explicit external-reference/custom-dialect exclusions.
- Move the rate-limit application error from the reserved MCP code `-32029`
  to `-33029`, retaining HTTP 429. Update clients that match the numeric code.
- Refresh the pinned official MCP source runner to the alpha.12 source plus
  PR #396; distinguish this commit from the published npm alpha.12 artifact.
- Add a coordinated-source OAuth-to-MCP integration fixture and exclude
  vulnerable Igniter/Mint dependency versions.
- Recover valid multi-round retries with missing answers by requesting only
  the remaining inputs. Preserve validated answers and the original expiry
  in fresh signed state, while rejecting unbound input and replay.

## 2.2.0 - 2026-09-13

- Allow a protected MCP endpoint to retain its canonical RFC 9728 resource in
  metadata while using Attesto core's explicit `:trusted_audiences` policy for
  access tokens issued to trusted reverse-gateway identifiers. Subscription
  delivery now reuses the same audience policy.

## 2.1.0 - 2026-09-03

- Add staged URL elicitation support for modern interactive requests in the
  `2026-07-28` protocol revision (`AttestoMCP.Server.UrlElicitation` and
  `AttestoMCP.Server.API` helpers: `stage_url_elicitation/5`,
  `resolve_url_elicitation/3`, `consume_url_elicitation/3`).
- Add `AttestoMCP.Server.UrlElicitationStore` behaviour with built-in
  in-process ETS adapter (`AttestoMCP.Server.UrlElicitationStore.ETS`) and
  optional PostgreSQL Ecto adapter (`AttestoMCP.Server.UrlElicitationStore.Ecto`).
- Implement atomic single-use consume semantics with foreign subject precedence
  to prevent cross-user state disclosure.
- Add periodic cleanup sweep of expired URL elicitations running alongside session
  cleanup with dedicated start, stop, and failure telemetry events.
- Make `mix attesto_mcp_server.gen.migration` write a second migration,
  `create_attesto_mcp_url_elicitations`, and skip each file whose base name
  already exists, so an existing host that reruns the task receives only the
  new table.
- Document `mode: "url"` elicitations in the usage guide: the link must carry
  the staged approval by opaque id, never as a bare route, and the staged
  record is subject-bound, single-use, time-limited, an explicit dead end when
  expired, consumed, or foreign, and swept. The section also states which
  operations deserve a browser gate and why the library does not refuse the
  mode by principal type.

## 2.0.1 - 2026-09-02

- Bind automatic protected-resource callbacks to the named profile loaded from
  the helper's `otp_app` argument on `attesto_phoenix` 3.x, keeping multiple
  named profiles isolated. The helper does not select a profile from
  `conn.private`. The `attesto_phoenix` 2.14 compatibility path supports one
  globally configured profile; using it for multiple profiles can route
  persistent replay or revocation reads through that global profile's store.

## 2.0.0 - 2026-09-02

- Make `PhoenixParser` treat each configured path as a decoded segment prefix:
  the prefix, route-equivalent trailing or repeated slashes, and every child
  path bypass host body parsing before authentication. This includes ordinary
  routes below the configured prefix, so unrelated routes must not overlap it.
  Malformed, oversized, multipart, and otherwise unsupported child requests
  reach the authenticated MCP boundary without prior body parsing.
- Add a validated Plug `client_ip` callback for authenticated and
  failed-authentication rate-limit buckets. The default remains
  `conn.remote_ip` verbatim, including non-IP local-peer terms; explicit
  callback failures fail closed and emit neutral `client_ip/exception`
  telemetry.
- Require `session_clustered: true` servers to configure the same explicit,
  shared `cursor_secret` on every node; an omitted or shorter-than-16-byte
  secret now refuses startup instead of generating incompatible per-process
  cursor keys. Load-balanced multi-node deployments serving stateless
  `2026-07-28` requests must also configure the same explicit secret, even when
  `session_clustered` is false, if cursors may cross nodes. Pagination cursors
  now bind to deterministic visible catalog content rather than node-local
  mutation history, so peers on the same OTP major with the same final catalog,
  secret, and pagination context can continue them across nodes even when their
  registration histories differ. A mixed-OTP-major rolling upgrade may
  invalidate in-flight cursors, so clients should restart pagination when that
  boundary is crossed.
- Emit periodic session-cleanup start/stop telemetry with a bounded reaped
  count and neutral outcome. Custom session-store cleanup responses must be
  valid batches of at most 1,000 keys; oversized or malformed responses are
  rejected as unavailable rather than truncated. Cleanup duration covers the
  store cleanup call and its bounded return normalization, failure reporting,
  and namespace filtering, but excludes start telemetry, local stream closing,
  and clustered close broadcast.

## 1.1.0 - 2026-09-01

- Add `AttestoMCP.Server.Result.tool_from_context/2,3` so tool handlers and
  shared adapters inherit the supervised server's JSON budget and output
  canonicalization settings without repeating or overriding those options.
  The standalone result constructors and their secure defaults remain
  unchanged.

## 1.0.0 - 2026-09-01

- Establish the stable 1.0 host API while preserving the strict output and
  string-keyed argument defaults from 0.14.
- Add a bounded `principal_binding` callback so handlers can receive complete
  loaded principals while session ownership, subscription isolation, cursors,
  and accounting use a small stable identifier. Add a separate post-auth,
  pre-body Plug `authorize` gate for mount-wide policy with neutral 403
  denials.
- Let `AttestoMCP.Server.Phoenix.protected_resource_options/2` compose a
  post-load principal callback without replacing AttestoPhoenix JTI revocation
  checks or principal loading.
- Add explicit `:json` and `:jason` tool-output canonicalization modes for
  protocol-encoded structs and atom values, retaining bounded input, encoded,
  decoded, and final wire validation. Trusted exception reporters receive a
  value-free JSON Pointer diagnostic when canonicalization fails.
- Add opt-in schema-limited atom keys for tool arguments. Conversion happens
  only after input validation, uses existing atoms, and never creates atoms
  from schemas or client data.
- Add `AttestoMCP.Server.Test.call_tool/4` for focused host tests that exercise
  scopes, definition policy, schemas, handler execution, and complete wire
  results through normal dispatch.
- Support up to 32 exact MCP paths in `PhoenixParser` and document a complete
  multi-server Phoenix deployment with distinct durable-session namespaces.
- Add bounded, cursor-based active legacy session ID pages for operator
  tooling. Bundled ETS and Ecto stores expose IDs without loading records or
  principal bindings; Ecto uses deterministic bytewise ordering.
- Add a public migration guide for moving documented `anubis_mcp` component,
  response, context, scope, and schema APIs onto AttestoMCP registrations.

## 0.14.0 - 2026-09-01

- Add an optional PostgreSQL-backed Ecto session store for the session-bound
  `2025-11-25` and `2025-06-18` revisions. Composite, collision-resistant
  server namespaces,
  row-locked updates, indexed expiry, and bounded cleanup let sessions survive
  application restarts and remain safe across nodes sharing one database.
- Make the Phoenix installer select durable session storage automatically when
  it finds exactly one statically confirmed, supervised PostgreSQL host Repo.
  It preserves existing custom choices, leaves Repo-free hosts on ETS, and
  falls back to ETS with an actionable notice when a sole Repo cannot be proven
  supervised or its adapter cannot be proven. It also resolves literal aliases
  in application and config source, refuses unsupported explicit or ambiguous
  Repo choices, and supports explicit `--session-store`, `--repo`, and
  `--schema-prefix` choices.
- Add `mix attesto_mcp_server.gen.migration` for the fixed
  `attesto_mcp_sessions` table. Generation is explicit and duplicate-safe;
  neither the installer nor the generator runs a database migration. Default
  paths follow the selected Repo's child application in umbrella projects.
- Keep Ecto optional so non-Ecto and stdio consumers retain the existing
  dependency footprint and in-memory default.
- Emit a neutral `[:attesto_mcp_server, :session_store, :failure]` event for
  each failed session-store call, keep adapter error details private, and
  preserve one `:session_store_unavailable` result through server, HTTP, and
  stdio boundaries.
- Bound Ecto query, transaction, and PostgreSQL row-lock waits below the server
  call budget. Record-bearing listings use small batches, counts use a SQL
  aggregate, and cleanup loads only bounded keys. Direct loads and
  record-bearing listings discard detected corrupt rows under lock with
  bounded telemetry so one unusable row cannot wedge later work.
- Keep session activity timestamps monotonic under concurrent refreshes and
  constrain session timeouts to `1..9_000_000_000_000_000_000` milliseconds.
  Malformed external session identifiers behave like absent sessions. Invalid
  stored record versions are discarded atomically, while rolling upgrades
  preserve higher integer record versions and bindings that a node cannot
  decode without creating atoms. Conditional corruption cleanup rechecks the
  current record under the adapter lock before deletion.
- Reject Ecto adapter operations that require their own row-locking transaction
  when called inside a caller-owned Repo transaction, before changing
  transaction-local timeouts, returning an explicit unsupported nesting error
  without poisoning the host transaction.
- In clustered mode, propagate explicit session deletion and periodic
  expired-row cleanup to peer processes with versioned, namespace-bound,
  size-limited control messages so every node closes its local streams without
  rebroadcast loops.
- Make session-store outages detected before response streaming return HTTP 503
  consistently across JSON-RPC POST, session-bound notification, GET, and
  DELETE paths. Configured HTTP method-scope checks and core method
  authorization precede outage disclosure. `Stdio.main/1` now stops the server
  it owns on exit and raises when its initial durable session cannot be stored.
- Make HexDocs open on the installer-first README so the Phoenix SaaS path is
  the package's default documentation entry point.

## 0.13.0 - 2026-08-31

- Require `attesto_mcp >= 1.3.0 and < 2.0.0`, adding compatibility with the
  Attesto 2.x authorization contracts.
- Accept direct public-Hex `attesto_phoenix` requirements from the supported
  2.14.1 floor through the 3.x line, retaining fail-closed intersection and
  dependency-source validation.
- Refresh path-based `Mix.install/2` examples so an existing install cache
  cannot retain dependency versions from an earlier server release.

## 0.12.2 - 2026-08-30

- Reject `2026-07-28` request metadata paired with missing, duplicate, or
  mismatched HTTP mirror headers before session-bound routing, preserving one
  neutral `body_header_mismatch` response across methods and stale session IDs.
- Add a runnable Livebook walkthrough for an authenticated local server,
  including discovery, initialization, tool listing, and a tool call.
- Add regression coverage proving late-bound named-server transport limits use
  the active server JSON budget, including absent-server startup and server
  restarts above the secure 2 MB default.
- Make the real Bandit disconnect and stdio interleaving regressions
  independent of peer-request and EOF-drain timing on loaded test hosts.
- Document the complete modern mirror-header contract and streamline the
  README around the batteries-included Phoenix path, client onboarding, and a
  concrete non-Phoenix dependency setup.

## 0.12.1 - 2026-08-30

- Support bounded resource URI templates containing up to 16 separated simple
  or reserved path expressions and 32 globally unique variables. Reverse
  matching remains deterministic, rejects adjacent ambiguous expressions,
  encoded separators for simple variables, traversal, and oversized captures,
  and is shared by direct, batch, startup, and replacement registration paths.
- Bound URI-template work across the complete candidate scan for one resource
  lookup. Budget exhaustion is distinct from a non-match, fails closed for
  selected resource dispatch and notification scopes, and a local resource
  notification reuses one scope resolution for modern and legacy delivery.
- Limit the complete primitive catalog to 1,000 definitions across all types.
  Atomic batches and replacements, repeated additions, startup, and registry
  recovery enforce the same total without mutating the catalog or revision on
  rejection.
- Add the server-wide `max_json_bytes` budget. The secure default remains
  2,000,000 bytes; hosts may explicitly raise it to a finite maximum of
  64,000,000 bytes. The selected budget now governs JSON Schema instances,
  handler-result normalization, final result validation, JSON-RPC encoding,
  Streamable HTTP, and stdio. Transport limits may not exceed their supervised
  server's budget. Registered definition and schema data use that same budget
  during startup, dynamic registration, catalog replacement, and restoration.
- Cap omitted HTTP transport limits by the selected JSON budget, including
  budgets below the 2,000,000-byte nominal body default. Explicit limits above
  the selected budget still fail during initialization.
- Make invalid low-level JSON-RPC `max_bytes` options fail closed for decoding
  and ID recovery instead of relying on Erlang term ordering.
- Document the stdio adapter's 512-byte minimum frame limit alongside its
  server-budget ceiling.
- Clarify that `Schema.max_instance_bytes/0` denotes the secure default rather
  than the configurable ceiling, and that `nil` and `true` schemas remain
  subject to the active JSON budget.
- Allow the public content and result constructors to opt into the same larger
  finite budget with `max_json_bytes`, while retaining the 2,000,000-byte
  default and all structural and canonical-Base64 checks.

## 0.12.0 - 2026-08-29

- Add bounded `alternative_scope_sets` to primitive definitions. The primary
  `required_scopes` clause and each alternative remain all-of requirements,
  while any complete clause can authorize the definition consistently across
  catalogs, selected lookups, resource templates, subscriptions, and both
  protocol eras.
- Add typed, strict `AttestoMCP.Server.Content` constructors and complete tool
  and resource constructors in `AttestoMCP.Server.Result`. They emit canonical
  string-key maps and enforce the same content, URI, JSON, and padded-Base64
  rules as handler-output validation; handwritten maps remain supported.
- Revalidate every finalized success result after adding protocol, cache, and
  server-identity fields. Oversized completion values and aggregate catalog or
  handler results now fail closed with bounded protocol errors in both eras.
- Align handler-output validation with the MCP content contracts. Embedded
  resources now require one text or blob resource-content entry rather than a
  nested `contents` array. Present optional string, map, list, and size members
  must now have their declared types, so explicit null text/blob siblings,
  null MIME/description/metadata members, malformed link titles, negative link
  sizes, and invalid link/resource icons or annotations are rejected. Use the
  public content constructors when migrating affected handlers.
- Document that JSON Schema defaults are annotations, and add the bounded,
  explicitly invoked `AttestoMCP.Server.Schema.apply_property_defaults/2`
  helper for direct property defaults without changing dispatch behavior.
- Add atomic `replace_catalog/2` for bounded generated catalogs. Failed and
  no-op replacements leave the catalog revision unchanged; successful changes
  emit one list-changed notification for each affected advertised catalog
  category. Resources and templates share one category; completion-only
  changes emit none.
- Make the 2,000,000-byte schema-instance ceiling explicit across server,
  Streamable HTTP, and stdio configuration. Oversized `max_body_bytes` or
  `max_message_bytes` settings now fail during initialization, while the
  message limit continues to govern JSON-RPC decoding.
- Validate `scope_map` through one shared bounded policy: only supported MCP
  request methods are accepted, and scope lists must be unique, valid,
  length-bounded, and aggregate-byte-bounded in both server and Plug options.
- Allow resolver-backed `:auth` to supply a strictly validated canonical
  resource or base origin after runtime configuration loads. The resolved URL
  must agree with the mounted path and any static pin, and the same identifier
  drives RFC 9728 metadata and audience verification.
- Require static and resolved `:base_url`/`:origin` values to be bare HTTP
  origins without application paths. A static path-bearing value now fails
  during Plug initialization, and a resolver-returned value fails closed when
  the resolver is evaluated, instead of producing protected-resource metadata
  that disagrees with the served route.
- Add a migration runbook for registrations, handler contracts, schema
  behavior, authorization ordering, Phoenix wiring, session continuity, and
  safe result construction.
- Bind clustered resource-notification delivery to a versioned, bounded
  publisher scope snapshot and the recipient's current definition scopes.
  Legacy cluster resource envelopes without publisher scopes fail closed, so
  catalog drift during deployment cannot weaken modern or legacy delivery
  authorization. Per-publish authorization callbacks now suppress both modern
  and legacy delivery consistently.
- Clarify the distinct modern and legacy subscription scope rules, including
  additive `subscriptions/listen` and per-notification requirements.
- Add explicit `2025-06-18` HTTP, stdio, initialization, and revision-filtering
  regressions, and make live stdio tests wait for owned child processes before
  removing isolated build directories.

## 0.11.1 - 2026-08-29

- Document the existing per-definition `authorize` contract, including callback
  forms, host context, evaluation order, neutral denial behavior, and the
  subscription boundary; test function and MFA callbacks on both allow and
  deny paths.
- Refuse non-isolated or uninspectable Phoenix endpoint sources before editing,
  distinguish those failures from safe installer warnings, and make metadata
  route reuse diagnostics specific without relaxing preflight.
- Clarify host-owned custom-scope configuration and pin parser bypass behavior
  for route-equivalent repeated trailing slashes.

## 0.11.0 - 2026-08-28

- Add per-mount RFC 9728 `scopes_supported` configuration. Custom-scope
  deployments no longer have to advertise the package's generic scopes and
  cause clients to request grants the authorization server does not issue.
- Add isolated host context builders, bounded server-wide telemetry dimensions,
  exact primitive identity on handler spans, host exception reporting, and a
  handler-task initialization callback with function and MFA forms.
- Add explicit bounded client-safe handler failures with
  `AttestoMCP.Server.Result.error/2`; arbitrary failure terms and exceptions
  remain private.
- Add host-wide `default_scopes` with explicit method-map precedence and
  additive subscription delivery scopes.
- Add atomic bounded `register_all/2`, startup `:registrations`, coalesced
  catalog invalidations, exact registry recovery, and stable named child specs.
- Add a durable legacy-session adapter contract, versioned bounded records,
  fail-closed store monitoring, and an in-memory default adapter with bounded
  fair cleanup, including immediate cleanup bookkeeping removal on explicit
  deletion. Optional clustered session mode uses one explicit shared store and
  namespace and fans notifications out to live peers; process-backed streams
  reconnect after failover, and transient store reads do not mislabel live
  streams as expired.
- Add opt-in HTTP definition-scope policies for filtered tools/resource
  catalogs and selected tool/resource calls. Selected definitions are bound
  before MRTR retry-state consumption and authorized through the prepared
  Attesto boundary; hidden and denied definitions remain neutral, while the
  existing generic scope defaults and subscription generic-plus-definition
  reauthorization remain unchanged.
- Make AttestoPhoenix Client ID Metadata Documents an explicit installer opt-in;
  existing cache, repository, prefix, allowlist, and disabled settings remain
  authoritative, and Req is required only for that opt-in.
- Reuse only one statically proven published
  `attesto_routes(protected_resource_paths: [path])` metadata route for the
  selected MCP path, preserving it byte-for-byte and adding only MCP wiring.
- Preflight Phoenix endpoint parser provenance and add a literal static-route
  bypass, including route-equivalent trailing slashes, only for a direct
  standard `Plug.Parsers` declaration; custom or ambiguous body readers receive
  an actionable refusal.
- Require header-only bearer presentation for both static and resolved auth,
  require successful tools with `outputSchema` to return conforming structured
  content even under permissive schemas, reject ungrantable definition scopes,
  redact private server options from crash status, and ignore unrelated calls,
  casts, and mailbox messages without terminating the server.

## 0.10.6 - 2026-08-28

- Apply the automatic AttestoPhoenix access-token JTI revocation check and
  principal loader when authenticating each MCP request or stream, before
  request-body reads, returning a neutral invalid-token response when either
  rejects.
- Treat configured and canonical authentication assigns as boundary-owned:
  clear them before verification, repopulate them only from verified results,
  and reject non-atom, nil, boolean, duplicate, or cross-owned assign-key
  configurations at Plug initialization. Verifier exceptions also retain the
  cleared boundary.
- Emit a payload-free `[:attesto_mcp_server, :auth, :policy_failure]` telemetry
  event when the automatic Phoenix revocation check returns an invalid result
  or its revocation/principal callback or the HTTP verifier raises, throws, or
  exits.
- Fix modern and legacy DPoP- and mTLS-bound subscription delivery with loaded
  opaque principals, including static auth configurations that use custom
  assign keys. Delivery re-verifies the captured token and sender binding
  without running host policy callbacks in shared publish processes; later
  revocation or principal-policy changes take effect on the next request or
  stream reconnect.
- Require the oldest published compatible AttestoPhoenix release, 2.14.1,
  when the installer reuses its protected-resource policy.

## 0.10.5 - 2026-08-28

- Carry the host's current AttestoPhoenix protected-resource adapter options
  into generated MCP routes, including DPoP replay/nonce, canonical-request,
  and mTLS certificate callbacks, instead of retaining only the core verifier.
- Resolve automatic AttestoPhoenix authentication at request time while
  keeping the canonical resource and public origin statically pinned. Resolver
  failures fail closed; on protected MCP requests, unavailable servers,
  oversized headers, and unsupported methods are rejected before runtime
  configuration is requested.
- Add a constrained runtime auth resolver contract for external zero-arity
  callbacks and portable-literal MFA values. Generated routes remain safe for
  Phoenix compile-time Plug initialization and cannot replace package-owned
  assigns or advertise unsupported bearer-token locations.
- Migrate only exact legacy installer-owned AttestoPhoenix forwards, including
  interrupted mixed old/new installations, while preserving application tool
  registrations, refusing near matches, and leaving nested quoted examples
  untouched.

## 0.10.4 - 2026-08-28

- Preserve Attesto's scheme-correct OAuth challenges and error details for
  invalid DPoP proofs, sender-bound tokens presented as Bearer, nonce requests,
  and insufficient scopes instead of replacing them with a generic Bearer
  response.
- Keep anonymous discovery challenges error-free, pin challenge metadata to the
  configured canonical resource origin, and cover header and host-owned
  credential paths with DPoP regressions.

## 0.10.3 - 2026-08-28

- Configure detected `attesto_phoenix` hosts for URL client metadata and native
  loopback clients by enabling Client ID Metadata Documents and ephemeral
  `localhost` callback ports without replacing existing host choices.
- Add the compatible Req dependency required by the default secure CIMD
  fetcher only on the `attesto_phoenix` installer path; explicit Attesto
  callback installations remain dependency-neutral.
- Harden dependency preflight to inspect only literal catalogs, intersect
  stable Hex requirements with the supported `attesto_phoenix` and Req ranges,
  and stop without edits on dynamic, duplicate, incompatible, restricted,
  custom-source, or otherwise unsafe declarations.
- Harden route and ownership recognition across forward arities 2–4, qualified
  and effective scoped calls, source-ordered/chained/grouped aliases, dynamic
  plug refusal, Phoenix-router identity, exact generated callbacks, fully
  qualified generated routes, and rooted generated module references.
- Fail closed on executable managed-module attributes, compile hooks, custom
  macro enablement, enclosing-file compiler directives, unsafe nested modules,
  and non-canonical endpoint paths; installer and runtime paths now use only
  slash-separated ASCII URI-unreserved segments.
- Preserve compatibility with the exact zero-option `AttestoPhoenix.Router`
  integration emitted after a trusted primary Phoenix router use while keeping
  every other secondary router macro source opaque and requiring router modules
  to own their source file for lexical provenance.
- Normalize atom cache-scope options, expose pagination and cache controls
  through the stable facade type, and keep stdio-only controls out of owned
  server startup.
- Exercise the combined installers in both the reusable fixture and an actual
  Phoenix 1.8.13 `mix phx.new` application, including config construction,
  generated routes, warnings-as-errors compilation, starter tests,
  authorization-installer reruns, and a zero-diff second MCP installation.
- Harden subscription ownership and cleanup, contain non-exception Plug
  failures, serialize first-use cursor secrets, and exercise prompt retrieval
  through both dated HTTP fixture paths.
- Enforce prompt and resource completion scopes at the authenticated HTTP
  boundary, including one explicit method override for both reference types.
- Preserve evaluated array-item annotations when JSON Schema references are
  reached through applicators, avoiding false rejection by `unevaluatedItems`.
- Reject non-object request `_meta` values as correlated protocol errors while
  keeping both stdio and HTTP transports available for later requests.
- Bound each legacy session and each modern resource filter to 128 unique
  resource URIs at 4,096 bytes per URI, detach retained URI slices from request
  bodies, and canonicalize duplicates without extra entries.
- Route every supported resource-update alias through the same legacy URI
  filter and avoid extending idle lifetime for rejected subscription changes.
- Reject malformed public notification fields before modern or legacy fanout,
  and suppress malformed direct subscription events without terminating the
  registry or preventing a later valid delivery.
- Detach accepted binary JSON-RPC and modern subscription IDs from larger
  decoded request buffers before retaining them.
- Refuse installer edits when existing static, parameterized, glob, resource,
  or forwarded routes overlap either generated mount; reject required macro
  calls whose router effects cannot be proven and accept callback punctuation
  only as a terminal Elixir identifier suffix.
- Bind pagination cursors to their catalog method and skip explicit
  owner-local subscription IDs when allocating server-generated identifiers.
- Add direct regressions for HTTP Origin enforcement and header budgets, plus
  a live stdio pipe recovery check spanning malformed, oversized, and valid
  consecutive frames.

## 0.10.2 - 2026-08-27

- Document the distinct tool, prompt, resource, and completion callback inputs,
  including prompt and resource envelopes, their declared atom-keyed fields,
  and resource MRTR retry entries.
- Add copyable prompt callback guidance after a live-client interoperability
  exercise exposed the ambiguity in the earlier generic callback wording.
- Derive the default advertised server version from package application
  metadata so patch releases cannot retain a stale hard-coded version.

## 0.10.1 - 2026-08-27

- Accept JSON request bodies already decoded by a Phoenix or Plug parser while
  retaining message-size, nesting, JSON-RPC validation, and recoverable-ID
  checks.
- Add Phoenix-style parser-pipeline and pass-through parser regressions using a
  current legacy-initialize client request.

## 0.10.0 - 2026-08-27

- Add an optional, idempotent Igniter installer for Phoenix hosts.
- Scaffold a supervised MCP module, protected `/mcp` and RFC 9728 metadata
  routes, runtime configuration, and starter tests without inventing secrets or
  application authorization policy.
- Detect `attesto_phoenix` hosts and reuse their validated Attesto configuration
  while keeping authorization-server concerns out of this package.
- Use a generated application-owned metadata wrapper so Phoenix 1.7 can mount
  metadata and protocol routes as distinct forwarded plug modules.
- Refuse unsafe origins, ambiguous router/module collisions, and partial edits;
  compare existing routes across nested scopes and aliases, refuse dynamic
  forwards and Phoenix 1.7 plug reuse, and preserve a zero-diff second run
  across the supported Igniter range.

## 0.9.0 - 2026-08-26

- Accept and exactly echo MCP `2025-06-18` during legacy initialization while
  retaining `2025-11-25` and preferred `2026-07-28` behavior.
- Preserve the negotiated legacy revision in HTTP session state and enforce it
  on later protocol-version headers.
- Add stdio initialize/list/call coverage and authenticated HTTP lifecycle
  regression coverage for `2025-06-18`.
- Isolate the live stdio regression's installation cache so stale build state
  cannot contaminate protocol stdout startup.
- Use the negotiated session revision when a legacy HTTP client omits the
  protocol-version header, while rejecting invalid or changed header values.
- Gate catalog/content icons, explicit elicitation modes, and sampling tool
  selection to revisions that define those fields.
- Reject an initialize protocol-version header when its revision was disabled
  by the host's `protocol_versions` configuration.
- Expose the exact negotiated legacy revision as `context.protocol_version` to
  request handlers.

## 0.8.0

Initial Apache-2.0 release of the Attesto-native dual-era MCP server.

- Require released `attesto_mcp ~> 1.2` and use its public prepared resource
  protection contract without sibling-path or older-auth fallbacks.
- Preserve unknown JSON-RPC extensions and add precise recursive message types.
- Isolate same-session legacy streams, prefer the newest live stream for
  server-originated client requests, wait briefly for an owned stream during
  connection startup, and make stale teardown ownership-safe.
- Make cancellation, detached-owner cleanup, nil-ID work, correlation, and
  request terminal telemetry exact-once under races.
- Add frozen official conformance evidence and exact TypeScript 2.0.0 and
  Python 2.1.1 client smoke gates for both supported eras.
