2.12.0

Chainlit/chainlit2.12.0Aug 25, 2026by dokterbob

AI Summary

A critical security release addressing command injection and SSRF vulnerabilities in the `/mcp` endpoint. It introduces a major refactoring of the MCP configuration structure and updates client-side APIs to eliminate exploitable client-side command passing.

Key Highlights

  • Fixes critical command injection (CVE-2026-45018) and SSRF (CVE-2026-45019).
  • Refactors MCP config from per-transport keys to a unified `[[features.mcp.servers]]` array.
  • Removes `connectStdioMCP` from client-side API to prevent client-side command injection.
  • Adds `connectMcp` and `connectUserMcp` helper functions.

Breaking Changes

  • Legacy MCP config keys ([features.mcp.sse], [features.mcp.stdio], etc.) abort startup.
  • Client-side functions `connectStdioMCP`, `connectSseMCP`, and `connectStreamableHttpMCP` are removed.
  • MCP connections no longer follow HTTP redirects.
  • User-provided connections require explicit `[features.mcp.user_servers] enabled = true`.

New Features

  • New `connectMcp(sessionId, name)` for named servers
  • New `connectUserMcp(sessionId, name, type, url, headers?)` for user-provided connections
  • Added `SECURITY.md` documentation
  • Support for single-tenant Azure Bot registrations
  • Localized tooltip to settings icon

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._