Plugin as MCP Host: How Codex CLI v0.141 Turns Plugins into Per-Thread Tool Servers
Plugin as MCP Host: How Codex CLI v0.141 Turns Plugins into Per-Thread Tool Servers
When OpenAI launched the Codex plugin marketplace on 27 March 2026, plugins were distribution units — bundles of skills, app connectors, and MCP server declarations that Codex loaded at session start1. Three months and fifteen releases later, plugins have quietly become something more consequential: per-thread MCP hosts whose stdio servers start on demand, run inside the executor’s environment, and expose tools scoped exclusively to the selecting thread23. Combined with the v0.142 marketplace reorganisation into Curated, Workspace, and Shared categories4, this creates a governed, composable tool-server architecture that merits attention from anyone building enterprise Codex workflows.
The Problem: Static MCP, Dynamic Work
Before v0.141, MCP server activation in Codex followed a session-level model. You declared servers in .codex/mcp.json or requirements.toml, and they started when the session started5. Every thread within that session shared the same MCP tool surface. This created three friction points:
- Tool sprawl — a Kubernetes debugging MCP server exposed
kubectltools to threads working on React components. - Environment mismatch — remote executor plugins declaring MCP servers had those declarations silently ignored because the app-server only resolved local plugin filesystems2.
- No dynamic composition — selecting a plugin at thread start did not activate its bundled MCP servers, forcing manual configuration.
The fundamental issue was an architectural mismatch: plugins were selected per-thread, but MCP servers were activated per-session.
The v0.141 Solution: Per-Thread Plugin MCP Activation
Pull requests #27870 and #2789323 introduced a three-layer activation flow that resolves this mismatch:
sequenceDiagram
participant Client
participant AppServer as App Server
participant Executor
participant MCP as Stdio MCP Server
Client->>AppServer: thread/start(selectedCapabilityRoots)
AppServer->>Executor: Resolve plugin filesystem
Executor-->>AppServer: .mcp.json contents
AppServer->>AppServer: Filter stdio-only declarations
AppServer->>AppServer: Freeze MCP snapshot for thread
AppServer->>MCP: Start stdio server in executor env
MCP-->>AppServer: Tool advertisements
AppServer-->>Client: Thread ready (tools available)
Note over Client,MCP: Unselected threads cannot access these tools
Step 1: Plugin Resolution Through Executor Filesystem
When thread/start includes selectedCapabilityRoots, the app-server resolves each plugin through its owning executor’s filesystem — not the host filesystem2. This is a deliberate security boundary: the resolution uses the exact filesystem capability granted to that executor, with no host-filesystem fallback.
Step 2: Stdio-Only Filtering and Normalisation
The system reads the plugin’s .mcp.json, filters to stdio declarations only (HTTP declarations are skipped with warnings until placement semantics are defined2), and normalises server entries with executor-bound environment IDs.
Step 3: Frozen Snapshot
Discovered MCP declarations are frozen once per active thread runtime23. This prevents dynamic changes mid-execution — a deliberate design choice that ensures thread-level tool consistency. Standard MCP config reloads preserve frozen registrations3.
The .mcp.json Plugin Configuration
A plugin that bundles an MCP server declares it in its .mcp.json at the plugin root:
{
"mcpServers": {
"k8s-tools": {
"command": "node",
"args": ["./mcp-servers/k8s/index.js"],
"transport": "stdio",
"env": {
"KUBECONFIG": "${EXECUTOR_KUBECONFIG}"
}
},
"monitoring-api": {
"url": "https://monitor.internal/mcp",
"transport": "http"
}
}
}
In this example, k8s-tools activates as a stdio server in the executor’s environment when the plugin is selected. monitoring-api is skipped — HTTP transport activation is not yet supported in the per-thread model2.
Per-Thread Isolation: What It Means in Practice
The isolation guarantees are strict3:
- MCP tools from a selected plugin are advertised only to the selecting thread
- The MCP process receives executor-only environment values
- Unselected threads cannot access these MCP servers
- Sessions without
selectedCapabilityRootsremain entirely unchanged
This means a team can have one thread debugging a Kubernetes cluster with kubectl tools, another thread reviewing database schemas with a PostgreSQL MCP server, and a third thread writing documentation with no external tools — all within the same Codex session, with zero cross-contamination.
graph TB
Session["Codex Session"]
Session --> T1["Thread 1<br/>Plugin: k8s-debugger"]
Session --> T2["Thread 2<br/>Plugin: db-tools"]
Session --> T3["Thread 3<br/>No plugin selected"]
T1 --> MCP1["stdio: kubectl-mcp<br/>Tools: get_pods, describe_node"]
T2 --> MCP2["stdio: pg-mcp<br/>Tools: query, list_tables"]
T3 --> NoMCP["No MCP tools"]
style T1 fill:#e1f5fe
style T2 fill:#f3e5f5
style T3 fill:#f5f5f5
style MCP1 fill:#e1f5fe
style MCP2 fill:#f3e5f5
Marketplace Reorganisation: Curated, Workspace, Shared
The v0.142 release reorganised the /plugins command output into three distinct marketplace sections46:
| Section | Source | Governance |
|---|---|---|
| OpenAI Curated | OpenAI-vetted plugins available to all users | OpenAI review process |
| Workspace | Plugins distributed within a ChatGPT workspace | Workspace admin controls |
| Shared with me | Plugins shared by workspace members | Per-user sharing permissions |
For enterprise teams, the Workspace tier is the critical one. Administrators can curate a set of approved plugins — each bundling governed MCP servers — and distribute them across the organisation. Combined with requirements.toml managed hooks7, this creates a supply chain:
- Security team authors a plugin bundling a SAST MCP server
- Admin publishes it to the Workspace marketplace
- Developers select it per-thread when working on security-sensitive code
- The MCP server starts in the executor’s sandboxed environment
- PostToolUse hooks validate the MCP server’s outputs before they reach the model
Turn-Level Plugin Recommendations
A subtler v0.142 addition: eligible turns can now recommend and install relevant plugins4. When Codex detects that a task would benefit from a plugin it has not yet loaded, it surfaces a recommendation. This shifts plugin discovery from “browse the marketplace” to “the agent tells you what it needs.”
For the per-thread MCP model, this means a thread could recommend a plugin, the user approves, and the plugin’s stdio MCP servers activate — all without restarting the session or reconfiguring .codex/mcp.json.
Security Considerations
The per-thread MCP model introduces new security surface that deserves attention:
Executor-bound resolution — plugins resolve through their owning executor’s filesystem, not the host2. This prevents a compromised plugin from reading arbitrary host files during MCP server startup.
Stdio-only activation — HTTP MCP servers are deliberately excluded from per-thread activation2. This avoids the complexity of network-level isolation between threads and keeps the initial implementation surface small.
Frozen snapshots — once a thread’s MCP declarations are frozen, they cannot be modified by the plugin, the MCP server, or another thread23. This prevents time-of-check/time-of-use (TOCTOU) attacks where a plugin modifies its MCP configuration after initial validation.
Sandbox interaction — MCP servers started by plugins inherit the executor’s sandbox constraints. In full-auto mode with network disabled, a plugin’s MCP server cannot exfiltrate data over the network even if the MCP server code is compromised8.
However, plugin MCP servers do run arbitrary code within the executor environment. Enterprise teams should validate plugin .mcp.json declarations through managed requirements.toml policies7 and restrict Workspace marketplace publishing to trusted authors.
Practical Configuration
Selecting a Plugin MCP at Thread Start (App-Server API)
{
"method": "thread/start",
"params": {
"selectedCapabilityRoots": [
"plugin:@openai/k8s-debugger"
]
}
}
CLI Usage
From the TUI, the /plugins command shows available plugins grouped by marketplace source. Selecting a plugin for a new thread activates its bundled MCP servers automatically46.
Verifying MCP Tool Availability
Once a plugin MCP server activates, its tools appear in the thread’s tool list. You can confirm with:
codex --print-tools
Or within a session, the model will list available tools including those from the plugin’s MCP server.
Plugin MCP with PostToolUse Hooks
Combine plugin MCP tools with PostToolUse hooks for governed tool usage:
# requirements.toml
[hooks.PostToolUse]
command = "python3 scripts/validate_mcp_output.py"
match_tools = ["k8s-tools.*"]
This runs validation after every tool call from the k8s-tools MCP server, regardless of which thread activated it7.
What This Architecture Enables
The per-thread plugin MCP model enables patterns that were previously impossible or required manual orchestration:
- Multi-environment debugging — one thread connected to staging via a staging-k8s plugin, another to production, with complete tool isolation
- Governed tool distribution — security tools distributed as Workspace plugins with auditable activation and PostToolUse validation
- Dynamic capability acquisition — turn-level recommendations mean threads acquire tools as needed rather than pre-configuring every possible MCP server
- Plugin-as-microservice — each plugin becomes a self-contained tool server, composable at the thread level, version-managed through the marketplace
Current Limitations
Several constraints apply to the current implementation23:
- HTTP MCP servers are not activated per-thread; only stdio transport is supported
- Resume and fork operations do not yet persist per-thread MCP state across session boundaries
- Hosted plugins (cloud-hosted rather than executor-hosted) follow a different activation path not covered by this mechanism
- No hot-reload — changing a plugin’s
.mcp.jsonrequires starting a new thread; existing frozen snapshots are immutable
Conclusion
The shift from session-level MCP configuration to per-thread plugin MCP activation is architecturally significant. It transforms plugins from passive instruction bundles into active tool servers with thread-scoped isolation, executor-bound security, and marketplace-governed distribution. For enterprise teams, the combination of Workspace marketplace curation, requirements.toml policy enforcement, and per-thread tool scoping creates a governed extensibility model that scales across projects without sacrificing the isolation guarantees that make agent-driven workflows safe to deploy.
The pattern to watch is the eventual activation of HTTP MCP servers per-thread — once that lands, plugins will be able to host both local tools (stdio) and remote services (HTTP) with the same per-thread isolation model, completing the vision of plugins as fully self-contained capability units.
Citations
-
OpenAI, “Plugins — Codex,” OpenAI Developers, 2026. https://developers.openai.com/codex/plugins ↩
-
jif-oai, “Discover stdio MCP servers from selected executor plugins,” GitHub Pull Request #27870, openai/codex, June 2026. https://github.com/openai/codex/pull/27870 ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 ↩9 ↩10 ↩11
-
jif-oai, “Activate selected executor plugin MCPs in app-server,” GitHub Pull Request #27893, openai/codex, June 2026. https://github.com/openai/codex/pull/27893 ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7
-
OpenAI, “Changelog — Codex CLI v0.142.0,” OpenAI Developers, 22 June 2026. https://developers.openai.com/codex/changelog ↩ ↩2 ↩3 ↩4
-
OpenAI, “Configuration Reference — Codex,” OpenAI Developers, 2026. https://developers.openai.com/codex/config-reference ↩
-
Releasebot, “Codex Updates by OpenAI — June 2026,” Releasebot, 2026. https://releasebot.io/updates/openai/codex ↩ ↩2
-
Daniel Vaughan, “Codex CLI Network Security: requirements.toml Enforcement, Landlock, and Air-Gapped Deployments,” Codex Knowledge Base, 31 March 2026. https://codex.danielvaughan.com/2026/03/31/codex-cli-network-security-requirements-toml/ ↩ ↩2 ↩3
-
Daniel Vaughan, “Codex CLI Plugin System: Bundling Skills, MCP Servers, and App Connectors,” Codex Knowledge Base, 30 March 2026. https://codex.danielvaughan.com/2026/03/30/codex-cli-plugin-system/ ↩