modelcontextprotocol / modelcontextprotocol/kotlin-sdk
StdioServerTransport fails to deserialize initial message when stdin starts with UTF-8 BOM
Nobody has claimed this yet.
- 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
- In
ReadBuffer(ordeserializeMessage): trim leading BOM characters on incoming lines (line.trimStart('\uFEFF')). - Alternatively, in
StdioServerTransport: strip the leadingEF BB BFbytes from the inputSourcebefore appending to the read buffer.
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 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