Skip to content

Commit 0a3015f

Browse files
Copilotericstj
andcommitted
Address code review feedback: rename Data to DecodedData, use span-based encoding, remove System.Text qualifiers
Co-authored-by: ericstj <8918108+ericstj@users.noreply.github.com>
1 parent b3eacff commit 0a3015f

7 files changed

Lines changed: 67 additions & 22 deletions

File tree

samples/EverythingServer/Resources/SimpleResourceType.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using ModelContextProtocol.Protocol;
22
using ModelContextProtocol.Server;
33
using System.ComponentModel;
4+
using System.Text;
45

56
namespace EverythingServer.Resources;
67

@@ -31,7 +32,7 @@ public static ResourceContents TemplateResource(RequestContext<ReadResourceReque
3132
} :
3233
new BlobResourceContents
3334
{
34-
Blob = System.Text.Encoding.UTF8.GetBytes(resource.Description!),
35+
Blob = Encoding.UTF8.GetBytes(resource.Description!),
3536
MimeType = resource.MimeType,
3637
Uri = resource.Uri,
3738
};

samples/EverythingServer/Tools/AnnotatedMessageTool.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using ModelContextProtocol.Protocol;
22
using ModelContextProtocol.Server;
33
using System.ComponentModel;
4+
using System.Text;
45

56
namespace EverythingServer.Tools;
67

