github agentic-community/mcp-gateway-registry 1.35.0
1.35.0 - Keycloak OBO Token Exchange, Users in Many Groups, and Bounded Registrant Metadata

3 hours ago

Release 1.35.0 - Keycloak OBO Token Exchange, Users in Many Groups, and Bounded Registrant Metadata

October 2026


Upgrading from 1.34.0

This section covers everything you need to know to upgrade from 1.34.0 to 1.35.0.

Breaking Changes

Helm installs with bundled Keycloak must adopt keycloak-client-secret before the first upgrade. The stack chart now renders this Secret itself (#1784). Older chart versions had the setup-keycloak Job create it, so on an existing cluster the Secret exists and no Helm release owns it. helm upgrade stops with an error that prints the two commands to run. Running them keeps the current client secrets, so nobody has to log in again:

kubectl -n <namespace> label secret keycloak-client-secret app.kubernetes.io/managed-by=Helm --overwrite
kubectl -n <namespace> annotate secret keycloak-client-secret \
  meta.helm.sh/release-name=<release> meta.helm.sh/release-namespace=<namespace> --overwrite

The running deployment keeps working while the upgrade waits. ArgoCD and other GitOps tools cannot run Helm's lookup, so set global.generateSecrets=false and supply the client secrets explicitly, or point global.existingKeycloakClientSecret at a Secret you manage. See Upgrading a cluster installed before this change.

Registrant metadata is capped at 10 KB and 8 levels of nesting. Server, agent and skill writes with larger or deeper metadata now fail with a 422 before any side effect. The server PUT/PATCH limit drops from 64 KB to 10 KB. Existing records stay readable, but editing one that is over the limit requires trimming its metadata first. Malformed Unicode and over-nested JSON also return 422 where they used to return 500 (#1943).

The mcpgw discovery tools omit metadata unless asked. list_services, list_agents and list_skills no longer return registrant metadata by default, and search_registry drops agent-card metadata as it already did for servers and skills. A client that reads metadata from these tools must name the keys it wants in metadata_fields. The REST API is unchanged. See From the mcpgw MCP tools (#1943).

AUTH_SERVER_NGINX_MARKER_SECRET now guards every internal token, and its characters are restricted. The auth server used to mint the registry API token whenever a request carried X-Registry-Api-Auth, a header any direct caller can send. It now requires the nginx marker for that token too (#1959). Two consequences follow:

  • If the marker differs between the registry and the auth server, every registry API and UI request returns 401, not only proxied MCP calls. Rotate it on both services together.
  • The marker may contain only printable ASCII other than $, with no spaces. Both services refuse to start otherwise; Helm reports it at render time and Terraform at plan time. Values generated by build_and_run.sh, the stack chart and Terraform already pass.

Other upgrade notes

Virtual servers drop backends that are disabled or not healthy. A virtual server now applies the same rule as the direct route. A mapped backend that is disabled, unhealthy, or not yet health-checked leaves the virtual server's tools/list and routing until it recovers. Virtual-server backends also stop receiving reserved caller and forwarding headers, including X-Forwarded-For and X-Real-IP on backends with egress_auth_mode=none, so a backend that logs client addresses now sees the gateway's (#1795).

An expired virtual-server session returns 404. The registry deletes a client session after an hour without use. A request carrying that session's Mcp-Session-Id now gets 404, which tells MCP clients to re-initialize on their own; it used to get 400 and stay broken until the user reconnected. A failed session lookup returns 503. A missing or malformed header still returns 400 (#1946).

A2A agents with high-severity scan findings are recorded as unsafe, not as failed scans. a2a-scanner exits 1 when it reports a high-severity finding, and the registry used to treat that as a scanner crash with no findings. It now stores the verdict with its findings, and AGENT_SECURITY_BLOCK_UNSAFE_AGENTS decides whether the agent is disabled. With SECURITY_BLOCK_ON_SCAN_FAILURE=false, such agents used to stay enabled. They are now disabled, because AGENT_SECURITY_BLOCK_UNSAFE_AGENTS defaults to true. Rescan stored results with POST /api/agents/{path}/rescan (#1951).

nginx errors on API routes come back as JSON. An nginx-generated 502 on /api/ is now a JSON body, MCP, virtual-server and A2A routes get a JSON-RPC error, and /api/ard/ gets the ARD envelope. Browser pages keep the HTML page, which the registry image now ships. Each in-flight auth check now reserves a 64 KiB buffer, about 64 MiB at 1,000 concurrent checks (#1959).

The first ECS apply after upgrading makes some one-time changes. It creates time_static.created, rewrites every CreatedAt tag once, replaces time_sleep.wait_for_bucket_policy (the 30-second wait runs once), and redeploys the auth server so it picks up MCP_TOKEN_DEFAULT_TTL_HOURS and MCP_TOKEN_MAX_TTL_HOURS. After that apply, terraform plan on an unchanged stack reports no changes (#1941, #1953). Infrastructure upgrades and repeat plans lists what to expect.

Binary skill resources arrive as raw bytes. GET /api/skills/{path}/content?resource= returns a non-UTF-8 resource (PNG, TTF, WOFF2) with its own media type instead of a corrupted JSON string. Text resources keep the JSON shape. The mcpgw get_skill_content tool returns binary bodies base64-encoded in content_base64 (#1960).

New Environment Variables

No new environment variables in this release.

The stack chart adds Helm values for secret generation (#1784):

Value Default Description
global.generateSecrets true Generate missing secrets at install and keep them on upgrade through lookup. Set false under ArgoCD and supply every secret explicitly.
global.existingKeycloakClientSecret empty Name of a Secret you manage. The chart then skips rendering keycloak-client-secret.
global.authProvider.keycloak.clientSecret, .m2mClientSecret empty Explicit Keycloak web and M2M client secrets. The setup Job writes them into the realm and fails if the realm disagrees.
keycloak.auth.adminPassword empty Keycloak admin password. Empty generates one.
keycloak.postgresql.auth.password, .postgresPassword empty Keycloak database passwords. Empty generates them.

Upgrade Instructions

Docker Compose

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

# If you set AUTH_SERVER_NGINX_MARKER_SECRET by hand, check it holds only
# printable ASCII other than '$' and no spaces. Then rebuild and restart:
./build_and_run.sh

Kubernetes / Helm (EKS)

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

# REQUIRED: the registry, keycloak-configure, mongodb-configure and stack charts changed
cd charts/mcp-gateway-registry-stack
helm dependency build
helm dependency update

# With bundled Keycloak, adopt keycloak-client-secret first (see Breaking Changes).
# 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.35.0

# No new variables. The first apply makes the one-time changes listed above.
cd terraform/aws-ecs
terraform plan
terraform apply

Major Features

Keycloak token exchange for on-behalf-of egress

The egress on-behalf-of (OBO) flow now works with Keycloak as the identity provider. The auth server sends an RFC 8693 token-exchange request with the user's access token, the target client as audience and the gateway's own client credentials, and maps Keycloak's errors to a re-authentication prompt or a configuration error.

  • The auth server verifies the exchanged token's signature, issuer, expiry, audience and subject before forwarding it.
  • It refuses an exchanged token whose audience includes the gateway. Keycloak's legacy exchange copies the target client's default scopes, and the shipped realm puts the gateway audience on basic, so the upstream would otherwise receive a token it could replay against the gateway as the user. Give target clients a copy of basic without the audience mapper, or use standard exchange on Keycloak 26.2 or later.
  • Keycloak OBO still needs HTTPS to Keycloak, a trusted IdP host and the exchange permission. See Keycloak (self-hosted) as the OBO identity provider.

PR #1678

Users in many groups no longer get 500s

A user in about 180 or more IdP groups got a 500 from registry API calls such as semantic search, and the registry never logged it. nginx reads the auth server's /validate response into a 16 KiB buffer, and that response carried the groups twice, once in X-Groups and once in the signed registry token.

  • Groups now cross nginx once, inside the token. /validate stops sending X-Groups, and every location that forwards the token clears a client-sent copy. A browser session's token carries no groups, because the registry reads them from the session store.
  • The auth subrequests use 64 KiB buffers and their own error handler, so the auth server's Entra-outage 503 reaches the client.
  • The auth server refuses to mint a token over 40 KiB or send /validate headers over 60 KiB, and logs one line with the sizes and a hashed user ID.
  • The Entra Graph group-recovery cap rises to 8 KiB, about the 200 groups Entra places inline.

PR #1959

Chart-owned Keycloak client secret for ArgoCD

The stack chart could not sync under ArgoCD. The registry and auth server mount keycloak-client-secret, and only a post-install Job created it, which ArgoCD runs only after every pod is healthy. The chart now renders the Secret during install, and the Job writes those values into the Keycloak realm and checks that they match.

  • helm upgrade no longer rotates both Keycloak client secrets while the running pods keep the old values.
  • The registry and auth-server charts keep FEDERATION_STATIC_TOKEN and FEDERATION_ENCRYPTION_KEY across upgrades instead of regenerating them.
  • global.generateSecrets controls generation for every chart-made secret. Existing installs need the one-time adoption step in Breaking Changes.

PR #1784

Bounded registrant metadata

Registrants control the metadata field, and the mcpgw discovery tools returned it to the model on every list call. This release caps metadata at 10 KB and 8 levels on every write path and makes it opt-in on the model-facing tools, which now return only the keys a caller names in metadata_fields. This is the first of two parts for #1811; YARA scanning of metadata follows separately.

PR #1943


What's New

Security

  • The auth server requires the nginx marker before minting the registry API token, and checks the marker's characters at startup, Helm render and Terraform plan (#1959)
  • Registrant metadata is bounded on write and omitted from mcpgw discovery tool output by default (#1943)
  • Virtual-server backends no longer receive reserved caller, gateway or forwarding headers (#1795)
  • The Keycloak OBO exchange refuses tokens the gateway itself would accept (#1678)
  • New tests show that the sample agent rejects a model-supplied absolute tool URL before sending any request, so the gateway token never leaves (#1936)

Virtual MCP Servers

  • Disabled, unhealthy and never-checked backends drop out of a virtual server's tool list and routing (#1795)
  • An unknown or expired Mcp-Session-Id returns 404, so clients re-initialize (#1946)

Skills

  • Binary companion resources download intact in the API, the web UI and the mcpgw get_skill_content tool (#1960)

Deployment

  • The stack chart renders keycloak-client-secret, and global.generateSecrets gates secret generation (#1784)
  • terraform plan on an unchanged ECS stack no longer replaces every service security group and task definition (#1941)
  • The ECS auth server receives the MCP token TTL settings, which the registry container had twice (#1953)

Documentation

  • The README and slide deck use SVG diagrams that show the auth server's /mcp-proxy hop and the database, and the deck gains an "Inside the Gateway" slide (#1942)
  • DEV_INSTRUCTIONS.md points at the test commands CI runs and at docs that still exist (#1965)

Tooling

  • The usage report forecasts from the current snapshot, labels builds apart from releases, and counts feature adoption per instance (#1950)

Bug Fixes

  • Users in many IdP groups no longer get a 500 from registry API calls (#1959)
  • An A2A scan that exits 1 with high-severity findings records the findings and an unsafe verdict instead of a scanner failure (#1951)
  • Registration with an omitted boolean form field, such as append_mcp_path or overwrite, keeps the field's default. FastAPI 0.147 through 0.161 read it as False, and the weekly lockfile update had moved the registry to 0.148 (#1956)
  • Metadata with invalid Unicode or deep nesting returns 422 instead of 500 (#1943)
  • Binary skill resources are no longer decoded as UTF-8 and corrupted (#1960)
  • ECS plans no longer redeploy every service because of a timestamp() tag (#1941)
  • The ECS auth server honors the configured MCP token TTLs (#1953)
  • ArgoCD syncs of the stack chart no longer deadlock on keycloak-client-secret (#1784)

Closed Issues

Issue Title Closed By
#1958 Binary skill resources are corrupted by GET /api/skills/{path}/content?resource= (returned as response.text) PR #1960
#1939 Bug: 500 on registry API calls for users with many groups ("upstream sent too big header" on /validate) PR #1959
#1916 terraform/aws-ecs: every plan replaces all service security groups because of a timestamp() tag PR #1941
#1663 ArgoCD + post-install hook creates a circular dependency for keycloak-client-secret (bug/design gap) PR #1784
#1571 DEV_INSTRUCTIONS.md mandates ./tests/run_all_tests.sh for PR merge, but that script and 4 other referenced paths no longer exist PR #1965

Pull Requests Included

PR Title
#1965 docs: fix stale test commands and links in DEV_INSTRUCTIONS.md
#1960 fix(skills): return binary companion resources as base64 JSON
#1959 fix(nginx): 500 too many groups
#1956 fix(deps): require fastapi 0.162 so omitted bool form fields keep their defaults
#1953 fix(terraform): pass the MCP token TTL settings to the ECS auth server
#1951 fix(security): an A2A scan that exits 1 with HIGH findings is a result, not a scanner failure
#1950 fix(usage-report): correct version labels, feature rates and forecast timing
#1948 chore(deps): weekly lockfile update (2026-10-26)
#1946 fix(virtual): return 404 for an unknown or expired client session
#1945 chore(deps): bump the actions group in /.github/workflows with 2 updates
#1943 fix(security): bound registrant metadata and make it opt-in on mcpgw discovery tools (#1811)
#1942 docs: replace the README and slide deck diagrams with SVG figures
#1941 fix(terraform): prevent unnecessary ECS resource replacements
#1936 test(security): assert rejected agent tool paths send no auth
#1795 fix(virtual-servers): strip reserved backend headers and gate unhealthy backends
#1784 fix(charts): chart-render keycloak-client-secret and gate secret generation
#1678 feat(egress): implement Keycloak RFC 8693 token-exchange for OBO

Security Dependency Updates

Package Previous Updated Scope
fastapi 0.143.0 0.162.0 registry (#1948, #1956); 0.148.0 in auth_server, metrics-service and the A2A agents (#1948)
starlette 1.7.0 1.9.0 all Python projects (#1948)
pymongo 4.18.3 4.19.0 registry, auth_server (#1948)
pydantic-settings 2.15.0 2.16.0 registry, auth_server, servers (#1948)
boto3 / botocore 1.43.111 1.43.114 registry, auth_server, A2A agents (#1948)
github/codeql-action/upload-sarif 4.38.3 4.39.0 CI workflows (#1945)
bridgecrewio/checkov-action 12.3133.0 12.3134.0 CI workflows (#1945)

Contributors

Thank you to all contributors for this release:


Support


Full Changelog: 1.34.0...1.35.0

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

NewReleases is sending notifications on new releases.