Skip to content

Strip stdio frames as bytes - #625

Merged
koic merged 1 commit into
modelcontextprotocol:mainfrom
koic:strip_stdio_frames_as_bytes
Oct 20, 2026
Merged

koic merged 1 commit into
modelcontextprotocol:mainfrom
koic:strip_stdio_frames_as_bytes

Conversation

@koic

@koic koic commented Oct 19, 2026

Copy link
Copy Markdown
Member

Motivation and Context

StdioTransport reads $stdin as UTF-8 and strips each frame before parsing it, in the read loop of #open and in the loop of #send_request that reads the client's reply. String#strip raises for a UTF-8 string holding an invalid byte at either end on every supported Ruby, and anywhere in it from Ruby 3.2 on, and nothing rescued it there: one frame such as \xff[], or a request with an invalid byte in a string value, ended the read loop and the server process, before the parser could answer it. JSON text must be UTF-8 (RFC 8259, section 8.1), and the Python SDK replaces such bytes on its stdio streams instead of stopping.

Frames are now stripped as bytes. A frame that is not JSON gets the -32700 parse error any other unparsable frame gets, and a value holding such a byte reaches the handlers, which serve or refuse it as over HTTP.

How Has This Been Tested?

test/mcp/server/transports/stdio_transport_test.rb: a frame starting with an invalid byte, a frame holding one in a value, and a client reply holding one in a value. Against the previous library all three raise from String#strip (the value tests from Ruby 3.2 on).

Breaking Changes

None.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update

Checklist

  • I have read the MCP Documentation
  • My code follows the repository's style guidelines
  • New and existing tests pass locally
  • I have added appropriate error handling
  • I have added or updated documentation as needed

atesgoral
atesgoral previously approved these changes Oct 20, 2026
## Motivation and Context

`StdioTransport` reads `$stdin` as UTF-8 and strips each frame before parsing it, in the read loop of `#open`
and in the loop of `#send_request` that reads the client's reply. `String#strip` raises for a UTF-8 string holding
an invalid byte at either end on every supported Ruby, and anywhere in it from Ruby 3.2 on,
and nothing rescued it there: one frame such as `\xff[]`, or a request with an invalid byte in a string value,
ended the read loop and the server process, before the parser could answer it. JSON text must be UTF-8
(RFC 8259, section 8.1), and the Python SDK replaces such bytes on its stdio streams instead of stopping.

Frames are now stripped as bytes. A frame that is not JSON gets the `-32700` parse error any other unparsable frame gets,
and a value holding such a byte reaches the handlers, which serve or refuse it as over HTTP.

## How Has This Been Tested?

`test/mcp/server/transports/stdio_transport_test.rb`: a frame starting with an invalid byte, a frame holding one in a value,
and a client reply holding one in a value. Against the previous library all three raise from `String#strip`
(the value tests from Ruby 3.2 on).

## Breaking Changes

None.
@koic
koic force-pushed the strip_stdio_frames_as_bytes branch from 5ba7060 to d280b57 Compare October 20, 2026 21:56
@koic
koic merged commit 9e2b3f7 into modelcontextprotocol:main Oct 20, 2026
11 checks passed
@koic
koic deleted the strip_stdio_frames_as_bytes branch October 20, 2026 21:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants