Streamable HTTP MCP client fails to parse Penpot MCP responses
What version of Codex is running?
codex-cli 0.134.0
Installed package metadata reports @openai/codex@0.134.0-linux-x64.
What platform is your computer?
Linux x86_64, running Codex CLI locally.
What issue are you seeing?
Codex can discover a local Penpot MCP server configured as streamable_http, but tool calls through Codex's registered MCP wrapper fail with a JSON-RPC deserialize error.
The same Penpot MCP server works when called directly over HTTP using the MCP Streamable HTTP endpoint, including executing code in the connected Penpot plugin. That makes this look like an interop/transport parsing issue in Codex's MCP client rather than a broken Penpot file/plugin setup.
MCP server config observed by Codex
[
{
"name": "penpot",
"enabled": true,
"transport": {
"type": "streamable_http",
"url": "http://localhost:4401/mcp",
"bearer_token_env_var": null,
"http_headers": null,
"env_http_headers": null
},
"startup_timeout_sec": 10.0,
"tool_timeout_sec": 120.0,
"auth_status": "unsupported"
}
]
Penpot MCP package:
@penpot/mcp 2.15.0
Penpot MCP server process:
node dist/index.js
Modern Streamable HTTP endpoint: http://localhost:4401/mcp
WebSocket server URL: ws://localhost:4402
The Penpot plugin WebSocket is connected:
[::1]:4402 <-> [::1]:54736 chromium
What steps can reproduce the bug?
- Start the local Penpot MCP server from
@penpot/mcp@2.15.0. - Configure Codex with the streamable HTTP server:
``text``
name: penpot
url: http://localhost:4401/mcp
transport: streamable_http
- In Penpot, load the MCP plugin from
http://localhost:4400/manifest.json, open the plugin UI, and click "Connect to MCP server". - Confirm the WebSocket to
localhost:4402is established. - In Codex, call any registered Penpot MCP tool, for example
penpot/high_level_overvieworpenpot/execute_code.
Expected behavior
Codex should parse the Penpot MCP Streamable HTTP response and return the tool result.
For example, execute_code with:
return 1;
should return:
{
"result": 1,
"log": ""
}
Actual behavior
Codex tool calls fail before returning the MCP tool result:
tool call error: tool call failed for `penpot/execute_code`
Caused by:
Transport send error: Transport [rmcp::transport::worker::WorkerTransport<rmcp::transport::streamable_http_client::StreamableHttpClientWorker<codex_rmcp_client::http_client_adapter::StreamableHttpClientAdapter>>] error: Deserialize error: data did not match any variant of untagged enum JsonRpcMessage
The same deserialize error also happens for penpot/high_level_overview.
Direct MCP HTTP control succeeds
Calling the same server directly works.
Initialize:
curl -i -s -X POST \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":201,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"codex-probe","version":"0.0.1"}}}' \
http://localhost:4401/mcp
Response:
HTTP/1.1 200 OK
content-type: text/event-stream
mcp-session-id: f64e1bf9-1a1a-4e4c-818a-3cf04f643d61
event: message
data: {"result":{"protocolVersion":"2025-03-26","capabilities":{"tools":{"listChanged":true}},"serverInfo":{"name":"penpot","version":"1.0.0"},"instructions":"..."},"jsonrpc":"2.0","id":201}
Then:
curl -s -X POST \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-H 'mcp-session-id: f64e1bf9-1a1a-4e4c-818a-3cf04f643d61' \
-d '{"jsonrpc":"2.0","method":"notifications/initialized","params":{}}' \
http://localhost:4401/mcp
curl -s -X POST \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-H 'mcp-session-id: f64e1bf9-1a1a-4e4c-818a-3cf04f643d61' \
-d '{"jsonrpc":"2.0","id":204,"method":"tools/call","params":{"name":"execute_code","arguments":{"code":"return 1;"}}}' \
http://localhost:4401/mcp
Response:
event: message
data: {"result":{"content":[{"type":"text","text":"{\n \"result\": 1,\n \"log\": \"\"\n}"}]},"jsonrpc":"2.0","id":204}
A direct execute_code probe also successfully inspected the open Penpot file:
Page: Page 1
Root: Root Frame
Root children: 0
Selection: 0
Why this seems distinct from related issues
This is related to MCP deserialize failures, but it does not appear to be a non-2xx HTTP response case like #20513: the direct response is HTTP/1.1 200 OK with content-type: text/event-stream.
It also differs from Codex Desktop app connector failures like #23606 because this is a user-configured local streamable_http MCP server in Codex CLI, and direct MCP calls to the same endpoint succeed.
Additional notes
There may still be a Penpot-side compatibility edge if the server is emitting Streamable HTTP/SSE in a form stricter clients reject. But since direct MCP HTTP calls work and Codex reports a deserialize error in codex_rmcp_client::http_client_adapter, I am filing this first on the Codex side.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