-
Notifications
You must be signed in to change notification settings - Fork 1.9k
feat: add maxMessageBytes to stdio transports, make ReadBuffer amortized O(1) #2245
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
2f3f291
0fcdb95
7a8627e
ee4a482
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| --- | ||
| '@modelcontextprotocol/client': minor | ||
| '@modelcontextprotocol/server': minor | ||
| --- | ||
|
|
||
| Add `maxMessageBytes` option to the stdio transports and make the stdio read buffer amortized O(1) per byte | ||
|
|
||
| A stdio peer that writes a very large amount of data without a newline (accidental binary output, runaway log line, or a malicious server) previously grew the receiving process's memory without bound, and each incoming chunk re-copied the entire buffered backlog (`Buffer.concat` per chunk). There was no public way to bound or replace the read buffer, so integrators who had built flood protection on v1 transport internals had nothing to migrate to. | ||
|
|
||
| `StdioClientTransport` and `StdioServerTransport` now accept an optional `maxMessageBytes`. When a single message exceeds it, the data is dropped, an `SdkError` with the new code `SdkErrorCode.MessageTooLarge` is reported via `onerror`, and the transport recovers at the next newline boundary. The default remains unlimited. The read buffer also now grows geometrically with read/scan offsets instead of concatenating on every chunk. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -141,6 +141,27 @@ | |
| const transport = new StreamableHTTPClientTransport(new URL('http://localhost:3000/mcp')); | ||
| ``` | ||
|
|
||
| ### Stdio transports: non-JSON lines are now skipped silently | ||
|
|
||
| In v1, a non-JSON line on the stdio stream (for example, debug output from a hot-reload | ||
| tool writing to stdout) surfaced as a `SyntaxError` through the transport's `onerror` | ||
| callback. In v2, the stdio read buffer silently skips lines that are not valid JSON and | ||
| continues with the next line; only valid-JSON messages that fail schema validation still | ||
| reach `onerror`. If you relied on `onerror` to detect a misbehaving server that writes | ||
| noise to stdout, that signal no longer fires for non-JSON lines. | ||
|
|
||
| Relatedly, both stdio transports now accept an optional `maxMessageBytes` setting that | ||
| bounds how large a single message may grow before it is dropped and reported via | ||
| `onerror` (`SdkError` with code `SdkErrorCode.MessageTooLarge`). v1 had no built-in | ||
| protection against a peer flooding the stream with unbounded data on a single line; if | ||
| you implemented such protection against v1 transport internals, migrate to this option. | ||
|
|
||
| ```typescript | ||
| import { StdioClientTransport } from '@modelcontextprotocol/client/stdio'; | ||
|
|
||
| const transport = new StdioClientTransport({ command: 'my-server' }, { maxMessageBytes: 4 * 1024 * 1024 }); | ||
| ``` | ||
|
|
||
|
Check warning on line 164 in docs/migration.md
|
||
|
Comment on lines
+144
to
+164
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 The new Extended reasoning...What the issue is. This PR adds a new enum member, Why this is introduced by this PR. Before this change, both lists matched the enum exactly — 14 members in the enum, 14 entries in each list — so they read as complete enumerations. After this change the enum has 15 members and the lists are stale by precisely the member the PR adds. Within the same documents the PR edits, one section instructs users to handle Step-by-step proof.
Why nothing else prevents it. Both lists are hand-maintained prose with no check against the actual enum, so the only safeguard is updating them in the same change that extends the enum — which this PR does for the "Unchanged APIs" sections (already qualified for Impact. Low — the new code is documented in the new stdio sections of both guides, so nobody is misled about behavior; this is purely a completeness/consistency gap in the reference lists. It mainly affects readers (or LLMs) who treat those lists as the canonical enumeration of How to fix. Add one row to the table in |
||
| ### Server auth split | ||
|
|
||
| Resource Server helpers (`requireBearerAuth`, `mcpAuthMetadataRouter`, `getOAuthProtectedResourceMetadataUrl`, `OAuthTokenVerifier`) are first-class in `@modelcontextprotocol/express`. | ||
|
|
@@ -984,8 +1005,8 @@ | |
| - `Client` constructor and most client methods (`connect`, `listTools`, `listPrompts`, `listResources`, `readResource`, etc.) — note: `callTool()` signature changed (schema parameter removed) | ||
| - `McpServer` constructor, `server.connect(transport)`, `server.close()` | ||
| - `Server` (low-level) constructor and all methods | ||
| - `StreamableHTTPClientTransport`, `SSEClientTransport`, `StdioClientTransport` constructors and options | ||
| - `StdioServerTransport` constructor and options | ||
| - `StreamableHTTPClientTransport` and `SSEClientTransport` constructors and options | ||
| - `StdioClientTransport` and `StdioServerTransport` constructors — note: both accept a new optional `maxMessageBytes` option in v2 (see the stdio transport notes above) | ||
| - All Zod schemas and type definitions from `types.ts` (except the aliases listed above) | ||
| - Tool, prompt, and resource callback return types | ||
|
|
||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.