Repository navigation
Release/1.1.0 - #7264
Release/1.1.0#7264
Conversation
e215955 to
bdcfef5
Compare
Signed-off-by: prakhar-singh1928 <prakhar.singh1928@ibm.com>
Signed-off-by: prakhar-singh1928 <prakhar.singh1928@ibm.com>
Signed-off-by: prakhar-singh1928 <prakhar.singh1928@ibm.com>
Signed-off-by: prakhar-singh1928 <prakhar.singh1928@ibm.com>
Signed-off-by: prakhar-singh1928 <prakhar.singh1928@ibm.com>
Signed-off-by: prakhar-singh1928 <prakhar.singh1928@ibm.com>
63ab271 to
46ea02b
Compare
Signed-off-by: prakhar-singh1928 <prakhar.singh1928@ibm.com>
Signed-off-by: prakhar-singh1928 <prakhar.singh1928@ibm.com>
Signed-off-by: prakhar-singh1928 <prakhar.singh1928@ibm.com>
Signed-off-by: prakhar-singh1928 <prakhar.singh1928@ibm.com>
Signed-off-by: prakhar-singh1928 <prakhar.singh1928@ibm.com>
Signed-off-by: prakhar-singh1928 <prakhar.singh1928@ibm.com>
Signed-off-by: prakhar-singh1928 <prakhar.singh1928@ibm.com>
Signed-off-by: prakhar-singh1928 <prakhar.singh1928@ibm.com>
1f08dcc to
f61441f
Compare
Run async e2e tests on a worker thread. The session-scoped sync Playwright fixture leaves an event loop running on the main thread, and pytest-asyncio then fails with "Runner.run() cannot be called from a running event loop". Accept both error shapes where the stateful-session /rpc path and the SDK handler differ: unknown tool, unknown prompt, ReDoS rejection, array structuredContent for legacy callers, and template read URIs. Delete the private tenant gateways with the tenant context that created them. The admin context got 404, so the gateways leaked and blocked the fast_time registration on the next stack start. Signed-off-by: prakhar-singh1928 <prakhar.singh1928@ibm.com>
Signed-off-by: Brian Hussey <brian.hussey@ie.ibm.com>
Bh/correct checkov failure
Signed-off-by: prakhar-singh1928 <prakhar.singh1928@ibm.com>
msureshkumar88
left a comment
There was a problem hiding this comment.
Review of release/1.1.0 (head f39cb8a)
The version bump is consistent at 1.1.0 across .bumpversion.cfg, pyproject.toml, Cargo.toml, Cargo.lock, uv.lock, the Helm chart, Containerfile and SECURITY.md. The 1.0.11 to 1.1.0 upgrade works: one Alembic head, upgrade, downgrade and re-upgrade pass, and the app starts on the upgraded database. The unit suite, ruff, interrogate, secrets scan and Rust checks pass. The release date fix in f39cb8a is correct.
The release changes consumer contracts that the CHANGELOG does not describe. Those gaps block the merge. Each finding below uses the format severity | path:line | problem | fix.
Blocking
blocking | CHANGELOG.md [1.1.0] Breaking Changes | The Rust MCP runtime integration is removed (#6177, #7255) with no entry. Removed: GET/PATCH /admin/runtime/mcp-mode and /admin/runtime/a2a-mode, the /_internal/mcp/transport mount, the Admin UI "MCP Runtime" cards, EXPERIMENTAL_RUST_MCP_*, MCP_RUST_*, RUST_MCP_MODE, RUST_MCP_BUILD, RUST_MCP_LOG, RUST_MCP_SESSION_AUTH_REUSE, ENABLE_RUST_MCP_RMCP_BUILD, and the compose build args ENABLE_RUST and ENABLE_RUST_MCP_RMCP. | Add a Breaking Changes entry that lists the removed endpoints and variables. State the migration: use the Python transport.
blocking | CHANGELOG.md [1.1.0] Breaking Changes | Federated resource names now get the gateway slug prefix (#6780). Migration c7e91a2b4d60 adds resources.original_name and resources.custom_name_slug and rewrites resources.name for every gateway-owned row. Names change on upgrade, on refresh and on gateway rename. An upgrade test turned "Report Final" into my-upstream-gw-report-final. The CHANGELOG has no entry. | Add a Breaking Changes entry. Cover the name change, the two new columns, the migration, the downgrade behavior (restores upstream names, discards manual base overrides), the new custom_name update field, and that URIs do not change.
blocking | CHANGELOG.md [1.1.0] | EXPOSE_ERROR_DETAILS is now deprecated and ignored, and error responses are always sanitized (#7079). The CHANGELOG has no entry. | Document the ignored variable and the new error body shape. Verify the body shape against the diff first.
blocking | mcpgateway/middleware/token_scoping.py (_PERMISSION_PATTERNS), CHANGELOG.md:21 | /protocol/completion/complete has no entry in _PERMISSION_PATTERNS. A direct call to _check_permission_restrictions('/protocol/completion/complete', 'POST', ['tools.read']) returns False. Default-deny then rejects a scoped token that includes tools.read. The CHANGELOG migration note tells operators to issue exactly such a token. Evidence missing: I did not exercise the full middleware stack. | Add ("POST", r"^/protocol/completion/complete", Permissions.TOOLS_READ), or correct the migration note. Add a test with a token scoped to tools.read only. Add a deny-path test for a token without it.
blocking | PR description | The description is the empty template. It has no summary, no linked issues and no breaking-change list. | Fill in the template. List the breaking changes and the verification commands with real results.
Functionally impacting
functionally-impacting | CHANGELOG.md:5-7 | The Deprecation Notice says the Rust MCP and A2A sidecars and ValidationMiddleware "will sunset on 2026-07-07". That date is past, and the Rust MCP integration is now removed. | Rewrite the notice to match the current state. Check whether the A2A runtime and ValidationMiddleware are removed before you write it.
functionally-impacting | CHANGELOG.md:52 | The Origin/Host entry says enforcement covers "all ingress modes (Python, rust-internal, rust-public)" and that the /_internal/mcp/transport bridge is exempt. The Rust ingresses and that bridge no longer exist. | Reword the entry to the public /mcp mount only. Remove the bridge sentence. Fix the matching comments in streamablehttp_transport.py.
functionally-impacting | CHANGELOG.md:19 | The pagination entry says "Rust tools lists remain complete until the bridge forwards client cursors". The Rust bridge is removed. | Delete the sentence.
functionally-impacting | CHANGELOG.md:60 | The logging-capability entry says docker-compose passes MCP_INBOUND_PROTOCOL_MODE "with a legacy default". docker-compose.yml:171 defaults to auto, as do config.py and .env.example. | Change legacy to auto.
functionally-impacting | CHANGELOG.md:46 | The entry says the mcp-servers/ directory is removed. mcp-servers/python/completion_test_server/ is still tracked, and docker-compose uses it. | Reword to "removed except completion_test_server", or remove the leftover.
functionally-impacting | CHANGELOG.md [1.1.0] Added and Security | These merged changes have no entry. Check each against the code before you write it:
- Regex sandbox for JSON Schema
pattern(#6957):REGEX_TIMEOUT_SECONDS,REGEX_WORKERS,REGEX_MAX_SUBJECT_BYTES, fails closed. - URL scheme enforcement (#6881):
STRICT_SCHEME_ENFORCEMENTand a startup scan. - Security fixes: the two "Merge commit from fork" commits (6424230, 69aa591), DCR outbound validation (#6961), handshake credential isolation (#6877), outbound HTTP hardening (#6965, #7076), HTML escaping (#7239), plugin-binding token scoping (#6709),
/v1rate-limit tier path (#6879), scoped/rpcauthorization (#6672). - Team invitations API (#6960, #6991) and OAuth
private_key_jwt(#7190 and follow-ups), plus the #7240 token handling change. - SSO and external IdP bearer verification (#6692, #6424, #7049),
SSO_API_TOKEN_AUTH_ENABLED. State the net result of #7180 and #7219. - MCP wire changes: tool and prompt error translation (#7204, #7169), oversized
_metarejected with-32602(#7259),structuredContentany JSON for2026-07-28callers (#7244),ttlMsandcacheScope(#7181),x-mcp-headervalidation (#6773),serverInfostamping (#7156), inbound era negotiation (#7145). - Per-user-per-server MCP rate limiting (#7191, #7213), OTel tenantless span suppression with
OTEL_SYSTEM_TRACES_ENABLED(#7155), metrics timeseries counts (#7138). - Gateway registration failure causes and HTTP statuses (#6724, fixed by #7096).
| Add one entry per item, or group related items. Confirm the security wording with the security owners.
Suggestions
suggestion | docs/docs/architecture/roadmap.md | The roadmap has no 1.1.0 entry. Section 1.4 of release-management.md asks for the milestone to close with the release date. | Mark the shipped themes and close milestone #24 on release day.
suggestion | Makefile:28-31, testing-up-rust* targets | The RUST_MCP_* variables and the testing-up-rust* targets stay, but the entrypoint no longer reads them. | Remove the dead targets, or document that they are no-ops.
Please update the CHANGELOG, the token scoping entry and the PR description. I will review again after the push.
The completion endpoint requires tools.read, but _PERMISSION_PATTERNS had no entry for POST /protocol/completion/complete. Default-deny then rejected tokens scoped to tools.read. Add the pattern and allow and deny tests. Signed-off-by: prakhar-singh1928 <prakhar.singh1928@ibm.com>
Remove the testing-up-rust and testing-rebuild-rust targets and the RUST_MCP_MODE and RUST_MCP_LOG variables. The entrypoint no longer reads them. Drop the Rust sections from the testing, compose, and performance docs. Remove stale Rust ingress text from the Origin/Host comments and .env.example. Note shipped pagination and completions in the roadmap. Signed-off-by: prakhar-singh1928 <prakhar.singh1928@ibm.com>
9e19618 to
42a06c9
Compare
msureshkumar88
left a comment
There was a problem hiding this comment.
Review of release/1.1.0 (head 42a06c9), CHANGELOG excluded
This pass covers the release branch except CHANGELOG.md, which #7278 tracks.
The release preparation is in good shape:
- The version is 1.1.0 in all seven
.bumpversion.cfgtargets,uv.lock,Cargo.lock, the Helmvalues.yamlimage tags, and the cedar and opa plugin pins. The two remaining1.0.11references (docs/docs/manage/api-usage.md:9,docs/tables/deprecations-removed.yaml:4) are historical and correct. - Alembic has one head,
c7e91a2b4d60. The branch contains every commit onmain. - CI: 72 checks pass, 6 skip (publish and sign jobs), none fail.
- The earlier non-CHANGELOG findings are fixed.
ae56d1f6eadds the completion route to_PERMISSION_PATTERNSwith allow-path and deny-path tests. The PR description is filled in. The Origin/Host comments are reworded. Thetesting-up-rust*targets andRUST_MCP_*Makefile variables are gone. The roadmap records the 1.1.0 themes. - The #7271 Admin UI fix is correct.
toggleUseDefaultMaxMembersreceives(checked, event)from the change delegator, and the checkbox is inside the<form>. I sent five mXSS payloads through the new table-context wrapper insecurity.js(tbody break-out, svg/math namespace confusion, form nesting). No event-handler attribute survived. - Local runs pass:
tests/unit/js/innerhtml-guard.test.js(7/7),tests/unit/mcpgateway/middleware/test_token_scoping.py,tests/unit/mcpgateway/test_version.py.
One wire-behavior bug blocks the release. Each finding uses severity | path:line | problem | fix.
Blocking
blocking | mcpgateway/transports/streamablehttp_transport.py:5279 (also :5085), mcpgateway/main.py:11562 | With USE_STATEFUL_SESSIONS=true and MCPGATEWAY_SESSION_AFFINITY_ENABLED=true, the transport dispatches a session's POST in-process to /_internal/mcp/rpc. That route sets _trusted_internal_mcp_dispatch, so tools/call skips _object_only_structured_content(). The transport then sends response.content to the client unchanged. A 2025-era client receives a non-object structuredContent, and the SDK rejects the CallToolResult ("valid dictionary"). This contradicts #7244, which keeps the 2025 wire unchanged. docker-compose.yml:398 and :406 enable both settings, so the compose deployment reaches this path. | Strip a non-object structuredContent before the transport sends the response to a legacy session. Alternatively, skip the strip in main.py only when the originating caller is modern. Restore the strict e2e assertion in the same change.
Functionally impacting
functionally-impacting | tests/live_gateway/e2e/test_e2e.py:1048 | The legacy structuredContent test now catches the client validation error and returns early, so it passes on the bug above. | Remove the except branch after the fix. Assert that the legacy client receives an object-only result with the value in a text block.
functionally-impacting | tests/live_gateway/e2e/test_e2e.py:824, :914, :5067 | The suite now accepts two wire behaviors that depend on the session path. Unknown tool: an isError result or JSON-RPC -32601. Unknown prompt: -32602 "Unknown prompt:" or -32002 "Prompt not found:". Bounded regex rejection: an isError result or -32603. One client gets different errors when stateful affinity is on. | Align the /rpc path with the SDK path, or document the difference as a known 1.1.0 limitation and track it in an issue.
functionally-impacting | docs/tables/deprecations-pending.yaml:13-23 | The "Rust MCP runtime sidecar" row says "Code remains: RUST_MCP_MODE, … and the transport proxies". #6177 and #7255 remove that integration. | Move the row to docs/tables/deprecations-removed.yaml with Removed: "1.1.0 (<release date>)".
Suggestions
suggestion | docs/docs/architecture/rust-mcp-runtime.md:24-68, :417; docs/docs/architecture/.pages:7 | The page is still in the docs navigation. It presents RUST_MCP_MODE as the operator control and lists the deleted testing-rebuild-rust* targets. | Remove the page from the navigation, or add a "removed in 1.1.0" notice at the top.
suggestion | crates/mcp_runtime/README.md, DEVELOPING.md, RELEASE-CHECKLIST.md, STATUS.md | These files reference the deleted testing-up-rust* and testing-rebuild-rust* targets. | Remove the references, or point them to the crate's standalone cargo workflow.
Minor
minor | AGENTS.md:530, tests/live_gateway/mcp/test_completion_federation.py:22, tests/live_gateway/e2e/test_e2e.py:144 | These lines still set RUST_MCP_MODE= or RUST_MCP_MODE=off. No code reads that variable. | Remove the variable from these lines.
minor | milestone v1.1.0 (#24) | The milestone is open with zero open issues. | Close it on release day, per release-management.md section 1.4.
Fix the blocking finding, or add a documented waiver to this PR. I will review again after the push.
|
For the mention blocking issue @msureshkumar88 |
|
@msureshkumar88 on functionally impacting 2, tool error shape and prompt error shape will differ and its expected. prompts don't have |
Signed-off-by: Vishu Bhatnagar <vishu.bhatnagar@ibm.com>
Signed-off-by: Vishu Bhatnagar <vishu.bhatnagar@ibm.com>
Signed-off-by: Vishu Bhatnagar <vishu.bhatnagar@ibm.com>
Signed-off-by: Vishu Bhatnagar <vishu.bhatnagar@ibm.com>
Signed-off-by: Vishu Bhatnagar <vishu.bhatnagar@ibm.com>
Signed-off-by: Vishu Bhatnagar <vishu.bhatnagar@ibm.com>
Signed-off-by: Vishu Bhatnagar <vishu.bhatnagar@ibm.com>
msureshkumar88
left a comment
There was a problem hiding this comment.
Follow-up review of release/1.1.0 (head 42a06c9)
Thank you for the replies. No commits have landed since the last review, so the findings below are still open at 42a06c9e9. The fixes confirmed in the last pass stay confirmed: the completion route in _PERMISSION_PATTERNS with tests, the PR description, the Origin/Host comments, the Makefile Rust targets and the roadmap entry.
Replies
#7131 as tracking for the blocking finding. #7131 does not cover this bug. It tracks upstream calls without Mcp-Session-Id that open a new transport per call, which is a connection-pooling topic. It does not mention structuredContent, /_internal/mcp/rpc, _trusted_internal_mcp_dispatch or #7244. Its last edit is 2026-10-05. The blocking finding needs a fix, or a waiver that links an issue for this exact bug.
Tool and prompt error shapes. I agree that prompts and tools have different error shapes. Prompts have no isError, and tools need it for plugins. The finding is different: one call gets a different error depending on the session path. An unknown tool returns an isError result on the SDK path and JSON-RPC -32601 on the stateful /rpc path. An unknown prompt returns -32602 "Unknown prompt:" on one path and -32002 "Prompt not found:" on the other. A bounded regex rejection returns an isError result on one path and -32603 on the other. The finding stays open.
Each finding uses severity | path:line | problem | fix.
Blocking
blocking | mcpgateway/transports/streamablehttp_transport.py:5279 (also :5085), mcpgateway/main.py:11561, :11776 | main.py skips _object_only_structured_content() for a trusted internal dispatch. The comment says the originating worker keeps the value. The transport does not strip it: it sends response.content from post_rpc_in_process() to the client unchanged. A 2025-era client on a stateful affinity session receives a non-object structuredContent and rejects the CallToolResult. docker-compose.yml:398 and :406 enable this path. | Apply _object_only_structured_content() to the JSON-RPC result in the transport before send(), at both dispatch sites. Confirm that only 2025-era sessions reach this path. Alternatively, remove the _trusted_internal_mcp_dispatch guard in main.py; the Rust runtime was the other internal caller and is removed. For a waiver instead, open an issue for this bug and list it in the PR description as a known 1.1.0 limitation.
blocking | CHANGELOG.md [1.1.0] Breaking Changes, "Rust MCP config surface removed" | The entry does not list what operators lose. | List the removed endpoints (GET/PATCH /admin/runtime/mcp-mode, /admin/runtime/a2a-mode, the /_internal/mcp/transport mount), the removed variables (EXPERIMENTAL_RUST_MCP_*, MCP_RUST_*, RUST_MCP_MODE, RUST_MCP_BUILD, RUST_MCP_LOG, RUST_MCP_SESSION_AUTH_REUSE, ENABLE_RUST_MCP_RMCP_BUILD) and the compose build args ENABLE_RUST and ENABLE_RUST_MCP_RMCP. State the migration: use the Python transport.
blocking | CHANGELOG.md [1.1.0] | #6780 is listed under Added only. Migration c7e91a2b4d60 rewrites resources.name for every gateway-owned row and adds original_name and custom_name_slug. | Add a Breaking Changes entry. Cover the name change on upgrade, refresh and gateway rename, the two columns, the downgrade behavior, the custom_name update field, and that URIs do not change.
blocking | CHANGELOG.md [1.1.0] | #7079 has no entry. EXPOSE_ERROR_DETAILS is deprecated and ignored, and error responses are always sanitized. | Add an entry for the ignored variable and the new error body shape.
Functionally impacting
functionally-impacting | tests/live_gateway/e2e/test_e2e.py:1046-1053 | The legacy structuredContent test catches the client validation error and returns early. It passes on the blocking bug. | Remove the except branch with the fix. Assert structured_content is None and that the text block carries [1, 2, 3].
functionally-impacting | tests/live_gateway/e2e/test_e2e.py:824, :914, :5067 | The error shape for one call depends on the session path (see Replies). | Align the /rpc path with the SDK path. Alternatively, document the difference as a known 1.1.0 limitation and track it in an issue.
functionally-impacting | docs/tables/deprecations-pending.yaml:13-23 | The "Rust MCP runtime sidecar" row is still pending. #6177 and #7255 remove that integration. | Move the row to docs/tables/deprecations-removed.yaml with Removed: "1.1.0 (2026-10-19)".
functionally-impacting | CHANGELOG.md:7 | The Deprecation Notice says the Rust sidecars and ValidationMiddleware "will sunset on 2026-07-07". The date is past and the Rust MCP integration is removed. | Rewrite the notice to the current state.
functionally-impacting | CHANGELOG.md:64 | "Rust tools lists remain complete until the bridge forwards client cursors." The bridge is removed. | Delete the sentence.
functionally-impacting | CHANGELOG.md:100 | The entry says docker-compose passes MCP_INBOUND_PROTOCOL_MODE "with a legacy default". The default is auto. | Change legacy to auto.
functionally-impacting | CHANGELOG.md [1.1.0], mcp-servers/ entry | The entry says the directory is removed. mcp-servers/python/completion_test_server/ is still tracked and used by docker-compose. | Reword to "removed except completion_test_server".
functionally-impacting | CHANGELOG.md [1.1.0] | These items have no entry: the two "Merge commit from fork" security fixes (6424230, 69aa591); REGEX_TIMEOUT_SECONDS, REGEX_WORKERS, REGEX_MAX_SUBJECT_BYTES (#6957); STRICT_SCHEME_ENFORCEMENT (#6881); SSO_API_TOKEN_AUTH_ENABLED; OTEL_SYSTEM_TRACES_ENABLED (#7155); ttlMs and cacheScope (#7181). | Add the variables to the existing entries, or add new entries. Confirm the security wording with the security owners.
Suggestions
suggestion | docs/docs/architecture/.pages:7, docs/docs/architecture/rust-mcp-runtime.md | The page is still in the navigation with a "sunsets on 2026-07-07" banner. | Remove it from the navigation, or add a "removed in 1.1.0" notice.
suggestion | crates/mcp_runtime/README.md, DEVELOPING.md, RELEASE-CHECKLIST.md, STATUS.md | 25 references to the deleted testing-up-rust* and testing-rebuild-rust* targets remain. | Remove them, or point to the standalone cargo workflow.
Minor
minor | AGENTS.md:530, tests/live_gateway/e2e/test_e2e.py:144, tests/live_gateway/mcp/test_completion_federation.py:22 | These lines still set RUST_MCP_MODE. No code reads it. | Remove the variable.
Fix the blocking findings, or document a waiver in the PR description with a linked issue for each. I will review again after the push.
|
Thanks @msureshkumar88 for review. Regarding |
chore: update web UI to v0.2.4 and document startup
67aeb44
msureshkumar88
left a comment
There was a problem hiding this comment.
Approval of release/1.1.0 (head 67aeb44)
The maintainer reviewed the open findings and waives all of them for 1.1.0. I approve on that basis.
The release preparation is sound. The version is 1.1.0 in every bump target. Alembic has one head. CI passes. The completion route in _PERMISSION_PATTERNS has allow-path and deny-path tests. The #7271 Admin UI fix holds against the mXSS payloads I sent. The new commits since 42a06c9e9 change only web UI docs, the web UI image pin and the CHANGELOG web UI entry.
Waived for 1.1.0
/rpc path (no usage; follow-up issue to be filed):
- Legacy
structuredContenton the stateful affinity/rpcpath (streamablehttp_transport.py:5279,:5085;main.py:11561,:11776) - The early return in the legacy
structuredContente2e test (test_e2e.py:1046-1053) - Session-path-dependent error shapes (
test_e2e.py:824,:914,:5067)
CHANGELOG [1.1.0]:
- The Rust removal entry (#7255, #6177) does not list the removed endpoints, variables and compose build args
- No Breaking Changes entry for the #6780 resource name rewrite (migration
c7e91a2b4d60) - No entry for #7079 (
EXPOSE_ERROR_DETAILSignored) CHANGELOG.md:7: the 2026-07-07 sunset notice- "Rust tools lists remain complete until the bridge forwards client cursors"
- "with a
legacydefault"; the default isauto - The
mcp-servers/entry does not mentioncompletion_test_server - No entries for the fork security merges (6424230, 69aa591),
REGEX_*(#6957),STRICT_SCHEME_ENFORCEMENT(#6881),SSO_API_TOKEN_AUTH_ENABLED,OTEL_SYSTEM_TRACES_ENABLED(#7155),ttlMsandcacheScope(#7181)
Rust deprecation records and cleanup:
- The Rust sidecar row in
docs/tables/deprecations-pending.yaml:13-23 docs/docs/architecture/rust-mcp-runtime.mdin the navigationtesting-up-rust*andtesting-rebuild-rust*references incrates/mcp_runtime/*.mdRUST_MCP_MODEinAGENTS.md:530,test_e2e.py:144,test_completion_federation.py:22
Close milestone #24 on release day. Correct the CHANGELOG items in a follow-up so the published release notes match the code.
Signed-off-by: prakhar-singh1928 <prakhar.singh1928@ibm.com>
brian-hussey
left a comment
There was a problem hiding this comment.
Looks good from my point of view.
[1.1.0] - 2026-10-19 - Modern MCP Protocol, Federated Completions, and Catalog Pagination
Overview
Release 1.1.0 delivers modern MCP protocol negotiation on by default, federated completions, MCP catalog pagination, MCP Origin/Host transport security enforcement, federated gateway tool-name collision detection, and configuration import ownership enforcement. It also removes deprecated anyio monkey-patch settings and the in-tree
mcp-servers/directory. This release consolidates 69 PRs across:MCP_CLIENT_CONNECT_MODEandMCP_INBOUND_PROTOCOL_MODEnow default toauto, inbound MCP era negotiation is complete,completion/completeis forwarded to the owning upstream, downstream MCP catalog lists are paginated, and federated resource names carry the gateway name as a prefix.private_key_jwttoken endpoint client authentication, MCP Origin/Host enforcement, configuration import ownership enforcement, call-time URL scheme allowlisting, DCR outbound request pinning, and external IdP token fallback on all auth paths.make container-bump-image-versions, moved CSS linting from Stylelint to Biome, migrated Tailwind CSS to v4, removed the anyio monkey-patch settings, the Rust MCP config surface, and the in-treemcp-servers/directory.Added
Protocol & Transport
Modern MCP protocol negotiation is on by default (#7061) -
MCP_CLIENT_CONNECT_MODEandMCP_INBOUND_PROTOCOL_MODEnow default toautoinstead oflegacy. Outbound upstream connections probeserver/discoverand negotiate the 2026-07-28 revision, with transparent fallback to the legacyinitializehandshake. Inbound clients may sendmcp-protocol-version: 2026-07-28instead of receiving a 400. Set either variable tolegacyto restore the previous behaviour.Inbound MCP era negotiation (#7156) - Completed inbound MCP era negotiation.
Federated completions (#6629, #6760) - Forward
completion/completeto the owning upstream with request metadata. Both legacy and modern MCP connections remain supported through existing negotiation settings; forwarding has no modern-only gate.Streamable HTTP
serverInfostamping (#6566, #7145) - The streamable HTTP server is configured with the app name and version forserverInfo.Federated resource names prefixed with gateway names (#6780) - Federated resource names carry the owning gateway's name as a prefix.
structuredContentaccepts any JSON value for 2026-era callers (#7244) - Tool results on the 2026-07-28 wire now accept any JSON value (string, number, array, null, or object) asstructuredContent. The 2025 wire is unchanged: non-object values are stripped and the text copy incontentis used instead.Federated gateway tool-name collisions (#6534) - Gateway registration, refresh, OAuth discovery, reactivation, rename, and visibility updates now reject detected tool-name collisions in public, team, and private visibility scopes. Gateway automation must handle the endpoint's conflict response when a previously accepted colliding registration is rejected. Existing duplicate rows require administrator review before affected invocation names are usable. Operators can identify duplicates with:
API & Platform
POST /v1/a2a/{agent_name}/invoke,POST /v1/a2a/invoke, andPOST /v1/a2a/{agent_name}/jsonrpc. The OpenAPI schema now exposes typed components while preserving the routes' passthrough behavior.Security & Auth
private_key_jwttoken endpoint client authentication (#6991) - OAuth token requests can authenticate with RFC 7523private_key_jwt.JWT_SECRET_KEYwhen old-token invalidation is required. Persistent database storage and short-lived local tokens remain recommended for production deployments.Operations & Tooling
make container-bump-image-versionsto bump container image version pins.make lint-weband thelint-webCI job now lintmcpgateway/static/*.csswith Biome, configured inbiome.json. The CI matrix entry is renamed fromstylelinttobiome-css. Stylelint and its configs leave the dev dependencies, which removes thebracesadvisory (GHSA-vfj7-8cjw-p6xm) fromnpm audit.make testing-upweb UI URL (#6415) - The startup summary now shows aContextForge Web UIrow whose port comes from the resolved Compose configuration. The printed URL now matches the port Compose publishes whenWEB_UI_PORTis set only in.env. Resolution failure stops the target instead of printing a fallback URL.mcp-servers/directory removed (#7078) - The unsupported sample/test MCP servers, the Go/Python scaffolding templates, and themcp-url-to-markdown-testsworkflow leave the repository. The six Python servers now live in IBM/contextforge-examples undermcp-servers/python/; the scaffolding templates are deleted without migration. The performance compose generator and benchmark docs now use the publishedghcr.io/ibm/cfex-mcp-fast-time-serverandghcr.io/ibm/cfex-mcp-benchmark-serverimages. The/v1/mcp-serversREST API is unchanged: it remains the product-language alias for the gateways API.Breaking Changes
MCP_LIST_PAGE_SIZE=100items by default. Clients that ignorenextCursorreceive only the first page. Database order changes fromcreated_at DESCtoid ASC. Server-scoped lists exclude other users' private rows for unrestricted administrators; administrators retain their own private rows. Multi-page direct-proxy lists now require Redis. ShareAUTH_ENCRYPTION_SECRETacross workers and keep traversals on one version during deployment. Restart traversals after reconnect, rotation, or rollback. Rust tools lists remain complete until the bridge forwards client cursors. This change delivers part of [BUG]: Add downstream pagination for MCP list methods #6990. See pagination configuration.POST /protocol/completion/completenow requirestools.readRBAC permission. Previously, authentication alone passed this endpoint's authorization check. Before upgrading from 1.0.11, granttools.readto completion callers' roles and include it in replacement tokens with explicit permission scopes. Denied callers receive HTTP 403 before upstream access. See completion permission migration.ANYIO_CANCEL_DELIVERY_PATCH_ENABLEDandANYIO_CANCEL_DELIVERY_MAX_ITERATIONSworkaround frommcpgateway/transports/sse_transport.py, together with theSSE_TASK_GROUP_CLEANUP_TIMEOUTsetting. The gateway now requiresanyio>=4.15.0, which fixes the_deliver_cancellationCPU spin loop upstream (anyio issues #695 and #1111). Unknown environment keys are ignored, so existing deployments start without changes. Remove the retired variables from your configuration to keep it accurate. The matching Helm values and docker-compose entries are also removed. DanglingMCP_SESSION_POOL_CLEANUP_TIMEOUTreferences, which no code path ever read, are removed from docker-compose, the Helm chart, and.env.example. SSE connection protection and cleanup timeouts remain the containment strategy; seedocs/docs/operations/cpu-spin-loop-mitigation.md.x-mcp-headerannotation validation on tool registration (#6773) -POST /tools(create) andPUT /tools/{id}(update) now validatex-mcp-headerannotations ininput_schemaagainst the MCP 2026-07-28 constraints. Payloads with invalid annotations are rejected with HTTP 422. Previously these passed validation silently. Update any tool registration payload that uses non-conformingx-mcp-headervalues before upgrading.Security
POST /v1/importwithconflict_strategy=updateno longer overwrites tools, resources, prompts, or gateways that the importer does not own. Bulk registration now runscheck_resource_ownership()before it modifies an existing row and reportsPermission deniedfor rows the caller does not own. Bulk update without a caller identity changes nothing. The import call toupdate_gateway()now passes the importer identity. Theadmin.importroutes useglobal_only=True, so a team-scoped role no longer satisfies them. The Admin UI import now passes the authenticated email asimported_by. Deployments that grantadmin.importthrough a team-scoped role must move that grant to a global role.Originheader on/mcpis now rejected with HTTP 403. SetMCP_ALLOWED_ORIGINSto a comma-separated list of allowed origins to enable enforcement (default: empty, backward-compatible). An optional companion settingMCP_ALLOWED_HOSTSenforces exacthost:portmatching on theHostheader. Enforcement runs at the public/mcpmount viaMCPOriginHostGate, covering the Python ingress path.Authorizationvalue suppresses all fallback authorization. The gateway retains inbound caller filtering, sanitizes plugin input, resolves output case-insensitively, and stripsX-Vault-Tokensbefore egress./v1paths (#6879) - Rate limit tier matching normalizes/v1paths.SSRF_PROTECTION_ENABLED=false. Dangerous-protocol, control-character, embedded-credential, and XSS checks apply unconditionally. A deployment that relied on the old side effect to reach a non-HTTP(S) URL will see it rejected. Pooled upstream MCP sessions no longer follow HTTP redirects. An upstream that answers/ssewith a 307 redirect to/sse/fails instead of following it; register the redirected URL directly. Connection setup fails closed when a caller-supplied HTTP client's TLS context cannot be read. Some outbound requests connect to a single validated address; a dual-stack upstream whose first-sorted address is unreachable can fail. Requests to a validated address use a dedicated connection pool. Behavior is unchanged when an environment proxy (HTTP_PROXY,HTTPS_PROXY,ALL_PROXY) applies to the target.tools.read.escapeHtmlis applied to the tool result body before rendering, preventing stored XSS in result output.Fixed
Auth & OAuth
get_current_user()(#6424, #7049) - External-IdP verification is wired intoget_current_user(), and all auth paths fall back to external IdP tokens.private_key_jwtfollow-ups (#7190) - Restoredprivate_key_jwtfollow-ups dropped from feat(oauth): add private_key_jwt token endpoint client authentication #6991.Gateway, Catalog & Tools
/appmount prefix.UI_BASE_URLaccepts a deployment base or a full frontend root ending in/app. Full roots produce a startup warning and have only the final mount segment removed during normalization. For a deployment prefix/contextforge/app, set the full roothttps://ui.example.com/contextforge/app/appto preserve both segments in emailed links. The invitation frontend route remains tracked by [BUG]: Invitation emails link to /accept-invitation #6776.server/discoverresponses for2026-07-28clients now omitcapabilities.logging. MCP 2026-07-28 deprecates that capability (SEP-2577), and ContextForge sends no request-scoped log messages. Clients on2025-11-25and older still receive the capability and can calllogging/setLevel. Use OpenTelemetry and structured logging for gateway observability. Modern clients reach the gateway only whenMCP_INBOUND_PROTOCOL_MODE=auto;docker-compose.ymlnow passes that variable to the gateway, with alegacydefault.cycle_started_at, so the gateway no longer skips refreshes._meta(#7259) - The gateway rejects oversize_metawith-32602and keeps the upstream resource error.psutil.cpu_freq()on macOS (#7087) - Guardedpsutil.cpu_freq()againstSystemError._meta["io.contextforge/errorCode"]and the result retains theisErrorshape. Previously the code was lost and the SDK made up a transport-level code. Clients that rely on a specific error code from the gateway's own fallback will see the upstream code instead.-32602(INVALID_PARAMS). Upstream JSON-RPC errors are relayed with their original code and sanitizeddata. Previously the shape and code varied across failure paths.ErrorFormatter. Clients that parsed the rawdetailormessagefield of error responses may need to update their parsers.Observability & A2A
Admin UI
innerHTMLguard. Edit modals use delegated handlers.Build, CI & Dependencies
FROMline.Changed
Gateway.last_seenannotation removed (#6682) - Removed the duplicate model annotation.Documentation
make container-bump-image-versions.Testing
Chores
mcp-server-filesystemexamples withmcp-server-fetchdocker-compose.ymldual-era test configurationRelease preparation bumped every version reference to 1.1.0, refreshed npm dependencies (12 packages updated, 0 vulnerabilities), bumped the
NODEJS_IMAGEpin to 10.2-1792386062, updateduv.lockandCargo.lock, normalised.bumpversion.cfgindentation, and removed Rust MCP leftovers from the Makefile, docs, and comments.