MCP tool execution events and metadata not exposed to LLM (breaks multi-turn conversations with Codex and other stateful tools)
Bug Description:
Claude Code's MCP client implementation filters out intermediate events and metadata emitted during tool execution, preventing the LLM from accessing critical information needed for multi-turn interactions with MCP tools like Codex.
Environment:
- Claude Code version: 2.0.1
- Codex MCP server version: 0.47.0
- Platform: macOS (Darwin 25.0.0)
Concrete Example: Codex MCP Server
When calling mcp__Codex__codex, the server emits a codex/event notification:
{
"jsonrpc": "2.0",
"method": "codex/event",
"params": {
"_meta": {"requestId": 2},
"id": "",
"msg": {
"type": "session_configured",
"session_id": "0199f3a1-15e4-77d3-93ef-f954c7978c66",
"model": "gpt-5-codex",
"reasoning_effort": "high",
"rollout_path": "~/.codex/sessions/2025/10/17/rollout-2025-10-17T12-24-04-0199f3a1-15e4-77d3-93ef-f954c7978c66.jsonl"
}
}
}
What Happens:
- ✅ Codex MCP server correctly emits the event via JSON-RPC
- ✅ Event is sent over stdio (verified in MCP Inspector)
- ✅ Claude Code's MCP client receives the event
- ❌ Event is filtered out - LLM never sees it
- ❌ LLM only receives final text response
- ❌
mcp__Codex__codex-replyrequiresconversationId(thesession_idfrom the event) - ❌ Multi-turn conversations are impossible
The Architectural Gap:
Codex MCP Server
↓ emits codex/event with session_id
↓ (stdio JSON-RPC channel)
Claude Code MCP Client
↓ receives events ✓
↓ filters/processes
✗ drops events before LLM
↓ only passes final text
LLM (Claude)
↓ cannot see session_id
↓ cannot use codex-reply
Verification:
- MCP Inspector shows all events including
session_id✓ - Claude LLM only sees final text response ✗
The Tension:
While it's understandable that:
- Flooding the LLM's context with every event would be problematic
- Special-casing every MCP tool (like extracting Codex's
session_id) doesn't scale
Claude Code should not drop ALL intermediate events either.
Proposed Solutions:
Option A - Surface all events in tool results (simple):
{
"events": [
{"type": "session_configured", "session_id": "0199f3a1-..."}
],
"content": "Consider layering a small abstraction..."
}
Option B - Per-tool configurable event metadata (recommended):
Allow MCP server configurations to specify which events to capture and which fields to extract. Example config:
{
"mcpServers": {
"Codex": {
"command": "codex",
"args": ["mcp-server"],
"accumulate_event_metadata": [
{
"event_filter": "params?.msg?.type==\"session_configured\"",
"fields": ["params.msg.session_id"]
}
]
}
}
}
This would:
- Monitor all intermediate events emitted during tool execution
- Apply the
event_filterexpression to each event - For matching events, extract only the specified
fields(like lodashpick) - Preserve the original event structure shape with pruned fields
- Return the pruned metadata alongside the final tool result
Result exposed to LLM:
{
"metadata": [
{
"params": {
"msg": {
"session_id": "0199f3a1-15e4-77d3-93ef-f954c7978c66"
}
}
}
],
"content": "Consider layering a small abstraction..."
}
This approach:
- ✅ Avoids context window bloat (only specified fields extracted)
- ✅ Preserves original event structure for easy access
- ✅ Scales to any MCP tool without hardcoding
- ✅ User-configurable per MCP server
- ✅ Backwards compatible (omit config = no metadata accumulation)
Option C - Debug mode (fallback):
Expose all MCP communication when in a debug/verbose mode for development and troubleshooting.
Impact:
This affects any MCP tool that:
- Requires stateful multi-turn interactions
- Emits session/conversation identifiers in events
- Uses intermediate metadata for coordination
- Relies on the LLM having access to full MCP protocol communication
Related Issues:
- #3174 (notifications/message not displayed)
- #4157 (progress notifications)
- #5960 (streaming responses)
But this issue is specifically about tool execution events and metadata being hidden from the LLM.
This issue has 6 comments on GitHub. Read the full discussion on GitHub ↗