Skip to content

Intermediate strings created in MCP transport #1259

@ericstj

Description

@ericstj

When fixing #1064 I was looking at other parts of the stack and noticed there are at least a few other places where we could go directly toa/from UTF-8 but do not.

var json = JsonSerializer.Serialize(message, McpJsonUtilities.JsonContext.Default.JsonRpcMessage);
LogTransportSendingMessageSensitive(Name, json);
await _outputStream.WriteAsync(Encoding.UTF8.GetBytes(json), cancellationToken).ConfigureAwait(false);

var line = await _inputReader.ReadLineAsync(shutdownToken).ConfigureAwait(false);
if (string.IsNullOrWhiteSpace(line))
{
if (line is null)
{
LogTransportEndOfStream(Name);
break;
}
continue;
}
LogTransportReceivedMessageSensitive(Name, line);
try
{
if (JsonSerializer.Deserialize(line, McpJsonUtilities.DefaultOptions.GetTypeInfo(typeof(JsonRpcMessage))) is JsonRpcMessage message)
{
await WriteMessageAsync(message, shutdownToken).ConfigureAwait(false);
}
else
{
LogTransportMessageParseUnexpectedTypeSensitive(Name, line);
}
}

var json = JsonSerializer.Serialize(message, McpJsonUtilities.JsonContext.Default.JsonRpcMessage);
LogTransportSendingMessageSensitive(Name, json);
using var _ = await _sendLock.LockAsync(cancellationToken).ConfigureAwait(false);
try
{
// Write the message followed by a newline using our UTF-8 writer
await _serverInput.WriteLineAsync(json).ConfigureAwait(false);

if (await _serverOutput.ReadLineAsync(cancellationToken).ConfigureAwait(false) is not string line)
{
LogTransportEndOfStream(Name);
break;
}
if (string.IsNullOrWhiteSpace(line))
{
continue;
}
LogTransportReceivedMessageSensitive(Name, line);
await ProcessMessageAsync(line, cancellationToken).ConfigureAwait(false);

We do have a some places that try to avoid strings -

int maxByteCount = Encoding.UTF8.GetMaxByteCount(value.Length);
Span<byte> buffer = writer.GetSpan(maxByteCount);
Debug.Assert(buffer.Length >= maxByteCount);
int bytesWritten = Encoding.UTF8.GetBytes(value, buffer);
writer.Advance(bytesWritten);

If folks agree we can make a pass at these and related types to avoid encoding to strings and go straight from UTF-8 bytes on the wire to serialized objects.

Metadata

Metadata

Labels

No labels
No labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions