Skip to content

Refuse subscriptions/listen requests that cannot be encoded - #585

Merged
koic merged 1 commit into
modelcontextprotocol:mainfrom
koic:refuse_listen_requests_that_cannot_be_encoded
Oct 2, 2026
Merged

koic merged 1 commit into
modelcontextprotocol:mainfrom
koic:refuse_listen_requests_that_cannot_be_encoded

Conversation

@koic

@koic koic commented Oct 1, 2026

Copy link
Copy Markdown
Member

Motivation and Context

A subscriptions/listen request whose id, or one of whose honored resource URIs, is a string holding bytes that are not valid UTF-8 parses, since the JSON parser accepts such bytes, but cannot be written back as JSON. The transport answered 200, registered the stream, and then failed to encode the acknowledgement that echoes both; JSON::GeneratorError is not among the write errors the stream handles, so the registered entry was never removed and kept one of the max_listen_subscriptions slots until the transport closed.

The acknowledgement is now encoded before the stream is committed to. An id that cannot be encoded is refused with HTTP 400 and JSON-RPC -32600, the error carrying a null id since the id itself cannot be written; an honored filter that cannot be encoded is refused with HTTP 400 and -32602. Neither registers a stream.

How Has This Been Tested?

New tests in test/mcp/server/transports/streamable_http_transport_test.rb send a listen request whose id and one whose resource URI hold an invalid byte, and check the 400 and that no stream is registered. Against the previous library both answer 200.
Two more send the invalid URI to a server without the subscribe capability, which acknowledges without it, and to a transport at its stream cap, which answers 503 first.

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

## Motivation and Context

A `subscriptions/listen` request whose id, or one of whose honored resource URIs, is a string holding bytes that
are not valid UTF-8 parses, since the JSON parser accepts such bytes, but cannot be written back as JSON.
The transport answered 200, registered the stream, and then failed to encode the acknowledgement that echoes both;
`JSON::GeneratorError` is not among the write errors the stream handles, so the registered entry was never removed
and kept one of the `max_listen_subscriptions` slots until the transport closed.

The acknowledgement is now encoded before the stream is committed to. An id that cannot be encoded is refused with
HTTP 400 and JSON-RPC `-32600`, the error carrying a null id since the id itself cannot be written;
an honored filter that cannot be encoded is refused with HTTP 400 and `-32602`. Neither registers a stream.

## How Has This Been Tested?

New tests in `test/mcp/server/transports/streamable_http_transport_test.rb` send a listen request whose id and
one whose resource URI hold an invalid byte, and check the 400 and that no stream is registered.
Against the previous library both answer 200.
Two more send the invalid URI to a server without the `subscribe` capability, which acknowledges without it,
and to a transport at its stream cap, which answers 503 first.

## Breaking Changes

None.
@koic
koic merged commit 402cb56 into modelcontextprotocol:main Oct 2, 2026
11 checks passed
@koic
koic deleted the refuse_listen_requests_that_cannot_be_encoded branch October 2, 2026 16:09
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