github agentic-community/mcp-gateway-registry 1.33.0
1.33.0 - Virtual-Server Credential Brokering and Root-Endpoint Routing

3 hours ago

Release 1.33.0 - Virtual-Server Credential Brokering and Root-Endpoint Routing

October 2026


Upgrading from 1.32.1

This section covers everything you need to know to upgrade from 1.32.1 to 1.33.0.

Breaking Changes

Every connected egress account must be reconnected once.

Egress credential binding is now tighter. A vaulted credential used to be bound to a destination origin (https://api.example.com); it is now bound to the exact outbound URL (https://api.example.com/mcp), and the set of approved destinations is captured when consent begins rather than re-read when the provider redirects back. The second part closes a window in which a server version added while the user was away at the provider could inherit an approval the user never saw.

The consequence is a one-time reconnect. Credentials written before this release carry the origin-only form, which no longer matches the exact URL, so the vend treats them as a miss and falls back to its normal re-consent path. There is no migration step, and nothing is lost:

  • An oauth_user server returns the usual connect prompt. The user approves once and the credential is rebound.
  • A pat server returns the "submit a PAT" instruction. The user resubmits it once on the Connected Accounts page.
  • A designated discovery identity must also reconnect. Until it does, its server reports unhealthy discovery, which is expected rather than an outage.

This affects any deployment with EGRESS_AUTH_ENABLED=true, not only those using virtual servers. Operators should expect connect prompts across the fleet on the first calls after the upgrade, and should tell users to expect one.

Further detail, including how to tell this apart from a real binding failure, is in the egress credential vault guide.

Other upgrade notes

Internal nginx location names change for some virtual-server backends. Internal names are now derived from an injective encoding of the registered path, so two registrations can no longer collide on one internal location. Any backend whose path contains -, . or _ gets a new internal name. Each live client session re-initializes those backends once, stateful backends receive a fresh Mcp-Session-Id, and the previous session records age out through their TTL index. Registered virtual servers, tool mappings and client-facing URLs are unchanged.

A virtual server now advertises a per-server protected-resource document on Microsoft Entra ID. This is what makes an MCP client able to complete the ingress login for a /virtual/<id> path at all on Entra. Operators running Entra should add each enabled virtual server's resource URL to the gateway application's identifierUris; GET /api/egress/obo-identifier-uris now returns virtual servers alongside registered ones, so it gives the exact set. Keycloak and Cognito deployments are unaffected and stay on the gateway-wide document.

Helm upgrades no longer rotate the Keycloak client secrets. The keycloak-configure Job asked Keycloak for a fresh web and M2M client secret on every run. Pods read Kubernetes Secrets only at startup, so an upgrade left the running auth server holding a credential that no longer existed and browser OAuth code exchange failed with a 401, while a direct password-grant check against the newly written Secret still passed. The Job now reads the existing credentials and synchronizes the Secret without rotating. If you had a habit of re-running a credential fetch script after every upgrade, you can drop it. Deliberate rotation is now a separate, coordinated action rather than a side effect of upgrading.

Permission grants written with a leading slash now take effect. The server access check compared a request path with its slashes stripped against grants stored exactly as written, so a grant recorded as /my-server never matched. Grants are now compared in canonical form. On an upgraded deployment some groups will gain access they were already supposed to have, and any duplicate entries added as a workaround (my-server alongside /my-server) become redundant and can be removed.

A Keycloak realm needs its dynamic-registration policies reapplied, and already-registered clients reviewed. Two limits now apply to a client that registers itself through RFC 7591 Dynamic Client Registration: it cannot request a client scope that confers gateway access, and it cannot define its own protocol mappers. Both are realm policy, so they take effect when the realm configuration is reapplied, not when the images roll. On Helm that happens automatically the next time the keycloak-configure Job runs; on Docker Compose and ECS, run bash keycloak/setup/upgrade-realm-for-dcr.sh, which reports the resulting allowlist so you can check it.

Reapplying the policies does not revoke a scope a client was already granted, because Keycloak records the grant on the client at registration. After the upgrade, review the realm's dynamically registered clients, which are the ones with a UUID-format client ID, and remove any that hold a scope ending in /read or /execute, or named mcp-registry-admin or registry-admins. bash keycloak/setup/cleanup-stale-dcr-clients.sh --dry-run lists the candidates; it skips any client with an active session, so a client currently in use needs to be removed by hand. The MCP client re-registers on its next connection and the new record is subject to the policies.

Discovery on a virtual server now costs more internal requests. Each tools/list authorizes every mapped backing server and checks its per-tool block state, so a virtual server fronting several backends issues several internal requests per discovery call where 1.32.1 issued roughly one. Measured on a seven-backend virtual server in steady state: 12 GET /validate to auth-server and 7 block checks to the registry, 19 in total. These are internal hops rather than inbound traffic, so they do not consume the edge limit_req, but they do add auth-server and registry load per discovery call and count against a backing server's own rate limit. Size accordingly if you run virtual servers with many backends.

An A2A agent in reverse-proxy mode now needs a declared interface that actually matches its backend. Registration and updates are rejected when supportedInterfaces declares no JSONRPC or HTTP+JSON binding at the agent's registered backend URL. An agent already registered before this release with a mismatched interface is not touched retroactively, but its live agent-card fetch through the gateway, and its advertised interfaces on read, were already inaccurate before this release and will now show fewer interfaces, or a 502 on the live card fetch, rather than the earlier misleading result. Re-register the agent with a correct interface, or disable reverse proxy for it, to clear this.

New Environment Variables

No new environment variables in this release.

Upgrade Instructions

Docker Compose

cd mcp-gateway-registry
git pull origin main
git checkout 1.33.0

# No new environment variables in this release, but review .env.example
# against your .env if you are skipping intermediate versions.
./build_and_run.sh

Kubernetes / Helm (EKS)

cd mcp-gateway-registry
git pull origin main
git checkout 1.33.0

# REQUIRED: the release updates the image tags in every chart's values.yaml,
# and the packaged subchart .tgz files are not in git, so a plain
# `helm upgrade` would deploy the previous release's images.
cd charts/mcp-gateway-registry-stack
helm dependency build
helm dependency update

# Update values.yaml if needed, then upgrade:
helm upgrade mcp-gateway . -f your-values.yaml

Terraform / ECS

cd mcp-gateway-registry
git pull origin main
git checkout 1.33.0

# The release updates the image defaults in variables.tf; review the plan
# before applying.
cd terraform/aws-ecs
terraform plan
terraform apply

Major Features

Virtual servers can front egress-brokered backing servers

A virtual MCP server aggregates tools from several registered servers behind one endpoint. Until now it could not front a backing server that needs a per-user credential: the router dispatched the backend call straight to the registered upstream with the Authorization header cleared, so the credential vend and inject step was skipped and the backing server received a token-less request. Virtual servers and egress auth were effectively mutually exclusive.

The backend hop now goes through the same vend and inject step a direct call uses, keyed on the resolved backing path so each backend receives its own credential. The caller must hold a grant on both the virtual server and the backing server, checked against the exact rewritten request. The caller's own gateway token is still never forwarded to a registrant-controlled backend; the backend sees only the credential brokered for it.

Two backing servers with different credential modes can now sit behind one virtual server and each gets its own vend, which was the original motivation: splitting a self-hosted Atlassian server into separate Jira and Confluence registrations and aggregating them.

The same change also lets a virtual server front a stateless Streamable HTTP backend. The router previously assumed every backend returns a session ID on initialize; one that returns none produced a lookup that missed on every request. A successful session-less initialize is now recorded as its own state.

PR #1825

A root-endpoint server is reachable at the URL it is advertised under

A server registered with append_mcp_path: false serves MCP at its path itself, and that bare path is what the gateway publishes as the server's protected-resource document and Connect URL. Only the trailing-slash prefix location was rendered, so a POST to the advertised bare path received a 301 redirect to the slash form, which MCP clients refuse to follow on a POST.

The bare path now has its own exact-match location carrying the same authorization, rate limiting and header handling as the prefix form, and /validate accepts the registered path with no trailing slash. A sibling path that merely shares the prefix is still refused.

PR #1831, PR #1870


What's New

Security and Authorization

  • The keycloak-configure Job reuses the existing OAuth client secrets instead of rotating them on every upgrade, no longer prints bootstrap passwords to its own log, and passes Secret data over stdin rather than on kubectl arguments (#1869)
  • Server access grants are compared in canonical form, so a grant stored with a leading slash is honoured (#1869)
  • A2A Pull Card fetches from the registrant's backend rather than the advertised gateway route, and refuses a card that points back at the gateway (#1869)
  • Resource-scoped tokens and search visibility bind to canonical asset paths (#1869)
  • Egress credentials bind to the exact outbound URL, and the approved destination set is captured when consent begins rather than re-read at the callback (#1825)
  • A virtual server's backing grant is checked separately from the virtual grant, on the exact rewritten request, so a tool present in a backend map but absent from the virtual tool list is no longer callable (#1825)
  • Internal nginx location names use an injective encoding, so two registered paths can no longer collide on one internal location (#1825)
  • A token's groups claim is validated as a JSON array of strings by one shared validator across every identity provider, rather than each provider coercing other shapes. A claim arriving as a bare string was wrapped into a single-element list, which turned the string into a group membership (#1884)
  • A dynamically registered client can no longer request a client scope that confers gateway access, nor define its own protocol mappers, which decide what a claim contains (#1884)
  • The set of scopes withheld from dynamic registration is derived from the list that creates them rather than from a naming convention. Two scopes created only on the Helm surface, mcp-registry-admin and registry-admins, are named without the separator the convention relied on and were requestable there (#1885)
  • The agent tool loop pins its destination rather than building it from a model-supplied parameter (#1863)

Virtual Servers

  • A backing server's per-tool block is enforced consistently across the stored catalogue and the runtime, including for a tool pinned to a backend version, which previously failed rather than running (#1869)
  • Backend session state distinguishes a stateful session ID from a successful session-less initialize, and is isolated by owner and pinned version (#1825)
  • Discovery across backends classifies each one consistently, so one backend's outage or revoked credential no longer blanks its healthy siblings (#1825)
  • A virtual server advertises a per-server protected-resource document where the identity provider requires one, and appears in GET /api/egress/obo-identifier-uris (#1825)

A2A

  • Agent cards gain the A2A 1.0 supportedInterfaces, securityRequirements, and signatures fields, threaded through register, update (including the pull-card diff), and the batch item processor (#1866)
  • A2A reverse-proxy registration and updates are rejected when no declared JSONRPC or HTTP+JSON interface actually matches the registered backend URL, and only matching interfaces are projected onto the gateway on read (#1887)
  • The live agent-card fetch through the gateway runs as a content handler against a new internal-only nginx location instead of a body filter, so an unroutable live card now returns a genuine 502 instead of a misleading 200, and the internal fetch strips caller-supplied identity headers before reaching the backend (#1887)

Search

  • A search result's endpoint_url is built from the server's own append_mcp_path setting instead of always appending /mcp (#1814)

Identity Provider

  • GET /api/management/iam/groups no longer fails the whole page when the identity provider's own group listing errors; it logs a warning and falls back to the groups already tracked in the registry's scope documents (#1883)

Reliability

  • An unavailable server version is reported as 503 rather than 409, which invited a retry the caller could not act on (#1868)
  • One malformed server record can no longer abort an entire nginx render; the affected server is skipped and logged (#1868)
  • The AWS credential check is bounded so it cannot delay registry startup (#1863)
  • MCP_PROXY_TIMEOUT is wired into the prebuilt Compose file (#1820)

Custom Entities

  • is_enabled is writable on create and update, so a record can be switched off. A disabled record leaves semantic search, and a disabled proxied record also loses its gateway route (#1858)

Registration

  • POST /api/servers/register includes metadata in the registration gate payload (#1872)

Developer Experience

  • The test suite drops its per-session database instead of leaving one behind per run. These had accumulated to the point where MongoDB refused connections with "Too many open files" (#1871)
  • The release retag workflow watches the branch prefix the image-tag PR actually uses, so it no longer skips silently (#1857)
  • A rejected virtual-server version pin logs which linkage property failed, and the version-document id format has one definition instead of seven (#1879)

Documentation

  • api/openapi.json carries the custom-entity is_enabled field, and the custom-entities guide documents the proxy-route effect of disabling a record (#1877)
  • The agents guide corrects the comment above the server-path guard (#1863)
  • POST /api/servers/register is documented as returning a disabled server by default (#1886)

Bug Fixes

  • A2A reverse-proxy registration could accept a declared interface that did not match the gateway's backend route, and a live card fetch returned a misleading 200 instead of failing when no interface was actually routable (#1887)
  • Search's endpoint_url ignored a server's append_mcp_path setting and always appended /mcp (#1814)
  • GET /api/management/iam/groups returned a 502 for the whole page when the identity provider's group listing failed, discarding the groups already tracked locally (#1883)
  • A groups claim arriving as a bare string was coerced into a one-element list, making the string a group membership (#1884)
  • Two admin client scopes on the Helm surface were requestable by a dynamically registered client (#1885)
  • A Helm upgrade rotated the Keycloak client secrets, breaking browser login while health checks stayed green (#1869)
  • Permission grants stored with a leading slash matched nothing (#1869)
  • A2A Pull Card fetched the agent card from the gateway's own route (#1869)
  • A virtual-server tool pinned to a backend version returned an error instead of running (#1869)
  • Virtual-server tool calls reached backing servers without their per-user credential (#1825)
  • A root-endpoint server answered a POST to its advertised URL with a 301 (#1831, #1870)
  • /validate refused a registered path with no trailing slash (#1870)
  • An unavailable server version returned 409 Conflict (#1868)
  • The egress callback error page did not render when the provider rejected the code exchange (#1825)
  • is_enabled was silently dropped from custom-entity create and update request bodies (#1858)
  • The test suite leaked one MongoDB database per session, and one per worker under xdist (#1871)
  • MCP_PROXY_TIMEOUT was ignored by the prebuilt Compose file (#1820)
  • Metadata was dropped from the registration gate payload on server registration (#1872)

Closed Issues

Issue Title Closed By
#1867 Follow-up to #1825: release-note the egress credential reconnect, plus three small code items PR #1877 and this release note
#1815 Feature request / bug: IAM > Groups page returns 502 entirely when the IdP group listing fails, discarding locally-tracked groups that were fetched successfully PR #1883
#1558 search endpoint_url ignores append_mcp_path and always appends /mcp PR #1814
#1221 Add ATR as a portable rule-pack scanner backend alongside Cisco AI Defense manual
#1862 agents/agent.py: invoke_mcp_tool builds its destination from a model-supplied parameter and attaches the bearer token PR #1863
#1859 Metadata not included in registration gate payload for POST /api/servers/register PR #1872
#1607 feat: route virtual-server backend calls through the egress hop so egress-brokered servers can be aggregated PR #1825

Pull Requests Included

PR Title
#1887 fix(a2a): advertise only routable agent interfaces through the gateway
#1886 docs(registry): document that POST /api/servers/register comes back disabled
#1885 fix(keycloak): withhold admin scope names from the DCR allowlist
#1884 fix(providers): normalize group claims and fix dcr mapping
#1883 fix(registry): keep scope groups when the IdP group listing fails
#1880 docs: refresh the 1.33.0 notes for the latest merges
#1879 fix(registry): name the rejected version linkage, and centralize the version id
#1878 docs: add the 1.33.0 release notes draft
#1877 docs(api): refresh openapi.json for is_enabled, and document the proxy-route effect
#1874 chore(deps): bump the actions group in /.github/workflows with 2 updates
#1872 fix(registry): include metadata in registration gate payload
#1871 fix(tests): drop the per-session test database instead of leaking one per run
#1870 fix(auth): accept a root-endpoint server's bare registered path at /validate
#1869 fix(registry): repair live auth, virtual routing, and agent card flows
#1868 fix(egress,registry): report unavailable versions as 503 and guard the nginx render
#1866 feat(agent-card): add A2A 1.0 supported_interfaces, security_requirements, signatures
#1865 chore(deps): weekly lockfile update (2026-10-12)
#1863 fix(agents,registry): pin the agent tool destination and report unused AWS credentials
#1858 fix(custom-entities): make is_enabled writable on create and update
#1857 fix(ci,docs): align the retag branch prefix with the PR it watches for
#1851 chore(deps): bump litellm from 1.93.0 to 1.93.2 in the uv group across 1 directory
#1831 fix(nginx): serve a root-endpoint server at its bare path
#1825 fix(virtual): broker per-user credentials for backing MCP servers
#1820 fix(compose): wire MCP_PROXY_TIMEOUT into the prebuilt compose file
#1814 fix(search): build endpoint_url with the server's append_mcp_path

Security Dependency Updates

Package Previous Updated Scope
litellm 1.93.0 1.93.2 uv group, 1 directory
GitHub Actions varies varies actions group, 2 updates

Contributors

Thank you to all contributors for this release:


Support


Full Changelog: 1.32.1...1.33.0

What's Changed

  • chore(deps): bump litellm from 1.93.0 to 1.93.2 in the uv group across 1 directory by @dependabot[bot] in #1851
  • fix(agents,registry): pin the agent tool destination and report unused AWS credentials by @aarora79 in #1863
  • fix(compose): wire MCP_PROXY_TIMEOUT into the prebuilt compose file by @scs001 in #1820
  • fix(virtual): broker per-user credentials for backing MCP servers by @omrishiv in #1825
  • fix(egress,registry): report unavailable versions as 503 and guard the nginx render by @aarora79 in #1868
  • fix(nginx): serve a root-endpoint server at its bare path by @go-faustino in #1831
  • fix(auth): accept a root-endpoint server's bare registered path at /validate by @aarora79 in #1870
  • fix(tests): drop the per-session test database instead of leaking one per run by @aarora79 in #1871
  • chore(deps): bump the actions group in /.github/workflows with 2 updates by @dependabot[bot] in #1874
  • chore(deps): weekly lockfile update (2026-10-12) by @github-actions[bot] in #1865
  • fix(custom-entities): make is_enabled writable on create and update by @go-faustino in #1858
  • docs(api): refresh openapi.json for is_enabled, and document the proxy-route effect by @aarora79 in #1877
  • fix(registry): include metadata in registration gate payload by @tankyhsu in #1872
  • docs: add the 1.33.0 release notes draft by @aarora79 in #1878
  • fix(registry): repair live auth, virtual routing, and agent card flows by @omrishiv in #1869
  • fix(registry): name the rejected version linkage, and centralize the version id by @aarora79 in #1879
  • docs: refresh the 1.33.0 notes for the latest merges by @aarora79 in #1880
  • fix(providers): normalize group claims and fix dcr mapping by @omrishiv in #1884
  • fix(keycloak): withhold admin scope names from the DCR allowlist by @aarora79 in #1885
  • feat(agent-card): add A2A 1.0 supported_interfaces, security_requirem… by @NatalieRaoEg in #1866
  • fix(search): build endpoint_url with the server's append_mcp_path by @go-faustino in #1814
  • docs(registry): document that POST /api/servers/register comes back disabled by @aarora79 in #1886
  • fix(a2a): advertise only routable agent interfaces through the gateway by @omrishiv in #1887
  • fix(registry): keep scope groups when the IdP group listing fails by @RizgarOzan in #1883
  • chore: update image tags to 1.33.0 by @github-actions[bot] in #1888

New Contributors

Full Changelog: 1.32.1...1.33.0

Don't miss a new mcp-gateway-registry release

NewReleases is sending notifications on new releases.