github IBM/mcp-context-forge v1.1.0
v1.1.0 - Release 2026-10-20 - Modern MCP Protocol, New Redesigned Web UI, Federated Completions, and Catalog Pagination

3 hours ago

[1.1.0] - 2026-10-19 - Modern MCP Protocol, New Redesigned Web UI, 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:

  • Protocol & Transport - MCP_CLIENT_CONNECT_MODE and MCP_INBOUND_PROTOCOL_MODE now default to auto, inbound MCP era negotiation is complete, completion/complete is forwarded to the owning upstream, downstream MCP catalog lists are paginated, and federated resource names carry the gateway name as a prefix.
  • Security & Auth - Added private_key_jwt token 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.
  • API & Platform - Added named A2A response models, the invitee invitations API, passwordless SSO user provisioning, per-user MCP server rate limiting, and success and error counts in the metrics timeseries.
  • Operations - Added 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-tree mcp-servers/ directory.
  • Testing - Eliminated pytest warnings and redundant tests, restored OAuth gateway membership coverage, verified resource template federation, and fixed the live-gateway E2E suite.

Added

Protocol & Transport

  • Modern MCP protocol negotiation is on by default (#7061) - MCP_CLIENT_CONNECT_MODE and MCP_INBOUND_PROTOCOL_MODE now default to auto instead of legacy. Outbound upstream connections probe server/discover and negotiate the 2026-07-28 revision, with transparent fallback to the legacy initialize handshake. Inbound clients may send mcp-protocol-version: 2026-07-28 instead of receiving a 400. Set either variable to legacy to restore the previous behaviour.

  • Inbound MCP era negotiation (#7156) - Completed inbound MCP era negotiation.

  • Federated completions (#6629, #6760) - Forward completion/complete to 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 serverInfo stamping (#6566, #7145) - The streamable HTTP server is configured with the app name and version for serverInfo.

  • Federated resource names prefixed with gateway names (#6780) - Federated resource names carry the owning gateway's name as a prefix.

  • structuredContent accepts 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) as structuredContent. The 2025 wire is unchanged: non-object values are stripped and the text copy in content is 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:

    SELECT name, COUNT(*) AS duplicate_count FROM tools WHERE visibility = 'public' GROUP BY name HAVING COUNT(*) > 1 ORDER BY name;
    SELECT team_id, name, COUNT(*) AS duplicate_count FROM tools WHERE visibility = 'team' GROUP BY team_id, name HAVING COUNT(*) > 1 ORDER BY team_id, name;
    SELECT owner_email, name, COUNT(*) AS duplicate_count FROM tools WHERE visibility = 'private' GROUP BY owner_email, name HAVING COUNT(*) > 1 ORDER BY owner_email, name;

API & Platform

  • Named response models for A2A routes (#6820, #6699) - Added named response models for POST /v1/a2a/{agent_name}/invoke, POST /v1/a2a/invoke, and POST /v1/a2a/{agent_name}/jsonrpc. The OpenAPI schema now exposes typed components while preserving the routes' passthrough behavior.
  • Invitee invitations API (#6960) - Added an API for invitee invitations.
  • Per-user MCP server rate limiting (#7191) - Adopted per-user rate limiting for MCP servers.
  • Metrics timeseries counts (#7138) - The observability metrics timeseries returns success and error counts.

Security & Auth

  • private_key_jwt token endpoint client authentication (#6991) - OAuth token requests can authenticate with RFC 7523 private_key_jwt.
  • Passwordless SSO user provisioning (#6692, #7180, #7219) - Added a passwordless SSO user provisioning service and its API. #7180 gated the API behind a feature flag. #7219 removes that flag.
  • JWT lifecycle clarification after database reset - Local JWTs remain cryptographically valid when database storage is lost but the signing key remains unchanged. Database cleanup is not credential rotation. Destructive resets must rotate JWT_SECRET_KEY when old-token invalidation is required. Persistent database storage and short-lived local tokens remain recommended for production deployments.

Operations & Tooling

  • Container image version bumper (#5811) - Added make container-bump-image-versions to bump container image version pins.
  • CSS linting moves from Stylelint to Biome (#7162) - make lint-web and the lint-web CI job now lint mcpgateway/static/*.css with Biome, configured in biome.json. The CI matrix entry is renamed from stylelint to biome-css. Stylelint and its configs leave the dev dependencies, which removes the braces advisory (GHSA-vfj7-8cjw-p6xm) from npm audit.
  • make testing-up web UI URL (#6415) - The startup summary now shows a ContextForge Web UI row whose port comes from the resolved Compose configuration. The printed URL now matches the port Compose publishes when WEB_UI_PORT is set only in .env. Resolution failure stops the target instead of printing a fallback URL.
  • Pinned-resource load test (#6984) - Added a pinned-resource performance benchmark stack and run history to the load test suite.
  • mcp-servers/ directory removed (#7078) - The unsupported sample/test MCP servers, the Go/Python scaffolding templates, and the mcp-url-to-markdown-tests workflow leave the repository. The six Python servers now live in IBM/contextforge-examples under mcp-servers/python/; the scaffolding templates are deleted without migration. The performance compose generator and benchmark docs now use the published ghcr.io/ibm/cfex-mcp-fast-time-server and ghcr.io/ibm/cfex-mcp-benchmark-server images. The /v1/mcp-servers REST API is unchanged: it remains the product-language alias for the gateways API.

ContextForge Web UI

  • New Web UI v0.2.4 (release notes) - Introduces a redesigned, more user-friendly interface for ContextForge, separate from the built-in Admin UI. The Web UI runs as a separate application and targets ContextForge API v1.1.0.

  • Docker Compose startup - From a configured gateway checkout, start the ui profile:

    make compose-pull PROFILE="--profile ui"
    make compose-up PROFILE="--profile ui"

    PROFILE replaces the Makefile's default profiles. On linux/amd64, use PROFILE="--profile with-fast-time --profile ui" in both commands to retain the default fast-time service.

    Open http://localhost:3001, or the port configured through WEB_UI_PORT. Compose starts the gateway, Web UI v0.2.4, and dedicated UI Redis session store. The UI connects to http://gateway:4444 inside the Compose network.

  • Separate npm startup - Install Node.js 22.22.1 or later and npm. Configure the gateway's .env and generate required secrets with make setup before the first run. For an existing .env, use make init-secrets-patch-env to replace weak or placeholder secrets. Start the configured gateway in one terminal:

    make dev

    In another terminal, clone the UI release and install both frontend and BFF dependencies:

    git clone --branch v0.2.4 --depth 1 https://github.com/contextforge-org/contextforge-web-ui.git
    cd contextforge-web-ui
    cp .env.example .env
    npm install
    npm install --prefix server

    Set CONTEXTFORGE_URL=http://localhost:8000, COOKIE_SECURE=false, and PORT=3000 in the UI's .env for local HTTP. Adjust CONTEXTFORGE_URL to the gateway's actual address and port. Leave REDIS_URL unset to use the in-process development session store; sessions reset when the BFF restarts.

    npm run dev:all

    Open http://localhost:3000. This command starts the BFF and Vite frontend together. Access the UI through the BFF on port 3000, which proxies frontend requests to Vite on port 5173. See the v0.2.4 setup guide for configuration details.

Breaking Changes

  • Downstream MCP catalog pagination (#6990, #7171) — Python MCP lists now return at most MCP_LIST_PAGE_SIZE=100 items by default. Clients that ignore nextCursor receive only the first page. Database order changes from created_at DESC to id 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. Share AUTH_ENCRYPTION_SECRET across 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 #6990. See pagination configuration.
  • Completion authorization (#6760) - POST /protocol/completion/complete now requires tools.read RBAC permission. Previously, authentication alone passed this endpoint's authorization check. Before upgrading from 1.0.11, grant tools.read to 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.
  • Experimental anyio cancellation monkey-patch removed (#5427) - Removed the opt-in ANYIO_CANCEL_DELIVERY_PATCH_ENABLED and ANYIO_CANCEL_DELIVERY_MAX_ITERATIONS workaround from mcpgateway/transports/sse_transport.py, together with the SSE_TASK_GROUP_CLEANUP_TIMEOUT setting. The gateway now requires anyio>=4.15.0, which fixes the _deliver_cancellation CPU 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. Dangling MCP_SESSION_POOL_CLEANUP_TIMEOUT references, 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; see docs/docs/operations/cpu-spin-loop-mitigation.md.
  • Rust MCP config surface removed (#7255, #6177) - Removed the Rust MCP configuration surface (closes #5819) and the Python Rust-runtime proxy integration (closes #5818), together with the remaining Rust MCP references in the Makefile, docs, and comments.
  • x-mcp-header annotation validation on tool registration (#6773) - POST /tools (create) and PUT /tools/{id} (update) now validate x-mcp-header annotations in input_schema against 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-conforming x-mcp-header values before upgrading.

Security

  • Configuration import ownership enforcement (#7243) - POST /v1/import with conflict_strategy=update no longer overwrites tools, resources, prompts, or gateways that the importer does not own. Bulk registration now runs check_resource_ownership() before it modifies an existing row and reports Permission denied for rows the caller does not own. Bulk update without a caller identity changes nothing. The import call to update_gateway() now passes the importer identity. The admin.import routes use global_only=True, so a team-scoped role no longer satisfies them. The Admin UI import now passes the authenticated email as imported_by. Deployments that grant admin.import through a team-scoped role must move that grant to a global role.
  • MCP Origin/Host enforcement (#6875) - Implements MCP 2025-11-25 §transport-security: a present-but-unlisted Origin header on /mcp is now rejected with HTTP 403. Set MCP_ALLOWED_ORIGINS to a comma-separated list of allowed origins to enable enforcement (default: empty, backward-compatible). An optional companion setting MCP_ALLOWED_HOSTS enforces exact host:port matching on the Host header. Enforcement runs at the public /mcp mount via MCPOriginHostGate, covering the Python ingress path.
  • A2A plugin header trust (#7157) - A2A pre-invoke plugins can supply credentials and custom headers without the agent caller-header allowlist. Plugin output now overrides configured agent authentication, and an empty Authorization value suppresses all fallback authorization. The gateway retains inbound caller filtering, sanitizes plugin input, resolves output case-insensitively, and strips X-Vault-Tokens before egress.
  • Outbound request validation in the DCR path (#6961) - Dynamic client registration validates and pins outbound requests.
  • Rate limit tier matching for /v1 paths (#6879) - Rate limit tier matching normalizes /v1 paths.
  • URL scheme allowlist at call time (#6881) - The gateway enforces the URL scheme allowlist at call time and scans the database on startup.
  • JSON Schema pattern evaluation (#6957) - The gateway bounds JSON Schema pattern evaluation in a worker process.
  • Outbound HTTP connection handling (#6965) - Hardened outbound HTTP connection handling. Outbound URL validation runs even when 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 /sse with 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.
  • Candidate credential handshakes (#6877) - Candidate credential handshakes are isolated.
  • Virtual-server scoped tool invocation (#6672) - Hardened virtual-server scoped tool invocation.
  • Token scoping for plugin-binding and completion routes (#6709) - Token scoping maps plugin-binding routes. Token scoping maps the completion route to tools.read.
  • Input validation and output escaping tightening (#7076) - Tightened input validation and output escaping across gateway paths. ADR-056 CSP hardening applies.
  • Result body HTML escaping (#7239) - escapeHtml is applied to the tool result body before rendering, preventing stored XSS in result output.
  • DCR outbound request pinning (#7081) - Dynamic client registration pins outbound requests to the validated endpoint address.

Fixed

Auth & OAuth

  • Email claim for generic OIDC providers (#6484) - SSO resolves an email claim for generic OIDC providers.
  • External-IdP verification in get_current_user() (#6424, #7049) - External-IdP verification is wired into get_current_user(), and all auth paths fall back to external IdP tokens.
  • private_key_jwt follow-ups (#7190) - Restored private_key_jwt follow-ups dropped from #6991.
  • OAuth token handling (#7240) - Improved OAuth token handling.
  • OAuth gateway catalog on empty fetch (#6049) - The gateway preserves its catalog when an OAuth fetch returns empty.

Gateway, Catalog & Tools

  • Frontend email routes (#6953) - Invitation and password emails include the React /app mount prefix. UI_BASE_URL accepts 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 root https://ui.example.com/contextforge/app/app to preserve both segments in emailed links. The invitation frontend route remains tracked by #6776.
  • Modern MCP clients no longer see the deprecated logging capability (#6630, #6631, #7030) - server/discover responses for 2026-07-28 clients now omit capabilities.logging. MCP 2026-07-28 deprecates that capability (SEP-2577), and ContextForge sends no request-scoped log messages. Clients on 2025-11-25 and older still receive the capability and can call logging/setLevel. Use OpenTelemetry and structured logging for gateway observability. Modern clients reach the gateway only when MCP_INBOUND_PROTOCOL_MODE=auto; docker-compose.yml now passes that variable to the gateway, with a legacy default.
  • Session-affinity worker ID (#6688) - The gateway computes the session-affinity worker ID from the live PID after fork.
  • Refresh throttle anchor (#7130) - The refresh throttle anchors to cycle_started_at, so the gateway no longer skips refreshes.
  • Gateway URL rejection messages (#7096) - Gateway URL rejections name the cause. Fixed CA signing status.
  • Oversize _meta (#7259) - The gateway rejects oversize _meta with -32602 and keeps the upstream resource error.
  • psutil.cpu_freq() on macOS (#7087) - Guarded psutil.cpu_freq() against SystemError.
  • Tool error translation (#7169) - Upstream JSON-RPC error codes are preserved in tool results. The error code is reported under _meta["io.contextforge/errorCode"] and the result retains the isError shape. 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.
  • Prompt error translation (#7204) - Prompt calls that fail with missing required arguments return -32602 (INVALID_PARAMS). Upstream JSON-RPC errors are relayed with their original code and sanitized data. Previously the shape and code varied across failure paths.
  • Error response format standardization (#7079) - JSON-RPC error response bodies are now formatted consistently across all gateway paths using a shared ErrorFormatter. Clients that parsed the raw detail or message field of error responses may need to update their parsers.

Observability & A2A

  • W3C trace context in A2A invocations (#7093) - A2A invocations propagate W3C trace context.
  • Tenantless background spans (#7155) - OpenTelemetry suppresses tenantless background spans.

Admin UI

  • Admin UI edit modals (#7271) - Table rows stay in the innerHTML guard. Edit modals use delegated handlers.

Build, CI & Dependencies

  • OS package refresh before Docker security scans (#7186) - CI refreshes OS packages before Docker security scans.
  • Checkov skip placement - Moved the checkov skip off the nginx FROM line.

Changed

  • Duplicate Gateway.last_seen annotation removed (#6682) - Removed the duplicate model annotation.

Documentation

  • Container image version bumping (#7137) - Replaced the manual UBI tag steps with make container-bump-image-versions.
  • Deprecation lifecycle (#6996) - Adopted the MCP-style deprecation lifecycle and recorded the deprecation backlog.

Testing

  • Pytest warnings and redundant tests (#6895) - Eliminated pytest warnings, dead test code, and redundant auth tests.
  • Resource template federation (#6962) - Verified resource template federation through the gateway.
  • OAuth gateway membership (#7210) - Restored OAuth gateway membership regression coverage.
  • Pre-provisioned SSO (#7183) - Validated pre-provisioned SSO authentication and documented onboarding.
  • Live-gateway E2E suite - Fixed suite ordering, error shapes, and cleanup.

Chores

PR Description
#7132 routine dependencies update - 2026-10-05
#7135 migrate Tailwind CSS v3 to v4 and fix npm CVEs
#6769 validate the Code Engine registry secret and clarify env logging
#7213 rate limit middleware updates
#7250 replace mcp-server-filesystem examples with mcp-server-fetch
#7257 routine dependency update
#7267 docker-compose.yml dual-era test configuration
#7199 follow-up to #6877
#7201 follow-up to #7157; ADR-057
#7200 follow-up to #6996

Release preparation bumped every version reference to 1.1.0, refreshed npm dependencies (12 packages updated, 0 vulnerabilities), bumped the NODEJS_IMAGE pin to 10.2-1792386062, updated uv.lock and Cargo.lock, normalised .bumpversion.cfg indentation, and removed Rust MCP leftovers from the Makefile, docs, and comments.

Don't miss a new mcp-context-forge release

NewReleases is sending notifications on new releases.