Commit 30422d2
committed
perf(core): UTF-8-first content blocks + lower-allocation stream/HTTP transports
This change set focuses on reducing allocations and unnecessary transcoding across the MCP wire path (JSON-RPC + MCP content), especially for line-delimited stream transports (stdio / raw streams) and for text content blocks that frequently originate as UTF-8 already.
Key protocol / model updates
- ContentBlock JSON converter now reads the "text" property directly into UTF-8 bytes (including unescaping) without first materializing a UTF-16 string.
- Added Utf8TextContentBlock ("type":"text") to allow hot paths to keep text payloads as UTF-8; TextContentBlock now supports a cached UTF-16 string backed by a Utf8Text buffer.
- Added opt-in deserialization behavior to materialize Utf8TextContentBlock instead of TextContentBlock via McpJsonUtilities.CreateOptions(materializeUtf8TextContentBlocks: true); DefaultOptions remains compatible and continues to materialize TextContentBlock.
- ImageContentBlock and AudioContentBlock still have Data as a base64 string; added DataUtf8 and DecodedData helpers to avoid repeated conversions.
- BlobResourceContents updated to cache and interoperate between (1) base64 string (Blob), (2) base64 UTF-8 bytes (BlobUtf8), and (3) decoded bytes (DecodedData)
Serialization / JSON-RPC improvements
- JsonRpcMessage converter now determines concrete message type (request/notification/response/error) by scanning the top-level payload with Utf8JsonReader.ValueTextEquals and skipping values in-place, avoiding JsonElement.GetRawText() / UTF-16 round-trips.
- McpJsonUtilities now exposes CreateOptions(...) to produce an options instance that can override the ContentBlock converter (materializeUtf8TextContentBlocks) while still chaining MEAI type info resolver support.
- Added McpTextUtilities helpers (UTF-8 decode, base64 encode on older TFMs, and common whitespace checks used by transports).
Transport / streaming changes (stdio + streams)
- StreamClientSessionTransport and StreamServerTransport reading loops were refactored to a newline-delimited (LF) byte scanner:
- Parses messages directly from pooled byte buffers + a reusable MemoryStream buffer.
- Handles both LF and CRLF by trimming a trailing '\r' after splitting on '\n'.
- Avoids UTF-16 materialization for parsing and (optionally) for logging by only decoding to string when trace logging is enabled.
- Added TextStreamClientSessionTransport to optimize the "text writer/reader" client transport:
- Prefers writing UTF-8 directly when the writer is a StreamWriter.
- Can read via the underlying stream when the reader is a UTF-8 StreamReader, falling back to ReadLineAsync for arbitrary TextReader implementations.
HTTP transport / client plumbing
- McpHttpClient now uses JsonContent.Create on NET TFMs and a new JsonTypeInfoHttpContent<T> on non-NET TFMs to serialize via JsonTypeInfo without buffering to compute Content-Length.
- StreamableHttpClientSessionTransport and SseClientSessionTransport disposal paths now consistently cancel and "defuse" CTS instances to reduce races/leaks during teardown.
- StreamableHttpSession updates activity tracking and shutdown ordering (dispose transport first, then cancel, then await server run) for cleaner termination.
Cancellation / lifecycle utilities
- Added CanceledTokenSource: a singleton already-canceled CancellationTokenSource plus a Defuse(ref cts, ...) helper to safely swap out mutable CTS fields during disposal.
URI template parsing
- UriTemplate parsing/formatting updated with a more explicit template-expression regex and improved query expression handling; uses GeneratedRegex and (on NET) a non-backtracking regex option for performance.
Tests / samples
- Added ProcessStartInfoUtilities to robustly locate executables on PATH and to handle Windows .cmd/.bat invocation semantics when UseShellExecute=false; updated integration tests accordingly.
- Added TextMaterializationTestHelpers to let tests run with either TextContentBlock or Utf8TextContentBlock materialization.
- Updated tests and samples to align with the new base64-string Data representation for image/audio blocks and to avoid unnecessary allocations in transport tests.
Behavioral notes / compatibility
- Wire format remains MCP/JSON-RPC compatible: messages are still newline-delimited JSON; CRLF continues to work.
- The Utf8TextContentBlock materialization is opt-in via McpJsonUtilities.CreateOptions(materializeUtf8TextContentBlocks: true); default behavior preserves prior materialized types for text blocks.1 parent b6e2c0d commit 30422d2
File tree
53 files changed
+2090
-545
lines changed- samples/EverythingServer
- Resources
- Tools
- src
- ModelContextProtocol.AspNetCore
- ModelContextProtocol.Core
- Authentication
- Client
- Internal
- Protocol
- Server
- tests
- Common/Utils
- ModelContextProtocol.AspNetCore.Tests
- ModelContextProtocol.ConformanceServer
- Prompts
- Resources
- Tools
- ModelContextProtocol.TestServer
- ModelContextProtocol.TestSseServer
- ModelContextProtocol.Tests
- Client
- Protocol
- Server
- Transport
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
53 files changed
+2090
-545
lines changed| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
31 | 31 | | |
32 | 32 | | |
33 | 33 | | |
34 | | - | |
| 34 | + | |
35 | 35 | | |
36 | 36 | | |
37 | 37 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
41 | 41 | | |
42 | 42 | | |
43 | 43 | | |
44 | | - | |
| 44 | + | |
45 | 45 | | |
46 | 46 | | |
47 | 47 | | |
| |||
Lines changed: 5 additions & 3 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | | - | |
| 1 | + | |
| 2 | + | |
2 | 3 | | |
3 | 4 | | |
4 | 5 | | |
| |||
16 | 17 | | |
17 | 18 | | |
18 | 19 | | |
19 | | - | |
| 20 | + | |
20 | 21 | | |
21 | 22 | | |
22 | 23 | | |
| |||
124 | 125 | | |
125 | 126 | | |
126 | 127 | | |
127 | | - | |
| 128 | + | |
| 129 | + | |
128 | 130 | | |
129 | 131 | | |
130 | 132 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
3 | 3 | | |
4 | | - | |
| 4 | + | |
5 | 5 | | |
6 | | - | |
7 | 6 | | |
8 | 7 | | |
9 | 8 | | |
| |||
263 | 262 | | |
264 | 263 | | |
265 | 264 | | |
| 265 | + | |
| 266 | + | |
266 | 267 | | |
267 | 268 | | |
268 | 269 | | |
| |||
275 | 276 | | |
276 | 277 | | |
277 | 278 | | |
278 | | - | |
| 279 | + | |
| 280 | + | |
| 281 | + | |
279 | 282 | | |
280 | 283 | | |
281 | 284 | | |
| |||
307 | 310 | | |
308 | 311 | | |
309 | 312 | | |
310 | | - | |
| 313 | + | |
311 | 314 | | |
312 | 315 | | |
313 | 316 | | |
| |||
380 | 383 | | |
381 | 384 | | |
382 | 385 | | |
383 | | - | |
| 386 | + | |
| 387 | + | |
| 388 | + | |
384 | 389 | | |
385 | 390 | | |
386 | 391 | | |
387 | 392 | | |
388 | 393 | | |
389 | | - | |
| 394 | + | |
| 395 | + | |
| 396 | + | |
390 | 397 | | |
391 | 398 | | |
392 | 399 | | |
393 | 400 | | |
394 | 401 | | |
395 | 402 | | |
396 | 403 | | |
397 | | - | |
| 404 | + | |
398 | 405 | | |
399 | 406 | | |
400 | 407 | | |
| |||
414 | 421 | | |
415 | 422 | | |
416 | 423 | | |
417 | | - | |
418 | | - | |
| 424 | + | |
| 425 | + | |
| 426 | + | |
| 427 | + | |
| 428 | + | |
419 | 429 | | |
420 | 430 | | |
421 | | - | |
| 431 | + | |
| 432 | + | |
| 433 | + | |
| 434 | + | |
| 435 | + | |
| 436 | + | |
| 437 | + | |
| 438 | + | |
| 439 | + | |
| 440 | + | |
| 441 | + | |
| 442 | + | |
| 443 | + | |
| 444 | + | |
| 445 | + | |
422 | 446 | | |
423 | | - | |
| 447 | + | |
424 | 448 | | |
425 | | - | |
| 449 | + | |
| 450 | + | |
| 451 | + | |
| 452 | + | |
| 453 | + | |
| 454 | + | |
| 455 | + | |
| 456 | + | |
| 457 | + | |
| 458 | + | |
| 459 | + | |
| 460 | + | |
| 461 | + | |
426 | 462 | | |
427 | 463 | | |
428 | 464 | | |
429 | 465 | | |
430 | 466 | | |
431 | 467 | | |
| 468 | + | |
432 | 469 | | |
433 | 470 | | |
434 | 471 | | |
| |||
Lines changed: 4 additions & 2 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
6 | 6 | | |
7 | 7 | | |
8 | 8 | | |
| 9 | + | |
9 | 10 | | |
10 | 11 | | |
11 | 12 | | |
| |||
581 | 582 | | |
582 | 583 | | |
583 | 584 | | |
584 | | - | |
585 | | - | |
| 585 | + | |
| 586 | + | |
| 587 | + | |
586 | 588 | | |
587 | 589 | | |
588 | 590 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
142 | 142 | | |
143 | 143 | | |
144 | 144 | | |
145 | | - | |
146 | | - | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
147 | 166 | | |
148 | 167 | | |
149 | 168 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
32 | 32 | | |
33 | 33 | | |
34 | 34 | | |
35 | | - | |
36 | | - | |
37 | | - | |
38 | | - | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
39 | 38 | | |
40 | 39 | | |
41 | 40 | | |
| |||
Lines changed: 3 additions & 2 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
| 3 | + | |
3 | 4 | | |
4 | 5 | | |
5 | 6 | | |
| |||
18 | 19 | | |
19 | 20 | | |
20 | 21 | | |
21 | | - | |
| 22 | + | |
22 | 23 | | |
23 | 24 | | |
24 | 25 | | |
| |||
114 | 115 | | |
115 | 116 | | |
116 | 117 | | |
117 | | - | |
| 118 | + | |
118 | 119 | | |
119 | 120 | | |
120 | 121 | | |
| |||
Lines changed: 1 addition & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
7 | 7 | | |
8 | 8 | | |
9 | 9 | | |
10 | | - | |
| 10 | + | |
11 | 11 | | |
12 | 12 | | |
13 | 13 | | |
| |||
0 commit comments