modelcontextprotocol / modelcontextprotocol/kotlin-sdk

StdioServerTransport fails to deserialize initial message when stdin starts with UTF-8 BOM

Open
#956 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug P2 ready for work
Dominant language
Kotlin
Stars
1.5k
Forks
248
Avg merge
1d 20h
Merged PRs (30d)
23

Description

Description

When an MCP client writes a UTF-8 BOM (\uFEFF / bytes EF BB BF) at the beginning of stdin before the first JSON-RPC message, StdioServerTransport fails on deserialization and immediately closes the session.

We observed this behavior when running against OpenAI Codex CLI / Codex on Windows (codex-cli 0.147.0).

Stack trace
java.lang.IllegalArgumentException: Element class kotlinx.serialization.json.JsonLiteral is not a JsonObject
    at kotlinx.serialization.json.JsonElementKt.getJsonObject(JsonElement.kt:232)
    at io.modelcontextprotocol.kotlin.sdk.types.JSONRPCMessagePolymorphicSerializer.selectDeserializer(serializers.kt:493)
    at io.modelcontextprotocol.kotlin.sdk.shared.ReadBufferKt.deserializeMessage(ReadBuffer.kt:136)
    at io.modelcontextprotocol.kotlin.sdk.shared.ReadBuffer.readMessage(ReadBuffer.kt:54)
    at io.modelcontextprotocol.kotlin.sdk.server.StdioServerTransport.processorPump(StdioServerTransport.kt:209)
Root cause

StdioServerTransport reads bytes into ReadBuffer, splits on newlines, and passes the raw line string to deserializeMessage(line). When the first line contains a leading BOM, kotlinx.serialization parses \uFEFF{...} as a JsonLiteral rather than a JsonObject. JSONRPCMessagePolymorphicSerializer.selectDeserializer expects a JsonObject and throws IllegalArgumentException. Because this exception happens inside processorPump, the coroutine fails and drops the stdio connection.

Suggested fix
  1. In ReadBuffer (or deserializeMessage): trim leading BOM characters on incoming lines (line.trimStart('\uFEFF')).
  2. Alternatively, in StdioServerTransport: strip the leading EF BB BF bytes from the input Source before appending to the read buffer.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with ReadBuffer.readMessage and deserializeMessage, then inspect StdioServerTransport.processorPump and JSONRPCMessagePolymorphicSerializer.selectDeserializer to trace the BOM failure. Reproduce with a UTF-8 BOM before the first JSON-RPC line and verify that the message deserializes without closing the stdio session.

Written by the indexing model from the issue text.

Assessment

Tech stack
kotlin
Domain
backend
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
74/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.