2.12.0
TEN-framework/ten-framework2.12.0Aug 25, 2026by dokterbob
Upgrading?
AI Summary
A critical security release addressing vulnerabilities in the MCP endpoint, requiring significant architectural changes to the configuration structure and removing client-side command injection risks.
Key Highlights
- Fixes critical command injection (CVE-2026-45018) and SSRF (CVE-2026-45019) vulnerabilities.
- Major refactoring of MCP configuration from transport-specific keys to a unified `[[features.mcp.servers]]` array.
- Security hardening of dependencies including `react-router-dom` and `socket.io-client`.
Breaking Changes
- Legacy MCP config keys ([features.mcp.sse], etc.) now abort startup instead of being ignored.
- Client-side `connectStdioMCP` removed from `@chainlit/react-client`.
- MCP URLs no longer follow HTTP redirects.
- User-provided connections require explicit `[features.mcp.user_servers] enabled = true`.
New Features
- Single-tenant Azure Bot registration support for Teams.
- Localized tooltip for the settings icon.
- Added `SECURITY.md` with a responsible disclosure policy.
Full Release Notes
## ⚠️ Security release — breaking changes, action required if you use MCP
This release fixes two vulnerabilities in the `/mcp` endpoint, both exploitable by an **unauthenticated** attacker whenever `features.mcp.enabled = true`.
| CVE | Advisory | CVSS v3.1 | Severity | Issue |
| --- | --- | --- | --- | --- |
| CVE-2026-45018 | SPL-2026-001 | 9.8 | Critical | Command injection via the MCP stdio transport |
| CVE-2026-45019 | SPL-2026-002 | 7.2 | High | SSRF via the MCP streamable-http / SSE transports |
**Affected**: v2.4.0rc0 – v2.11.x. MCP has been disabled by default since v2.7.0, which limits real-world exposure — if you never set `features.mcp.enabled = true`, you were not vulnerable.
**If you cannot upgrade immediately**, set `features.mcp.enabled = false` (the default). This fully prevents exploitation of both issues.
📄 **Full technical detail, impact analysis and mitigations: [`docs/security-advisory-2026-mcp.md`](https://github.com/Chainlit/chainlit/blob/main/docs/security-advisory-2026-mcp.md)**
The fix for the command injection is architectural rather than filtering — no argument-level validation can sandbox a command whose full argument list the attacker controls. `fullCommand` is removed from the client request entirely: stdio servers are now declared only in server-side config, and the client sends just a name.
---
## 🔧 Migration guide
### 1. MCP config (`.chainlit/config.toml`)
Legacy MCP keys now **abort startup** instead of being silently ignored. If your config contains `[features.mcp.sse]`, `[features.mcp.stdio]`, `[features.mcp.streamable-http]` or `allowed_executables`, the app will refuse to start until you migrate.
**Before (v2.11.x):**
```toml
[features.mcp]
enabled = true
[features.mcp.stdio]
enabled = true
allowed_executables = ["npx", "uvx"]
[features.mcp.sse]
enabled = true
allowed_urls = ["https://mcp.example.com"]
```
**After (v2.12.0):**
```toml
[features.mcp]
enabled = true
# Developer-configured servers (replaces allowed_executables / allowed_urls)
[[features.mcp.servers]]
name = "github"
type = "stdio"
command = "npx -y @modelcontextprotocol/server-github"
[[features.mcp.servers]]
name = "my-sse"
type = "sse"
url = "https://mcp.example.com/sse"
# Optional: allow end-users to connect their own SSE/HTTP servers
[features.mcp.user_servers]
enabled = true
allowed_urls = ["https://mcp.example.com"]
```
Additional notes:
- Inline `KEY=value` assignments must move to an `env` mapping on the server entry.
- **Redirects are no longer followed.** If an `allowed_urls` entry or a server `url` relied on an `http` → `https` upgrade redirect, configure the final `https://` URL directly.
- URLs must not contain `.`/`..` segments, encoded separators (`%2e`, `%2f`, `%5c`), double-encoded sequences (`%25`), backslashes, or non-ASCII characters.
- A `user_servers` connection's `name` cannot match (case-insensitively, ignoring surrounding whitespace) the `name` of any server in `[[features.mcp.servers]]`.
### 2. `@chainlit/react-client` 0.5.0
| Before (v2.11.x) | After (v2.12.0) |
| --- | --- |
| `connectStdioMCP(sessionId, name, fullCommand)` | **Removed, no replacement** — see below |
| `connectSseMCP(sessionId, name, url, headers?)` | `connectUserMcp(sessionId, name, 'sse', url, headers?)` |
| `connectStreamableHttpMCP(sessionId, name, url, headers?)` | `connectUserMcp(sessionId, name, 'streamable-http', url, headers?)` |
| _(n/a)_ | `connectMcp(sessionId, name)` — connects any named server |
`connectStdioMCP` is removed with no replacement: accepting a client-supplied command *was* CVE-2026-45018. Declare stdio servers in `[[features.mcp.servers]]` and connect by name with `connectMcp(sessionId, name)`.
`IMcp` also changed: `clientType` is now optional and narrowed to `'sse' | 'streamable-http'`, `type` was added for named servers, `command` was removed, and `isUserProvided` was added — check that flag directly rather than inferring from the presence of `url` or `clientType`.
`IChainlitConfig.features.mcp` changed to match: the per-transport `sse` / `streamable_http` / `stdio` flag objects are replaced by `servers?: Array<{ name, type }>` and `user_servers?: { enabled?: boolean }`.
---
## ⚠️ Breaking changes
- Legacy MCP config keys (`[features.mcp.sse]`, `[features.mcp.stdio]`, `[features.mcp.streamable-http]`, `allowed_executables`) abort startup when MCP is enabled instead of being silently ignored; they are replaced by a unified `[[features.mcp.servers]]` array and an optional `[features.mcp.user_servers]` section
- stdio MCP servers must be declared in `[[features.mcp.servers]]` with `type = "stdio"` and a `command` — a client-supplied `fullCommand` is rejected, and inline `KEY=value` assignments must move to an `env` mapping on the server entry
- `type` is now required on every `[[features.mcp.servers]]` entry — `StdioMcpServer`, `SseMcpServer` and `StreamableHttpMcpServer` no longer default it, so servers constructed in Python must pass it explicitly
- User-provided SSE/HTTP connections require an explicit `[features.mcp.user_servers] enabled = true` and a non-empty `allowed_urls`, where they were previously enabled by default
- MCP connections no longer follow HTTP redirects, for developer-configured servers as well as user-provided ones — configure the final `https://` URL directly
- User-provided MCP connections are re-checked against their allowlist entry on every request rather than only the first
- Duplicate, empty and colliding MCP server names are rejected instead of loading silently
- `/mcp` returns `isUserProvided` instead of `url`/`headers` for developer-configured (named) servers
- `@chainlit/react-client` 0.5.0 removes `connectStdioMCP()`, `connectSseMCP()` and `connectStreamableHttpMCP()` — use `connectMcp()` for named servers and `connectUserMcp()` for user-provided ones
## Security
- Fix critical command injection (CVE-2026-45018, SPL-2026-001) and SSRF (CVE-2026-45019, SPL-2026-002) in the `/mcp` endpoint — stdio MCP servers are now defined server-side and the client supplies only a name
- Filter `Cookie`, `Host`, `Forwarded`, `X-Forwarded-*`, `X-Real-IP`, `Via`, `Proxy-Authorization` and the method/URL override headers from user-provided MCP connections
- Stop disclosing the `user_servers` allowlist and server details through `/project/settings`
- Reject MCP URLs containing `.`/`..` segments, encoded separators, double-encoded sequences, backslashes or non-ASCII characters
- Raise backend minimum versions for `mcp`, `pydantic`, `pydantic-settings`, `pyjwt` and `python-multipart`, and pin more than thirty vulnerable JS dependencies — including `lodash`, `postcss`, `micromatch`, `form-data`, `undici`, `ws` and `rollup` — to patched ranges across all four workspaces
- Upgrade `react-router-dom` to 6.30.6, clearing an open-redirect to XSS advisory that covered every previously shipped 6.30.x
- Upgrade `socket.io-client` to 4.8.3 in the published `@chainlit/react-client`, moving its `engine.io-client`/`ws` chain onto patched versions — the one dependency change here that reaches downstream npm consumers
## Added
- Add a localized tooltip to the settings icon
- Add `SECURITY.md` with a responsible disclosure policy
- Support single-tenant Azure Bot registrations for Teams via `TEAMS_APP_TENANT_ID`
## Fixed
- Bound the MCP connect handshake so a blocked destination fails fast instead of hanging and leaking its connection task
- Keep the existing MCP session until a reconnect has succeeded, so a failed reconnect no longer drops a working connection
- Serialise concurrent reconnects to the same MCP server name to avoid leaking a live connection
- Report the underlying cause of an MCP connect failure instead of an empty error
- Drop malformed stored MCP entries instead of letting them break the chat page
- Resolve MCP servers declared in a chat profile's `config_overrides` instead of returning a 500 from `/project/settings`
- Redirect OAuth login failures to the login page with a friendly error instead of raw JSON or a bare 500
- Render file elements with a `null` mime instead of crashing the thread view
- Reconstruct uploaded PDFs as `Pdf` elements in `Element.from_dict`
- Handle a missing `userEnv` payload on WebSocket connect
- Validate `DatePicker` mode and `min_date`/`max_date` bounds
- Scope cache entries by function identity so same-named callables no longer return each other's values
- Degrade emoji markers in `lint-translations` instead of raising `UnicodeEncodeError` on legacy consoles
- Expose the OAuth2 model on `OAuth2PasswordBearerWithCookie` so OpenAPI generation no longer raises
- Resolve the transparent Copilot UI in light mode
## Other changes
- Declare `pydantic>=2.11.0` explicitly, narrowing the installable range from `>=2.7.2`; this was already required transitively by `mcp>=1.28.1`, so no install that resolves today stops resolving
- Drop the unused `audioop-lts` core dependency
---
## 🙏 Credit
CVE-2026-45018 and CVE-2026-45019 were reported by **Vipin** and **Stephen** at **SPL Security** (security@spl.team) under coordinated disclosure, with working proof-of-concept exploits for both issues. We thank them for a thorough and responsibly disclosed report.
---
## Merged pull requests
* feat: add missing tooltip to settings icon by @eiseleMichael in https://github.com/Chainlit/chainlit/pull/2903
* docs: add SECURITY.md with responsible disclosure policy by @dokterbob in https://github.com/Chainlit/chainlit/pull/2900
* fix(teams): support single-tenant bots via TEAMS_APP_TENANT_ID env var by @xodn348 in https://github.com/Chainlit/chainlit/pull/2928
* fix(socket): handle missing user env by @pragnyanramtha in https://github.com/Chainlit/chainlit/pull/2927
* ci: install Cypress binary before e2e tests by @lntutor in https://github.com/Chainlit/chainlit/pull/2989
* fix(elements): render file elements with null mime instead of crashing by @mihidumh in https://github.com/Chainlit/chainlit/pull/2939
* fix(settings): validate DatePicker mode and bounds by @lntutor in https://github.com/Chainlit/chainlit/pull/2988
* Fix: Resolve transparent UI issue in Copilot Light Mode by @taeminlee in https://github.com/Chainlit/chainlit/pull/2977
* fix(elements): reconstruct uploaded PDFs as Pdf elements in Element.from_dict by @lntutor in https://github.com/Chainlit/chainlit/pull/2984
* chore(deps): drop unused `audioop-lts` core dependency by @lukehsiao in https://github.com/Chainlit/chainlit/pull/2980
* fix(cache): scope entries by function identity by @lntutor in https://github.com/Chainlit/chainlit/pull/2987
* fix(cli): avoid lint-translations UnicodeEncodeError on legacy consoles by @lntutor in https://github.com/Chainlit/chainlit/pull/3006
* fix(auth): expose cookie OAuth model for OpenAPI by @gaoflow in https://github.com/Chainlit/chainlit/pull/2968
* fix(deps): bump lodash to 4.18.1 for prototype pollution fix by @EyalAmitay in https://github.com/Chainlit/chainlit/pull/3012
* fix(auth): redirect OAuth login failures to login page instead of raw JSON (#1273) by @dokterbob in https://github.com/Chainlit/chainlit/pull/2955
* chore(deps): upgrade dependencies with known vulnerabilities by @dokterbob in https://github.com/Chainlit/chainlit/pull/2999
* **fix(mcp)!: prevent unauthenticated RCE and SSRF via `/mcp` (CVE-2026-45018, CVE-2026-45019)** — reported by SPL Security, fixed under coordinated disclosure
## New Contributors
* @xodn348 made their first contribution in https://github.com/Chainlit/chainlit/pull/2928
* @pragnyanramtha made their first contribution in https://github.com/Chainlit/chainlit/pull/2927
* @lntutor made their first contribution in https://github.com/Chainlit/chainlit/pull/2989
* @lukehsiao made their first contribution in https://github.com/Chainlit/chainlit/pull/2980
* @gaoflow made their first contribution in https://github.com/Chainlit/chainlit/pull/2968
**Full Changelog**: https://github.com/Chainlit/chainlit/compare/2.11.1...2.12.0
_`@chainlit/react-client` 0.5.0 is released alongside this version._
Related Repositories
public-apis/public-apis★ 491,123
A collective list of free APIs
NousResearch/hermes-agent★ 255,883
The agent that grows with you
yt-dlp/yt-dlp★ 199,986
A feature-rich command-line audio/video downloader
microsoft/markitdown★ 191,478
Python tool for converting files and office documents to Markdown.
Significant-Gravitas/AutoGPT★ 187,698
AutoGPT is the vision of accessible AI for everyone, to use and to build on. Our mission is to provide the tools, so that you can focus on what matters.