Support inbound MCP notifications routed into an active Codex CLI session
What feature would you like to see?
I want a way for external channels to push inbound messages into a running Codex CLI session via MCP notifications.
Right now Codex can call MCP tools, but there is no documented path for arbitrary server notifications (for example channel events) to show up as user-visible input in the active CLI thread.
That blocks a practical setup where Telegram or similar channels can message the bot and have those messages land in the existing terminal session.
Why this matters
Claude-style channel plugins can do this pattern by emitting a notification like notifications/claude/channel and the active session receives it as inbound context. In Codex CLI today, the same MCP server can expose tools, but inbound channel messages do not reach the running session.
Using codex app-server is not a replacement for this request. The point is to keep using the normal interactive CLI/TUI session and still accept inbound channel events.
What I expected
A supported mechanism so a configured MCP server can emit an inbound event that Codex CLI maps to a new user message (or equivalent thread item) in the active session.
What I observed
- MCP tools are available and callable in CLI
- No obvious way to inject inbound channel notifications into an already running CLI session
- No command to send input to an active session from another process
Suggested direction
One of these would solve it:
- A standard notification method Codex CLI listens to and converts into a thread item
- A local control API/IPC endpoint for "send message to active session"
- A documented extension point specifically for channel/inbox style MCP servers
Minimal repro context
- Run Codex CLI in normal interactive mode
- Connect a custom MCP server that emits async notifications for inbound chat events
- Confirm tools from that server work
- Confirm emitted inbound notifications do not appear in the active Codex conversation
This would unlock Telegram/Slack/other inbox integrations without moving to app-server mode.
14 Comments
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
I spent time tracing this more deeply from the perspective of local CLI-to-CLI orchestration, and I think the core need is slightly broader than “support inbound MCP notifications”.
The underlying request is:
please expose an opt-in local ingress for interactive Codex CLI/TUI sessions, so another trusted local process can deliver a real inbound turn (or draft append / turn steer) without PTY emulation.
Why this matters
There is a real gap today between:
For local orchestrators, wrappers, and tmux-based multi-agent setups, the fallback today is terminal injection:
send-keyspaste-bufferpipe-pane -IThose all have the same class of problems:
That is both a UX problem and a safety/integration problem. Local automation ends up relying on brittle PTY tricks instead of a supported control plane.
Why App Server is relevant, but not the whole answer
The docs and code already point in the right architectural direction:
thread/startthread/resumethread/forkturn/startturn/steerturn/interruptThat is exactly the class of primitive local orchestrators want.
The missing piece is: there does not seem to be a supported way to apply those semantics to an already-running interactive Codex CLI/TUI session in local workflows.
So the ask is not “please add a one-off tmux feature”.
It is:
please let interactive sessions opt into a local ingress that reuses the existing thread/turn control model.
What would solve it
Any of these would be strong solutions:
codex --listen uds:///path/to/session.sockcodex tui send --session <id> --text "..." [--submit]--steeror--interruptturn-started,turn-interrupted,approval-requested,approval-resolved,idle,draft-presence-changed)Why this is useful beyond one niche workflow
This helps more than tmux users.
It would make Codex easier to integrate with:
The common need is:
Suggested product shape
My strong preference would be:
That feels more consistent with the current architecture than adding more PTY-facing hacks or transport-specific exceptions.
If this is directionally aligned with the roadmap, I’d be happy to provide a tighter repro matrix and a more explicit comparison of:
My project solves this with a tmux-based bridge — Telegram messages are forwarded into the active Codex pane. Not via MCP, but it works reliably
I built a Telegram bridge to control Codex from my phone
Turn local Codex sessions on macOS into a Telegram control surface you can actually use from your phone.
https://github.com/Asuka-wx/codex-telegram-bridge
One-line positioning:
Keep your existing ssh + tmux + Codex workflow, and use Telegram only as the remote layer for status, approvals, and follow-up input.
Why It Matters
No workflow rewrite: keep using local tmux + Codex instead of switching to a browser dashboard or remote desktop
Actually remote-usable: not just logs, but approvals, interrupts, and follow-up input
Clear multi-task control: one control topic plus task-specific topics for parallel work
Phone-friendly by design: useful when you are away from your machine but still need to keep work moving
Lower setup friction: one-command installer plus launchd service support
To be complementary to what @Asuka-wx suggests, for Codex desktop you can ask agent to use the new "automation" tool, which allows message delivery to a specific existing thread/session. By that we can achieve at most 1 minutes delay for an inbound message delivery.
This thread seems to converge on a broader primitive than MCP notifications: an explicit ingress path into the already-running interactive CLI/TUI session, with delivery semantics that the live session can observe and either accept/reject.
I built Faryo as a narrower tmux-backed version of that idea. It does not solve MCP notification routing, and it is not a Telegram bridge. Codex stays in tmux as the live process; phone/browser sends short input, approvals/interrupts, and handoff back to that same process while showing compact output. The point is to avoid creating a second conversation surface that drifts from the terminal: https://github.com/Snailflyer/faryo
For the use cases here, is the hard requirement "arbitrary MCP server notifications become user messages", or would an authenticated local/mobile control endpoint into the existing CLI session cover most of the workflow?
I did this on my fork on this commit and had been using this extensively for inbound messages from iMessage + enabling cross-agent comms for Codex threads (i.e. letting different agents send messages to each other): https://github.com/openai/codex/commit/a614a0612fbf20e1419a956d65cf3f1988cd7ef4
Additional third-party integration context from a desktop voice-agent companion implementation.
We currently support Claude Code and OpenCode with small local integrations:
~/.claude/plugins/...; hooks onStop,StopFailure,Notification, andPermissionRequest; hook opens a companion reply UI via custom URL and waits on local request/response files.~/.config/opencode/plugins/...; listen forsession.idle,session.error, andpermission.asked; open the same reply UI and send typed response back through the OpenCode SDK when available.Codex CLI looks close for the outbound/notification half because it has plugins plus hooks. A Codex plugin could likely mirror the Claude Code side by registering hooks and opening a local companion UI.
The blocker is the inbound half for an already-running interactive CLI/TUI session: after the companion captures dictated/typed text, there does not appear to be a supported way to deliver that text back as the next real user turn in the active Codex session. PTY paste would work only as a brittle fallback and loses session semantics.
For this use case, the useful primitive would be an opt-in local ingress into the current interactive session, ideally reusing existing app-server/thread/turn semantics. Concrete examples that would solve it:
codex tui send --session <id> --text ... --submitturn/start/turn/steerThis would let voice, accessibility, mobile, and local sidecar tools integrate with Codex CLI without pretending to be a keyboard.
One related observation from the Codex Desktop / app-server side: this looks
like the same active-session ingress problem from a different surface.
I opened #25914 for the narrower Desktop case, where a local external
app-server client needs a supported way to discover and attach to the currently
active Codex Desktop thread/turn.
What I observed locally:
start a thread, start a turn, observe events, and interrupt a turn owned by
that client.
thread history through thread listing/reading, but did not get a loaded,
controllable target for the active Desktop UI thread.
thread/loaded/listreturned no loaded threads from theclient-visible surface, and
turn/interruptagainst the active-threadcandidate returned
thread not found.So the gap is not only "how do I send text into a terminal". It is also: how can
a trusted local client tell whether a persisted thread is loaded in the current
app-server process, whether it is the interactive thread the user is looking at,
and whether there is an active turn that can be steered or interrupted?
For this issue's broader local-ingress request, it would help if the supported
contract distinguished at least:
That would let local orchestrators and companion tools use thread/turn
semantics instead of guessing from session files, UI labels, or PTY state.
Bleh. I've spent the last week trying to make an app that allows people to connect Codex CLI TUI's together and support message passing between them, so we can have orchestration agents. But this is the current roadblock I'm running into. We need a way to be able to inject messages into Codex CLI's reliably, from other apps, so that I can send messages telling the agent to check their inbox, etc. Then they can all use my app as a communication bridge. @sidkandan sums it up appropriately. And I've considered tmux, but I don't want to use tmux... I just want to use native Codex CLI, it's a better user experience. Plus I'm using https://github.com/ogulcancelik/herdr which has a great API as well, and so this is the only roadblock at the moment.
Like you said, the goal is to use Codex CLI rather than re-inventing our own TUI or CLI... I'm sick of trying to find alternatives to Codex that work well, but support orchestration. None of them have all the features I need, and the CLI doesn't have any means to accomplish this. Very frustrating.
@nullbio I've kind of solved this in repowire. Uses tmux as a transport so it happens clunkily out of band . A feature like this would be terribly awesome and reduce the dependency surface massively (and bugs)
@nullbio this is exactly the roadblock i hit too. repowire does the connect-multiple-codex-TUIs-and-pass-messages thing today, but via tmux injection out of band, which is why im +1ing this issue. a native ingress would let it drop the hack and the bug surface that comes with it.
if its useful as a reference for the orchestration layer (peer addressing, ask/ack with delivery outcomes, broadcast) happy to point you at the bits that took longest to get right, or compare notes. no point both of us rediscovering the same tmux failure modes.
I have a patch that implements the narrow version of this for CLI sessions: opt-in MCP notification ingress into the active session as
Submission::UserInput.Empirical validation on a local patched 0.141.0-line CLI build:
toSession: true,msgId,source, and text payload.[MCP notification]header.msgIddedupe.The branch keeps this default-off behind per-server
surface_notifications = true, so existing MCP notifications remain trace-only unless the user explicitly trusts that server for session ingress.Patch link: https://github.com/openai/codex/compare/main...alexfrmn:upstream-pr-mcp-surface-notifications?expand=1
This request maps closely to LS work on typed inbound events, session identity, durable delivery, and authorization boundaries.
A useful distinction is:
MCP notification received
≠ authenticated user instruction
≠ trusted factual claim
≠ authorized side effect
An inbound channel message should become a typed thread item rather than an indistinguishable synthetic user message.
A possible envelope:
{
"schema_version": "mcp-inbound-message/v0.1",
"event_id": "telegram:update:18492",
"source": {
"mcp_server": "telegram-channel",
"channel_type": "telegram",
"channel_id": "chat-882",
"sender_id": "user-431"
},
"target": {
"session_id": "codex-thread-123",
"trajectory_id": "project-alpha"
},
"message": {
"content_type": "text/plain",
"text": "Please check whether the deployment completed"
},
"delivery": {
"deduplication_key": "telegram:update:18492",
"received_at": "2026-06-24T18:42:11Z"
},
"trust": {
"source_authenticated": true,
"sender_verified": false,
"execution_authorized": false
}
}
The receiving path could be:
MCP notification
→ validate configured server
→ authenticate sender/channel
→ validate target session
→ deduplicate
→ append durable thread item
→ notify or wake session
→ classify request
→ normal approval and sandbox flow
The core invariant would be:
«Inbound MCP messages may provide context or request work, but they must not bypass the active session’s approval and sandbox policy.»
This matters especially for messages such as:
“Run this command”
“Deploy production”
“Send this email”
“Delete the generated files”
Delivery should not be equivalent to execution permission.
A second invariant:
«The UI should visibly distinguish local user input from external channel input.»
For example:
[Telegram · unverified sender]
Please deploy the current branch
rather than rendering it exactly like a message typed locally by the authenticated operator.
Useful controls might include:
A durable processing receipt could look like:
{
"event_id": "telegram:update:18492",
"session_id": "codex-thread-123",
"status": "DELIVERED",
"thread_item_id": "item-773",
"turn_started": false,
"action_decision": "REQUIRES_REVIEW"
}
It would also be useful to separate:
DELIVERED
→ DISPLAYED
→ ACKNOWLEDGED
→ PROCESSED
→ ACTION_PROPOSED
→ ACTION_EXECUTED
This prevents the MCP server from treating basic delivery as proof that Codex completed the requested work.
A deterministic conformance fixture could test:
Related LS work:
https://github.com/safal207/LS/issues/594
https://github.com/safal207/LS/issues/595
https://github.com/safal207/LS/issues/596
https://github.com/safal207/LS/issues/597
Would a vendor-neutral "McpInboundMessage" and "InboundMessageReceipt" fixture help test authenticated delivery, session targeting, deduplication, UI attribution, and approval preservation?
This request maps closely to LS work on typed inbound events, session identity, durable delivery, and authorization boundaries.
A useful distinction is:
MCP notification received
≠ authenticated user instruction
≠ trusted factual claim
≠ authorized side effect
An inbound channel message should become a typed thread item rather than an indistinguishable synthetic user message.
A possible envelope:
{
"schema_version": "mcp-inbound-message/v0.1",
"event_id": "telegram:update:18492",
"source": {
"mcp_server": "telegram-channel",
"channel_type": "telegram",
"channel_id": "chat-882",
"sender_id": "user-431"
},
"target": {
"session_id": "codex-thread-123",
"trajectory_id": "project-alpha"
},
"message": {
"content_type": "text/plain",
"text": "Please check whether the deployment completed"
},
"delivery": {
"deduplication_key": "telegram:update:18492",
"received_at": "2026-06-24T18:42:11Z"
},
"trust": {
"source_authenticated": true,
"sender_verified": false,
"execution_authorized": false
}
}
The receiving path could be:
MCP notification
→ validate configured server
→ authenticate sender/channel
→ validate target session
→ deduplicate
→ append durable thread item
→ notify or wake session
→ classify request
→ normal approval and sandbox flow
The core invariant would be:
«Inbound MCP messages may provide context or request work, but they must not bypass the active session’s approval and sandbox policy.»
This matters especially for messages such as:
“Run this command”
“Deploy production”
“Send this email”
“Delete the generated files”
Delivery should not be equivalent to execution permission.
A second invariant:
«The UI should visibly distinguish local user input from external channel input.»
For example:
[Telegram · unverified sender]
Please deploy the current branch
rather than rendering it exactly like a message typed locally by the authenticated operator.
Useful controls might include:
A durable processing receipt could look like:
{
"event_id": "telegram:update:18492",
"session_id": "codex-thread-123",
"status": "DELIVERED",
"thread_item_id": "item-773",
"turn_started": false,
"action_decision": "REQUIRES_REVIEW"
}
It would also be useful to separate:
DELIVERED
→ DISPLAYED
→ ACKNOWLEDGED
→ PROCESSED
→ ACTION_PROPOSED
→ ACTION_EXECUTED
This prevents the MCP server from treating basic delivery as proof that Codex completed the requested work.
A deterministic conformance fixture could test:
Related LS work:
https://github.com/safal207/LS/issues/594
https://github.com/safal207/LS/issues/595
https://github.com/safal207/LS/issues/596
https://github.com/safal207/LS/issues/597
Would a vendor-neutral "McpInboundMessage" and "InboundMessageReceipt" fixture help test authenticated delivery, session targeting, deduplication, UI attribution, and approval preservation?