⚠️ 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
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):
[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):
[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=valueassignments must move to anenvmapping on the server entry. - Redirects are no longer followed. If an
allowed_urlsentry or a serverurlrelied on anhttp→httpsupgrade redirect, configure the finalhttps://URL directly. - URLs must not contain
./..segments, encoded separators (%2e,%2f,%5c), double-encoded sequences (%25), backslashes, or non-ASCII characters. - A
user_serversconnection'snamecannot match (case-insensitively, ignoring surrounding whitespace) thenameof 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]]withtype = "stdio"and acommand— a client-suppliedfullCommandis rejected, and inlineKEY=valueassignments must move to anenvmapping on the server entry typeis now required on every[[features.mcp.servers]]entry —StdioMcpServer,SseMcpServerandStreamableHttpMcpServerno 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 = trueand a non-emptyallowed_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
/mcpreturnsisUserProvidedinstead ofurl/headersfor developer-configured (named) servers@chainlit/react-client0.5.0 removesconnectStdioMCP(),connectSseMCP()andconnectStreamableHttpMCP()— useconnectMcp()for named servers andconnectUserMcp()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
/mcpendpoint — 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-Authorizationand the method/URL override headers from user-provided MCP connections - Stop disclosing the
user_serversallowlist 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,pyjwtandpython-multipart, and pin more than thirty vulnerable JS dependencies — includinglodash,postcss,micromatch,form-data,undici,wsandrollup— to patched ranges across all four workspaces - Upgrade
react-router-domto 6.30.6, clearing an open-redirect to XSS advisory that covered every previously shipped 6.30.x - Upgrade
socket.io-clientto 4.8.3 in the published@chainlit/react-client, moving itsengine.io-client/wschain onto patched versions — the one dependency change here that reaches downstream npm consumers
Added
- Add a localized tooltip to the settings icon
- Add
SECURITY.mdwith 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_overridesinstead 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
nullmime instead of crashing the thread view - Reconstruct uploaded PDFs as
Pdfelements inElement.from_dict - Handle a missing
userEnvpayload on WebSocket connect - Validate
DatePickermode andmin_date/max_datebounds - Scope cache entries by function identity so same-named callables no longer return each other's values
- Degrade emoji markers in
lint-translationsinstead of raisingUnicodeEncodeErroron legacy consoles - Expose the OAuth2 model on
OAuth2PasswordBearerWithCookieso OpenAPI generation no longer raises - Resolve the transparent Copilot UI in light mode
Other changes
- Declare
pydantic>=2.11.0explicitly, narrowing the installable range from>=2.7.2; this was already required transitively bymcp>=1.28.1, so no install that resolves today stops resolving - Drop the unused
audioop-ltscore 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 #2903
- docs: add SECURITY.md with responsible disclosure policy by @dokterbob in #2900
- fix(teams): support single-tenant bots via TEAMS_APP_TENANT_ID env var by @xodn348 in #2928
- fix(socket): handle missing user env by @pragnyanramtha in #2927
- ci: install Cypress binary before e2e tests by @lntutor in #2989
- fix(elements): render file elements with null mime instead of crashing by @mihidumh in #2939
- fix(settings): validate DatePicker mode and bounds by @lntutor in #2988
- Fix: Resolve transparent UI issue in Copilot Light Mode by @taeminlee in #2977
- fix(elements): reconstruct uploaded PDFs as Pdf elements in Element.from_dict by @lntutor in #2984
- chore(deps): drop unused
audioop-ltscore dependency by @lukehsiao in #2980 - fix(cache): scope entries by function identity by @lntutor in #2987
- fix(cli): avoid lint-translations UnicodeEncodeError on legacy consoles by @lntutor in #3006
- fix(auth): expose cookie OAuth model for OpenAPI by @gaoflow in #2968
- fix(deps): bump lodash to 4.18.1 for prototype pollution fix by @EyalAmitay in #3012
- fix(auth): redirect OAuth login failures to login page instead of raw JSON (#1273) by @dokterbob in #2955
- chore(deps): upgrade dependencies with known vulnerabilities by @dokterbob in #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 #2928
- @pragnyanramtha made their first contribution in #2927
- @lntutor made their first contribution in #2989
- @lukehsiao made their first contribution in #2980
- @gaoflow made their first contribution in #2968
Full Changelog: 2.11.1...2.12.0
@chainlit/react-client 0.5.0 is released alongside this version.