Codex rejects MCP tool results containing a resource_link (MCP Apps) with "Unexpected response type"
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 125k
- Forks
- 19.4k
- PR merge metrics
- PR metrics pending
Description
Summary
When a tool result contains a resource_link content block (the io.modelcontextprotocol/ui MCP Apps extension), Codex fails the whole call with Unexpected response type. A result without it works. The resource_link block is the only variable.
Environment
- Codex Desktop 0.150.0-alpha.8 (also observed on ChatGPT desktop 26.820.60940)
- rmcp
=3.1.3(percodex-rs/Cargo.toml) - Server negotiates protocol
2025-11-25
Reproduce
Minimal, zero-dependency Node stdio server (101 lines): https://github.com/ahonn/codex-mcp-apps-repro
git clone https://github.com/ahonn/codex-mcp-apps-repro
# ~/.codex/config.toml
[mcp_servers.appsrepro]
command = "node"
args = ["/absolute/path/to/codex-mcp-apps-repro/server.js"]
Start a session and call each tool:
plain_tool->{ "content": [ { "type": "text" } ], "isError": false }✅view_tool-> fails ❌:
tool call failed for `appsrepro/view_tool`
Caused by:
Unexpected response type
The two tools differ only in the result: view_tool appends one resource_link block.
Triggering result (spec-valid)
{
"content": [
{ "type": "text", "text": "result with a view" },
{ "type": "resource_link", "uri": "ui://demo/view/v1", "mimeType": "text/html;profile=mcp-app" }
],
"isError": false
}
Expected vs actual
- Expected: the result is accepted; the
resource_linkblock is handled or ignored. - Actual: the call fails with rmcp
ServiceError::UnexpectedResponse.
Analysis
Unexpected response type is rmcp's ServiceError::UnexpectedResponse, raised client-side when a ServerResult does not match CallToolResult. However, rmcp 3.1.3 deserializes this response without issue: a standalone rmcp 3.1.3 client, via both call_tool and the manual send_request + match ServerResult::CallToolResult path used for pre-2026-07-28 servers, accepts it and returns [text, resource_link]. So the defect appears to be in the layer that consumes tool results, not in rmcp_client deserialization. The server advertises the io.modelcontextprotocol/ui extension in initialize, and view_tool returns a spec-valid resource_link.
Impact
Any MCP server exposing MCP Apps content is unusable from Codex for its UI-bound tools. A server-side workaround is to stop advertising the ui extension to Codex.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by running the linked zero-dependency Node server and compare plain_tool with view_tool. Trace Codex’s MCP tool-result consumption after rmcp 3.1.3 deserializes the response, especially the call_tool and manual send_request paths described in the issue. Done means a spec-valid resource_link no longer causes Unexpected response type while plain results remain working.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- node.js, rust
- Domain
- api, backend
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 68/100