@@ -41,7 +42,7 @@ public static IEnumerable<ContentBlock> AnnotatedMessage(MessageType messageType
4142
{
4243
contents.Add(new ImageContentBlock
4344
{
44-
Data = System.Text.Encoding.UTF8.GetBytes(TinyImageTool.MCP_TINY_IMAGE.Split(",").Last()),
45+
Data = Encoding.UTF8.GetBytes(TinyImageTool.MCP_TINY_IMAGE.Split(",").Last()),
4546
MimeType = "image/png",
4647
Annotations = new() { Audience = [Role.User], Priority = 0.5f }
4748
});
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
#if !NET
5+
6+
namespace System.Text;
7+
8+
internal static class EncodingExtensions
9+
{
10+
/// <summary>
11+
/// Gets the number of bytes required to encode the specified characters.
12+
/// </summary>
13+
public static int GetByteCount(this Encoding encoding, ReadOnlySpan<char> chars)
14+
{
15+
if (chars.IsEmpty)
16+
{
17+
return 0;
18+
}
19+
20+
unsafe
21+
{
22+
fixed (char* charsPtr = chars)
23+
{
24+
return encoding.GetByteCount(charsPtr, chars.Length);
25+
}
26+
}
27+
}
28+
29+
/// <summary>
30+
/// Encodes the specified characters into the specified byte span.
31+
/// </summary>
32+
public static int GetBytes(this Encoding encoding, ReadOnlySpan<char> chars, Span<byte> bytes)
33+
{
34+
if (chars.IsEmpty)
35+
{
36+
return 0;
37+
}
38+
39+
unsafe
40+
{
41+
fixed (char* charsPtr = chars)
42+
fixed (byte* bytesPtr = bytes)
43+
{
44+
return encoding.GetBytes(charsPtr, chars.Length, bytesPtr, bytes.Length);
45+
}
46+
}
47+
}
48+
}
49+
50+
#endif

src/ModelContextProtocol.Core/AIContentExtensions.cs

Lines changed: 7 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
using Microsoft.Extensions.AI;
22
using ModelContextProtocol.Client;
33
using ModelContextProtocol.Protocol;
4-
#if !NET
5-
using System.Runtime.InteropServices;
6-
#endif
4+
using System.Text;
75
using System.Text.Json;
86
using System.Text.Json.Nodes;
97

@@ -324,7 +322,7 @@ public static AIContent ToAIContent(this ResourceContents content)
324322

325323
AIContent ac = content switch
326324
{
327-
BlobResourceContents blobResource => new DataContent(blobResource.Data, blobResource.MimeType ?? "application/octet-stream"),
325+
BlobResourceContents blobResource => new DataContent(blobResource.DecodedData, blobResource.MimeType ?? "application/octet-stream"),
328326
TextResourceContents textResource => new TextContent(textResource.Text),
329327
_ => throw new NotSupportedException($"Resource type '{content.GetType().Name}' is not supported.")
330328
};
@@ -449,20 +447,12 @@ public static ContentBlock ToContentBlock(this AIContent content, JsonSerializer
449447

450448
return contentBlock;
451449

452-
unsafe byte[] GetUtf8Bytes(ReadOnlySpan<char> utf16)
450+
static byte[] GetUtf8Bytes(ReadOnlySpan<char> utf16)
453451
{
454-
// gets UTF-8 bytes from UTF-16 chars without intermediate string allocations
455-
fixed (char* pChars = utf16)
456-
{
457-
var byteCount = System.Text.Encoding.UTF8.GetByteCount(pChars, utf16.Length);
458-
var bytes = new byte[byteCount];
459-
460-
fixed (byte* pBytes = bytes)
461-
{
462-
System.Text.Encoding.UTF8.GetBytes(pChars, utf16.Length, pBytes, byteCount);
463-
}
464-
return bytes;
465-
}
452+
// Get UTF-8 bytes from UTF-16 chars without intermediate string allocations
453+
byte[] bytes = new byte[Encoding.UTF8.GetByteCount(utf16)];
454+
Encoding.UTF8.GetBytes(utf16, bytes);
455+
return bytes;
466456
}
467457
}
468458

src/ModelContextProtocol.Core/Protocol/BlobResourceContents.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ public static BlobResourceContents FromData(ReadOnlyMemory<byte> data, string ur
4747
byte[] buffer = new byte[maxLength];
4848
if (Base64.EncodeToUtf8(data.Span, buffer, out _, out int bytesWritten) == OperationStatus.Done)
4949
{
50+
Debug.Assert(bytesWritten == buffer.Length, "Base64 encoding should always produce exact length for non-padded input");
5051
blob = buffer.AsMemory(0, bytesWritten);
5152
}
5253
else
@@ -67,7 +68,7 @@ public static BlobResourceContents FromData(ReadOnlyMemory<byte> data, string ur
6768
/// Gets or sets the base64-encoded UTF-8 bytes representing the binary data of the item.
6869
/// </summary>
6970
/// <remarks>
70-
/// This is a zero-copy representation of the wire payload of this item. Setting this value will invalidate any cached value of <see cref="Data"/>.
71+
/// This is a zero-copy representation of the wire payload of this item. Setting this value will invalidate any cached value of <see cref="DecodedData"/>.
7172
/// </remarks>
7273
[JsonPropertyName("blob")]
7374
public required ReadOnlyMemory<byte> Blob
@@ -94,7 +95,7 @@ public required ReadOnlyMemory<byte> Blob
9495
/// </para>
9596
/// </remarks>
9697
[JsonIgnore]
97-
public ReadOnlyMemory<byte> Data
98+
public ReadOnlyMemory<byte> DecodedData
9899
{
99100
get
100101
{
@@ -121,7 +122,7 @@ private string DebuggerDisplay
121122
{
122123
get
123124
{
124-
string lengthDisplay = _decodedData is null ? DebuggerDisplayHelper.GetBase64LengthDisplay(Blob) : $"{Data.Length} bytes";
125+
string lengthDisplay = _decodedData is null ? DebuggerDisplayHelper.GetBase64LengthDisplay(Blob) : $"{DecodedData.Length} bytes";
125126
string mimeInfo = MimeType is not null ? $", MimeType = {MimeType}" : "";
126127
return $"Uri = \"{Uri}\"{mimeInfo}, Length = {lengthDisplay}";
127128
}

src/ModelContextProtocol/ModelContextProtocol.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
<PackageId>ModelContextProtocol</PackageId>
88
<Description>.NET SDK for the Model Context Protocol (MCP) with hosting and dependency injection extensions.</Description>
99
<PackageReadmeFile>README.md</PackageReadmeFile>
10+
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
1011
<!-- Suppress the experimental tasks warning -->
1112
<NoWarn>$(NoWarn);MCPEXP001</NoWarn>
1213
</PropertyGroup>

tests/Directory.Build.props

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)..'))" />
33

44
<PropertyGroup>
5+
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
56
<!-- Suppress the experimental tasks warning in test projects -->
67
<NoWarn>$(NoWarn);MCPEXP001</NoWarn>
78
</PropertyGroup>

0 commit comments

Comments
 (0)