Skip to main content
Pass mcpServers when you open a thread. Those servers are attached to this thread only.
Supported shapes:
  • { type: "http" | "sse", url, headers? }
  • { type: "stdio", command, args?, env? }
Supplying mcpServers turns on strict mode unless you set loadUserMcpServers: true. Strict means: withhold the user’s and project’s own MCP config, and try to load only the servers you supplied.
Strict mode is enforced only on Claude. Codex, Cursor, Droid, and OpenCode are best-effort with a named residual. Pi has no MCP surface — injected servers are refused rather than opening a tool-less thread. Do not tell your users “only your tools are loaded” without checking thread.mcpCapability.

Honesty table

mcpCapability

Every thread that requested MCP reports what it actually got. null means one of two things: this thread never asked for MCP, or it asked and an older runtime omitted the report. The SDK logs the second case (requested MCP but the runtime reported no capability). Do not read null as “no MCP was requested” when you passed mcpServers or loadUserMcpServers: false — treat the guarantee as unverified.
On a delivery-only thread (loadUserMcpServers: true) the user’s own MCP loads by design. strictRequested is false, residual is null, and level makes no isolation claim. Treating level === "enforced" as “only my tools” on that thread would be backwards. Presence of the mcpCapability object is not a guarantee. Read strictRequested, then branch on level. After setModel, the report is refreshed. A Claude thread switched to Codex will not keep advertising enforced.

Opt back in

An explicit false is not the same as omitting the field. Omitting both mcpServers and loadUserMcpServers lets the embedded session profile decide (SDK threads are strict by default).

Next

Chat UI

Show tool activity to your users as labels, not raw tool names.