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

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions