Upgrade Guide

This guide covers user-visible migration steps between MCP Hangar releases.

Upgrade to 1.5.0

MCP Hangar 1.5.0 adds a one-time admin bootstrap, a configurable command-bus rate limit, the interceptor invocation surface with phase-aware hooks, task-lifecycle audit events, and a per-tenant discovery entry point. It also fixes OIDC bearer authentication over the HTTP surface. Upgrade is drop-in (pip install -U mcp-hangar==1.5.0, or pull ghcr.io/mcp-hangar/mcp-hangar:1.5.0); the notes below cover the behavior changes worth reviewing.

OIDC bearer auth over serve --http now works

If you configured OIDC/JWT front-door auth (auth.oidc) on the HTTP server in 1.4.x and every request returned 401 with auth_method: none even for a valid token, that was a header-casing bug in the JWT authenticator -- it is fixed in 1.5.0. No config change is needed; existing auth.oidc config now authenticates bearer tokens as intended.

Bootstrap the initial admin

A fresh durable auth store with anonymous access disabled could not create its first administrator through the protected API. mcp-hangar auth bootstrap-admin --config PATH --principal PRINCIPAL now grants the one-time global admin role to an existing external (OIDC) principal using the server's own durable backend. It fails closed when auth is disabled, anonymous access is allowed, or the store is non-durable (memory / event_sourcing), and a second run is refused without mutating storage. No secret is printed.

Behavior changes to review

  • Tool isError results now count as failures. A backend MCP tool result with isError: true is treated as a tool failure -- reflected in the per-call result, batch succeeded/failed counts, health, and ToolInvocationFailed events. If you previously treated error results as successes, expect failure counts to rise.
  • The SQLite event store fails fast. When a durable event store cannot be initialized (path not writable / backend unavailable), Hangar now refuses to start instead of silently degrading to a non-durable in-memory store. Opt into the fallback with event_store.driver: memory or event_store.allow_memory_fallback: true. /health/ready returns 503 if the store degraded to in-memory while a durable driver was configured.
  • Group circuit breaker. A tripped circuit breaker on one group member no longer blocks a healthy remaining member from serving.
  • Command-bus rate limit is configurable. The previously-fixed command-bus rate limit can now be tuned in config.yaml; review the configuration reference if you relied on the old fixed value.

Upgrade to 1.4.0

MCP Hangar 1.4.0 builds on the 1.3 front-door release. It adds tenant-scoped digest pins, multi-issuer OIDC trust, resource-bound JWT audiences, and per-tenant canary routing for MCP server groups.

Review OIDC audience binding

If auth.oidc.resource_uri is set, it now becomes the expected JWT aud value for every trusted issuer. This aligns token validation with the RFC 9728 Protected Resource Metadata resource value and RFC 8707 resource indicators.

Before upgrading production front-door deployments:

  • Confirm the authorization server issues tokens with aud equal to auth.oidc.resource_uri.
  • If you need legacy audience values per issuer, leave resource_uri unset and configure audience on each issuer instead.
  • Prefer setting resource_uri behind proxies; otherwise Hangar derives the resource from the incoming request scheme and host.

Move multi-issuer deployments to auth.oidc.issuers

Single-issuer config still works:

auth:
  oidc:
    enabled: true
    issuer: https://issuer-a.example.com
    audience: mcp-hangar

Use auth.oidc.issuers when one Hangar instance trusts multiple authorization servers:

auth:
  oidc:
    enabled: true
    resource_uri: https://hangar.example.com
    tenant_claim: tenant_id
    issuers:
      - issuer: https://issuer-a.example.com
        audience: https://hangar.example.com
        jwks_uri: https://issuer-a.example.com/jwks
      - issuer: https://issuer-b.example.com
        audience: https://hangar.example.com
        jwks_uri: https://issuer-b.example.com/jwks
        groups_claim: roles

Tokens with a missing, empty, non-string, or untrusted iss claim now fail closed with a 401 instead of reaching any issuer validator.

Add tenant-scoped digest pins intentionally

1.4.0 can enforce schema pins per tenant on the live invocation path:

mcp_servers:
  payments:
    mode: remote
    endpoint: https://payments.example.com/mcp
    tool_projection:
      digest_enforcement: block
      tenant_overrides:
        "tenant:a":
          pins:
            refund: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef

digest_enforcement is scoped per MCP server. If unset, pin mismatches default to block. Roll out pins with audit or warn first when you are recomputing digests or onboarding a new tenant.

The deprecated allow_degraded unknown-tool policy is still accepted with a DeprecationWarning in 1.4.0, but allow_unverified remains the canonical value. Do not add new allow_degraded config.

Gate canary routing by tenant identity

Group canary routing only applies when Hangar has a tenant_id for the caller. Explicit tenant pins win first, then the sticky percentage split, then the regular load-balancing strategy.

mcp_servers:
  search:
    mode: group
    strategy: weighted_round_robin
    canary:
      member: search-v2
      split_pct: 10
      pinned_tenants:
        "tenant:beta": search-v2
    members:
      - id: search-v1
        mode: remote
        endpoint: https://search-v1.example.com/mcp
      - id: search-v2
        mode: remote
        endpoint: https://search-v2.example.com/mcp

Invalid canary targets are skipped with a warning. If a pinned or canary member is not in rotation, Hangar falls back to the group load balancer instead of routing traffic to an unhealthy member.

Upgrade to 1.3.0

MCP Hangar 1.3.0 relicenses the project to MIT, folds the former enterprise package into the main mcp_hangar package, and tightens digest-pinning canonicalization.

Recompute pinned tool digests

compute_tool_digest() now uses RFC 8785 JSON Canonicalization Scheme (JCS) instead of json.dumps output. Existing pinned digests produced by earlier versions may no longer match and must be regenerated before enforcement mode is enabled.

When recomputing digests, note the v1.3 normalization rules:

  • None, {}, [], and "" are treated as absent values.
  • Tool entries with a missing, empty, or non-string name field are rejected.

Recommended rollout:

  1. Upgrade one environment with digest enforcement set to audit or warn.
  2. Collect the new expected digests from the audited tool inventory.
  3. Replace old pins with the RFC 8785/JCS digests.
  4. Re-enable block only after the audited drift is resolved.

Rename ALLOW_DEGRADED to ALLOW_UNVERIFIED

DigestUnknownPolicy.ALLOW_DEGRADED was renamed to DigestUnknownPolicy.ALLOW_UNVERIFIED for clarity.

If your YAML or code uses the string value allow_degraded, change it to allow_unverified. MCP Hangar 1.3 still accepts allow_degraded with a DeprecationWarning; 1.4.0 still accepts the alias, but new configuration should use only allow_unverified.

Remove license-tier assumptions

The former BSL/enterprise split is gone. All MCP Hangar features are now available under the MIT license.

Required cleanup for integrations and deployment manifests:

  • Stop setting HANGAR_LICENSE_KEY; v1.3 ignores it and emits a DeprecationWarning when present.
  • Remove checks for LicenseTier, LicenseValidation, or ApplicationContext.license_tier.
  • Update imports that referenced the former enterprise/ package; auth, compliance, approvals, integrations, and persistence modules now live under src/mcp_hangar/.

Verify interceptor discovery clients

interceptors/list now returns unique instance names required by SEP-1763:

  • mcp-hangar-validator
  • mcp-hangar-mutator

If a client keyed both entries by the previous shared name mcp-hangar, update it to handle the two explicit instance names.