diff --git a/samples/EverythingServer/Resources/SimpleResourceType.cs b/samples/EverythingServer/Resources/SimpleResourceType.cs
index 15d055d..eafc6c6 100644
--- a/samples/EverythingServer/Resources/SimpleResourceType.cs
+++ b/samples/EverythingServer/Resources/SimpleResourceType.cs
@@ -1,6 +1,7 @@
using ModelContextProtocol.Protocol;
using ModelContextProtocol.Server;
using System.ComponentModel;
+using System.Text;
namespace EverythingServer.Resources;
@@ -31,7 +32,7 @@ public class SimpleResourceType
} :
new BlobResourceContents
{
- Blob = resource.Description!,
+ Blob = Encoding.UTF8.GetBytes(resource.Description!),
MimeType = resource.MimeType,
Uri = resource.Uri,
};
diff --git a/samples/EverythingServer/Tools/AnnotatedMessageTool.cs b/samples/EverythingServer/Tools/AnnotatedMessageTool.cs
index 7f92d0a..4ebdf15 100644
--- a/samples/EverythingServer/Tools/AnnotatedMessageTool.cs
+++ b/samples/EverythingServer/Tools/AnnotatedMessageTool.cs
@@ -1,6 +1,7 @@
using ModelContextProtocol.Protocol;
using ModelContextProtocol.Server;
using System.ComponentModel;
+using System.Text;
namespace EverythingServer.Tools;
@@ -41,7 +42,7 @@ public class AnnotatedMessageTool
{
contents.Add(new ImageContentBlock
{
- Data = TinyImageTool.MCP_TINY_IMAGE.Split(",").Last(),
+ Data = Encoding.UTF8.GetBytes(TinyImageTool.MCP_TINY_IMAGE.Split(",").Last()),
MimeType = "image/png",
Annotations = new() { Audience = [Role.User], Priority = 0.5f }
});
diff --git a/src/Common/EncodingUtilities.cs b/src/Common/EncodingUtilities.cs
new file mode 100644
index 0000000..5012788
--- /dev/null
+++ b/src/Common/EncodingUtilities.cs
@@ -0,0 +1,59 @@
+using System.Buffers;
+using System.Buffers.Text;
+using System.Diagnostics;
+using System.Text;
+
+namespace ModelContextProtocol;
+
+/// Provides helper methods for encoding operations.
+internal static class EncodingUtilities
+{
+ ///
+ /// Converts UTF-16 characters to UTF-8 bytes without intermediate string allocations.
+ ///
+ /// The UTF-16 character span to convert.
+ /// A byte array containing the UTF-8 encoded bytes.
+ public static byte[] GetUtf8Bytes(ReadOnlySpan utf16)
+ {
+ byte[] bytes = new byte[Encoding.UTF8.GetByteCount(utf16)];
+ Encoding.UTF8.GetBytes(utf16, bytes);
+ return bytes;
+ }
+
+ ///
+ /// Encodes binary data to base64-encoded UTF-8 bytes.
+ ///
+ /// The binary data to encode.
+ /// A ReadOnlyMemory containing the base64-encoded UTF-8 bytes.
+ public static ReadOnlyMemory EncodeToBase64Utf8(ReadOnlyMemory data)
+ {
+ int maxLength = Base64.GetMaxEncodedToUtf8Length(data.Length);
+ byte[] buffer = new byte[maxLength];
+ OperationStatus status = Base64.EncodeToUtf8(data.Span, buffer, out _, out int bytesWritten);
+ Debug.Assert(status == OperationStatus.Done, "Base64 encoding should succeed for valid input data");
+ Debug.Assert(bytesWritten == buffer.Length, "Base64 encoding should always produce the same length as the max length");
+ return buffer.AsMemory(0, bytesWritten);
+ }
+
+ ///
+ /// Decodes base64-encoded UTF-8 bytes to binary data.
+ ///
+ /// The base64-encoded UTF-8 bytes to decode.
+ /// A ReadOnlyMemory containing the decoded binary data.
+ /// The input is not valid base64 data.
+ public static ReadOnlyMemory DecodeFromBase64Utf8(ReadOnlyMemory base64Data)
+ {
+ int maxLength = Base64.GetMaxDecodedFromUtf8Length(base64Data.Length);
+ byte[] buffer = new byte[maxLength];
+ if (Base64.DecodeFromUtf8(base64Data.Span, buffer, out _, out int bytesWritten) == OperationStatus.Done)
+ {
+ // Base64 decoding may produce fewer bytes than the max length, due to whitespace anywhere in the string or padding.
+ Debug.Assert(bytesWritten <= buffer.Length, "Base64 decoding should never produce more bytes than the max length");
+ return buffer.AsMemory(0, bytesWritten);
+ }
+ else
+ {
+ throw new FormatException("Invalid base64 data");
+ }
+ }
+}
diff --git a/src/Common/Polyfills/System/Text/EncodingExtensions.cs b/src/Common/Polyfills/System/Text/EncodingExtensions.cs
new file mode 100644
index 0000000..65369ad
--- /dev/null
+++ b/src/Common/Polyfills/System/Text/EncodingExtensions.cs
@@ -0,0 +1,50 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#if !NET
+
+namespace System.Text;
+
+internal static class EncodingExtensions
+{
+ ///
+ /// Gets the number of bytes required to encode the specified characters.
+ ///
+ public static int GetByteCount(this Encoding encoding, ReadOnlySpan chars)
+ {
+ if (chars.IsEmpty)
+ {
+ return 0;
+ }
+
+ unsafe
+ {
+ fixed (char* charsPtr = chars)
+ {
+ return encoding.GetByteCount(charsPtr, chars.Length);
+ }
+ }
+ }
+
+ ///
+ /// Encodes the specified characters into the specified byte span.
+ ///
+ public static int GetBytes(this Encoding encoding, ReadOnlySpan chars, Span bytes)
+ {
+ if (chars.IsEmpty)
+ {
+ return 0;
+ }
+
+ unsafe
+ {
+ fixed (char* charsPtr = chars)
+ fixed (byte* bytesPtr = bytes)
+ {
+ return encoding.GetBytes(charsPtr, chars.Length, bytesPtr, bytes.Length);
+ }
+ }
+ }
+}
+
+#endif
diff --git a/src/Common/ServerSentEvents/SseEventWriterHelpers.cs b/src/Common/ServerSentEvents/SseEventWriterHelpers.cs
index 53e315d..57fc17a 100644
--- a/src/Common/ServerSentEvents/SseEventWriterHelpers.cs
+++ b/src/Common/ServerSentEvents/SseEventWriterHelpers.cs
@@ -47,20 +47,12 @@ internal static class SseEventWriterHelpers
return;
}
-#if NET
int maxByteCount = Encoding.UTF8.GetMaxByteCount(value.Length);
Span buffer = writer.GetSpan(maxByteCount);
Debug.Assert(buffer.Length >= maxByteCount);
int bytesWritten = Encoding.UTF8.GetBytes(value, buffer);
writer.Advance(bytesWritten);
-#else
- // netstandard2.0 doesn't have the Span overload of GetBytes
- byte[] bytes = Encoding.UTF8.GetBytes(value.ToString());
- Span buffer = writer.GetSpan(bytes.Length);
- bytes.AsSpan().CopyTo(buffer);
- writer.Advance(bytes.Length);
-#endif
}
public static bool ContainsLineBreaks(this ReadOnlySpan text) =>
diff --git a/src/ModelContextProtocol.Core/AIContentExtensions.cs b/src/ModelContextProtocol.Core/AIContentExtensions.cs
index 03f9642..a07a22b 100644
--- a/src/ModelContextProtocol.Core/AIContentExtensions.cs
+++ b/src/ModelContextProtocol.Core/AIContentExtensions.cs
@@ -1,9 +1,7 @@
using Microsoft.Extensions.AI;
using ModelContextProtocol.Client;
using ModelContextProtocol.Protocol;
-#if !NET
-using System.Runtime.InteropServices;
-#endif
+using System.Text;
using System.Text.Json;
using System.Text.Json.Nodes;
@@ -281,9 +279,9 @@ public static class AIContentExtensions
{
TextContentBlock textContent => new TextContent(textContent.Text),
- ImageContentBlock imageContent => new DataContent(Convert.FromBase64String(imageContent.Data), imageContent.MimeType),
+ ImageContentBlock imageContent => new DataContent(imageContent.DecodedData, imageContent.MimeType),
- AudioContentBlock audioContent => new DataContent(Convert.FromBase64String(audioContent.Data), audioContent.MimeType),
+ AudioContentBlock audioContent => new DataContent(audioContent.DecodedData, audioContent.MimeType),
EmbeddedResourceBlock resourceContent => resourceContent.Resource.ToAIContent(),
@@ -324,7 +322,7 @@ public static class AIContentExtensions
AIContent ac = content switch
{
- BlobResourceContents blobResource => new DataContent(Convert.FromBase64String(blobResource.Blob), blobResource.MimeType ?? "application/octet-stream"),
+ BlobResourceContents blobResource => new DataContent(blobResource.DecodedData, blobResource.MimeType ?? "application/octet-stream"),
TextResourceContents textResource => new TextContent(textResource.Text),
_ => throw new NotSupportedException($"Resource type '{content.GetType().Name}' is not supported.")
};
@@ -401,13 +399,13 @@ public static class AIContentExtensions
DataContent dataContent when dataContent.HasTopLevelMediaType("image") => new ImageContentBlock
{
- Data = dataContent.Base64Data.ToString(),
+ Data = EncodingUtilities.GetUtf8Bytes(dataContent.Base64Data.Span),
MimeType = dataContent.MediaType,
},
DataContent dataContent when dataContent.HasTopLevelMediaType("audio") => new AudioContentBlock
{
- Data = dataContent.Base64Data.ToString(),
+ Data = EncodingUtilities.GetUtf8Bytes(dataContent.Base64Data.Span),
MimeType = dataContent.MediaType,
},
@@ -415,7 +413,7 @@ public static class AIContentExtensions
{
Resource = new BlobResourceContents
{
- Blob = dataContent.Base64Data.ToString(),
+ Blob = EncodingUtilities.GetUtf8Bytes(dataContent.Base64Data.Span),
MimeType = dataContent.MediaType,
Uri = string.Empty,
}
diff --git a/src/ModelContextProtocol.Core/ModelContextProtocol.Core.csproj b/src/ModelContextProtocol.Core/ModelContextProtocol.Core.csproj
index bc4ae4c..05e732c 100644
--- a/src/ModelContextProtocol.Core/ModelContextProtocol.Core.csproj
+++ b/src/ModelContextProtocol.Core/ModelContextProtocol.Core.csproj
@@ -25,6 +25,7 @@
+
diff --git a/src/ModelContextProtocol.Core/Protocol/BlobResourceContents.cs b/src/ModelContextProtocol.Core/Protocol/BlobResourceContents.cs
index 904e71b..859d45a 100644
--- a/src/ModelContextProtocol.Core/Protocol/BlobResourceContents.cs
+++ b/src/ModelContextProtocol.Core/Protocol/BlobResourceContents.cs
@@ -1,4 +1,7 @@
+using System.Buffers;
+using System.Buffers.Text;
using System.Diagnostics;
+using System.Runtime.InteropServices;
using System.Text.Json.Serialization;
namespace ModelContextProtocol.Protocol;
@@ -9,7 +12,7 @@ namespace ModelContextProtocol.Protocol;
///
///
/// is used when binary data needs to be exchanged through
-/// the Model Context Protocol. The binary data is represented as a base64-encoded string
+/// the Model Context Protocol. The binary data is represented as base64-encoded UTF-8 bytes
/// in the property.
///
///
@@ -24,18 +27,76 @@ namespace ModelContextProtocol.Protocol;
[DebuggerDisplay("{DebuggerDisplay,nq}")]
public sealed class BlobResourceContents : ResourceContents
{
+ private ReadOnlyMemory? _decodedData;
+ private ReadOnlyMemory _blob;
+
///
- /// Gets or sets the base64-encoded string representing the binary data of the item.
+ /// Creates an from raw data.
///
+ /// The raw unencoded data.
+ /// The URI of the blob resource.
+ /// The optional MIME type of the data.
+ /// A new instance.
+ ///
+ public static BlobResourceContents FromBytes(ReadOnlyMemory bytes, string uri, string? mimeType = null)
+ {
+ ReadOnlyMemory blob = EncodingUtilities.EncodeToBase64Utf8(bytes);
+
+ return new()
+ {
+ _decodedData = bytes,
+ Blob = blob,
+ MimeType = mimeType,
+ Uri = uri
+ };
+ }
+
+ ///
+ /// Gets or sets the base64-encoded UTF-8 bytes representing the binary data of the item.
+ ///
+ ///
+ /// Setting this value will invalidate any cached value of .
+ ///
[JsonPropertyName("blob")]
- public required string Blob { get; set; }
+ public required ReadOnlyMemory Blob
+ {
+ get => _blob;
+ set
+ {
+ _blob = value;
+ _decodedData = null; // Invalidate cache
+ }
+ }
+
+ ///
+ /// Gets the decoded data represented by .
+ ///
+ ///
+ ///
+ /// When getting, this member will decode the value in and cache the result.
+ /// Subsequent accesses return the cached value unless is modified.
+ ///
+ ///
+ [JsonIgnore]
+ public ReadOnlyMemory DecodedData
+ {
+ get
+ {
+ if (_decodedData is null)
+ {
+ _decodedData = EncodingUtilities.DecodeFromBase64Utf8(Blob);
+ }
+
+ return _decodedData.Value;
+ }
+ }
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private string DebuggerDisplay
{
get
{
- string lengthDisplay = DebuggerDisplayHelper.GetBase64LengthDisplay(Blob);
+ string lengthDisplay = _decodedData is null ? DebuggerDisplayHelper.GetBase64LengthDisplay(Blob) : $"{DecodedData.Length} bytes";
string mimeInfo = MimeType is not null ? $", MimeType = {MimeType}" : "";
return $"Uri = \"{Uri}\"{mimeInfo}, Length = {lengthDisplay}";
}
diff --git a/src/ModelContextProtocol.Core/Protocol/ContentBlock.cs b/src/ModelContextProtocol.Core/Protocol/ContentBlock.cs
index c854787..ff3d15a 100644
--- a/src/ModelContextProtocol.Core/Protocol/ContentBlock.cs
+++ b/src/ModelContextProtocol.Core/Protocol/ContentBlock.cs
@@ -1,6 +1,9 @@
+using System.Buffers;
+using System.Buffers.Text;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
+using System.Runtime.InteropServices;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;
@@ -87,7 +90,7 @@ public abstract class ContentBlock
string? type = null;
string? text = null;
string? name = null;
- string? data = null;
+ ReadOnlyMemory? data = null;
string? mimeType = null;
string? uri = null;
string? description = null;
@@ -128,7 +131,7 @@ public abstract class ContentBlock
break;
case "data":
- data = reader.GetString();
+ data = reader.HasValueSequence ? reader.ValueSequence.ToArray() : reader.ValueSpan.ToArray();
break;
case "mimeType":
@@ -279,12 +282,12 @@ public abstract class ContentBlock
break;
case ImageContentBlock imageContent:
- writer.WriteString("data", imageContent.Data);
+ writer.WriteString("data", imageContent.Data.Span);
writer.WriteString("mimeType", imageContent.MimeType);
break;
case AudioContentBlock audioContent:
- writer.WriteString("data", audioContent.Data);
+ writer.WriteString("data", audioContent.Data.Span);
writer.WriteString("mimeType", audioContent.MimeType);
break;
@@ -376,14 +379,73 @@ public sealed class TextContentBlock : ContentBlock
[DebuggerDisplay("{DebuggerDisplay,nq}")]
public sealed class ImageContentBlock : ContentBlock
{
+ private ReadOnlyMemory? _decodedData;
+ private ReadOnlyMemory _data;
+
+ ///
+ /// Creates an from decoded image bytes.
+ ///
+ /// The unencoded image bytes.
+ /// The MIME type of the image.
+ /// A new instance.
+ ///
+ /// This method stores the provided bytes as and encodes them to base64 UTF-8 bytes for .
+ ///
+ ///
+ public static ImageContentBlock FromBytes(ReadOnlyMemory bytes, string mimeType)
+ {
+ ReadOnlyMemory data = EncodingUtilities.EncodeToBase64Utf8(bytes);
+
+ return new()
+ {
+ _decodedData = bytes,
+ Data = data,
+ MimeType = mimeType
+ };
+ }
+
///
public override string Type => "image";
///
- /// Gets or sets the base64-encoded image data.
+ /// Gets or sets the base64-encoded UTF-8 bytes representing the image data.
///
+ ///
+ /// Setting this value will invalidate any cached value of .
+ ///
[JsonPropertyName("data")]
- public required string Data { get; set; }
+ public required ReadOnlyMemory Data
+ {
+ get => _data;
+ set
+ {
+ _data = value;
+ _decodedData = null; // Invalidate cache
+ }
+ }
+
+ ///
+ /// Gets the decoded image data represented by .
+ ///
+ ///
+ ///
+ /// When getting, this member will decode the value in and cache the result.
+ /// Subsequent accesses return the cached value unless is modified.
+ ///
+ ///
+ [JsonIgnore]
+ public ReadOnlyMemory DecodedData
+ {
+ get
+ {
+ if (_decodedData is null)
+ {
+ _decodedData = EncodingUtilities.DecodeFromBase64Utf8(Data);
+ }
+
+ return _decodedData.Value;
+ }
+ }
///
/// Gets or sets the MIME type (or "media type") of the content, specifying the format of the data.
@@ -402,14 +464,73 @@ public sealed class ImageContentBlock : ContentBlock
[DebuggerDisplay("{DebuggerDisplay,nq}")]
public sealed class AudioContentBlock : ContentBlock
{
+ private ReadOnlyMemory? _decodedData;
+ private ReadOnlyMemory _data;
+
+ ///
+ /// Creates an from decoded audio bytes.
+ ///
+ /// The unencoded audio bytes.
+ /// The MIME type of the audio.
+ /// A new instance.
+ ///
+ /// This method stores the provided bytes as and encodes them to base64 UTF-8 bytes for .
+ ///
+ ///
+ public static AudioContentBlock FromBytes(ReadOnlyMemory bytes, string mimeType)
+ {
+ ReadOnlyMemory data = EncodingUtilities.EncodeToBase64Utf8(bytes);
+
+ return new()
+ {
+ _decodedData = bytes,
+ Data = data,
+ MimeType = mimeType
+ };
+ }
+
///
public override string Type => "audio";
///
- /// Gets or sets the base64-encoded audio data.
+ /// Gets or sets the base64-encoded UTF-8 bytes representing the audio data.
///
+ ///
+ /// Setting this value will invalidate any cached value of .
+ ///
[JsonPropertyName("data")]
- public required string Data { get; set; }
+ public required ReadOnlyMemory Data
+ {
+ get => _data;
+ set
+ {
+ _data = value;
+ _decodedData = null; // Invalidate cache
+ }
+ }
+
+ ///
+ /// Gets the decoded audio data represented by .
+ ///
+ ///
+ ///
+ /// When getting, this member will decode the value in and cache the result.
+ /// Subsequent accesses return the cached value unless is modified.
+ ///
+ ///
+ [JsonIgnore]
+ public ReadOnlyMemory DecodedData
+ {
+ get
+ {
+ if (_decodedData is null)
+ {
+ _decodedData = EncodingUtilities.DecodeFromBase64Utf8(Data);
+ }
+
+ return _decodedData.Value;
+ }
+ }
///
/// Gets or sets the MIME type (or "media type") of the content, specifying the format of the data.
diff --git a/src/ModelContextProtocol.Core/Protocol/DebuggerDisplayHelper.cs b/src/ModelContextProtocol.Core/Protocol/DebuggerDisplayHelper.cs
index 77f7193..1300580 100644
--- a/src/ModelContextProtocol.Core/Protocol/DebuggerDisplayHelper.cs
+++ b/src/ModelContextProtocol.Core/Protocol/DebuggerDisplayHelper.cs
@@ -27,4 +27,25 @@ internal static class DebuggerDisplayHelper
return "invalid base64";
}
+
+ ///
+ /// Gets the decoded length of base64 data encoded as UTF-8 bytes for debugger display.
+ ///
+ internal static string GetBase64LengthDisplay(ReadOnlyMemory base64Data)
+ {
+#if NET
+ if (System.Buffers.Text.Base64.IsValid(base64Data.Span, out int decodedLength))
+ {
+ return $"{decodedLength} bytes";
+ }
+#else
+ try
+ {
+ return $"{Convert.FromBase64String(System.Text.Encoding.UTF8.GetString(base64Data.ToArray())).Length} bytes";
+ }
+ catch { }
+#endif
+
+ return "invalid base64";
+ }
}
diff --git a/src/ModelContextProtocol.Core/Protocol/ResourceContents.cs b/src/ModelContextProtocol.Core/Protocol/ResourceContents.cs
index 0948c42..fe1b7ff 100644
--- a/src/ModelContextProtocol.Core/Protocol/ResourceContents.cs
+++ b/src/ModelContextProtocol.Core/Protocol/ResourceContents.cs
@@ -1,3 +1,4 @@
+using System.Buffers;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
@@ -78,7 +79,7 @@ public abstract class ResourceContents
string? uri = null;
string? mimeType = null;
- string? blob = null;
+ ReadOnlyMemory? blob = null;
string? text = null;
JsonObject? meta = null;
@@ -104,7 +105,7 @@ public abstract class ResourceContents
break;
case "blob":
- blob = reader.GetString();
+ blob = reader.HasValueSequence ? reader.ValueSequence.ToArray() : reader.ValueSpan.ToArray();
break;
case "text":
@@ -127,7 +128,7 @@ public abstract class ResourceContents
{
Uri = uri ?? string.Empty,
MimeType = mimeType,
- Blob = blob,
+ Blob = blob.Value,
Meta = meta,
};
}
@@ -162,7 +163,7 @@ public abstract class ResourceContents
Debug.Assert(value is BlobResourceContents or TextResourceContents);
if (value is BlobResourceContents blobResource)
{
- writer.WriteString("blob", blobResource.Blob);
+ writer.WriteString("blob", blobResource.Blob.Span);
}
else if (value is TextResourceContents textResource)
{
diff --git a/src/ModelContextProtocol.Core/Server/AIFunctionMcpServerResource.cs b/src/ModelContextProtocol.Core/Server/AIFunctionMcpServerResource.cs
index fcd855d..469ba5d 100644
--- a/src/ModelContextProtocol.Core/Server/AIFunctionMcpServerResource.cs
+++ b/src/ModelContextProtocol.Core/Server/AIFunctionMcpServerResource.cs
@@ -7,6 +7,7 @@ using System.Diagnostics;
using System.Globalization;
using System.Reflection;
using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
using System.Text;
using System.Text.Json;
using System.Text.Json.Nodes;
@@ -391,7 +392,12 @@ internal sealed class AIFunctionMcpServerResource : McpServerResource
DataContent dc => new()
{
- Contents = [new BlobResourceContents { Uri = request.Params!.Uri, MimeType = dc.MediaType, Blob = dc.Base64Data.ToString() }],
+ Contents = [new BlobResourceContents
+ {
+ Uri = request.Params!.Uri,
+ MimeType = dc.MediaType,
+ Blob = EncodingUtilities.GetUtf8Bytes(dc.Base64Data.Span)
+ }],
},
string text => new()
@@ -420,7 +426,7 @@ internal sealed class AIFunctionMcpServerResource : McpServerResource
{
Uri = request.Params!.Uri,
MimeType = dc.MediaType,
- Blob = dc.Base64Data.ToString()
+ Blob = EncodingUtilities.GetUtf8Bytes(dc.Base64Data.Span)
},
_ => throw new InvalidOperationException($"Unsupported AIContent type '{ac.GetType()}' returned from resource function."),
diff --git a/src/ModelContextProtocol/ModelContextProtocol.csproj b/src/ModelContextProtocol/ModelContextProtocol.csproj
index 0477ca5..2cbf513 100644
--- a/src/ModelContextProtocol/ModelContextProtocol.csproj
+++ b/src/ModelContextProtocol/ModelContextProtocol.csproj
@@ -7,6 +7,7 @@
ModelContextProtocol
.NET SDK for the Model Context Protocol (MCP) with hosting and dependency injection extensions.
README.md
+ True
$(NoWarn);MCPEXP001
diff --git a/tests/Directory.Build.props b/tests/Directory.Build.props
index 11327f9..2f0b767 100644
--- a/tests/Directory.Build.props
+++ b/tests/Directory.Build.props
@@ -2,6 +2,7 @@
+ True
$(NoWarn);MCPEXP001
diff --git a/tests/ModelContextProtocol.AspNetCore.Tests/HttpServerIntegrationTests.cs b/tests/ModelContextProtocol.AspNetCore.Tests/HttpServerIntegrationTests.cs
index ce4f3b5..0af1bdc 100644
--- a/tests/ModelContextProtocol.AspNetCore.Tests/HttpServerIntegrationTests.cs
+++ b/tests/ModelContextProtocol.AspNetCore.Tests/HttpServerIntegrationTests.cs
@@ -180,7 +180,7 @@ public abstract class HttpServerIntegrationTests : LoggedTest, IClassFixture(result.Contents[0]);
- Assert.NotNull(blobContent.Blob);
+ Assert.False(blobContent.Blob.IsEmpty);
}
[Fact]
diff --git a/tests/ModelContextProtocol.ConformanceServer/Prompts/ConformancePrompts.cs b/tests/ModelContextProtocol.ConformanceServer/Prompts/ConformancePrompts.cs
index 345e215..b0b62b9 100644
--- a/tests/ModelContextProtocol.ConformanceServer/Prompts/ConformancePrompts.cs
+++ b/tests/ModelContextProtocol.ConformanceServer/Prompts/ConformancePrompts.cs
@@ -54,7 +54,7 @@ public class ConformancePrompts
Content = new ImageContentBlock
{
MimeType = "image/png",
- Data = TestImageBase64
+ Data = System.Text.Encoding.UTF8.GetBytes(TestImageBase64)
}
},
new PromptMessage
diff --git a/tests/ModelContextProtocol.ConformanceServer/Resources/ConformanceResources.cs b/tests/ModelContextProtocol.ConformanceServer/Resources/ConformanceResources.cs
index 1e36cb6..1c680e3 100644
--- a/tests/ModelContextProtocol.ConformanceServer/Resources/ConformanceResources.cs
+++ b/tests/ModelContextProtocol.ConformanceServer/Resources/ConformanceResources.cs
@@ -34,7 +34,7 @@ public class ConformanceResources
{
Uri = "test://static-binary",
MimeType = "image/png",
- Blob = TestImageBase64
+ Blob = System.Text.Encoding.UTF8.GetBytes(TestImageBase64)
};
}
diff --git a/tests/ModelContextProtocol.ConformanceServer/Tools/ConformanceTools.cs b/tests/ModelContextProtocol.ConformanceServer/Tools/ConformanceTools.cs
index 8f1f017..0a1f7ec 100644
--- a/tests/ModelContextProtocol.ConformanceServer/Tools/ConformanceTools.cs
+++ b/tests/ModelContextProtocol.ConformanceServer/Tools/ConformanceTools.cs
@@ -36,7 +36,7 @@ public class ConformanceTools
{
return new ImageContentBlock
{
- Data = TestImageBase64,
+ Data = System.Text.Encoding.UTF8.GetBytes(TestImageBase64),
MimeType = "image/png"
};
}
@@ -50,7 +50,7 @@ public class ConformanceTools
{
return new AudioContentBlock
{
- Data = TestAudioBase64,
+ Data = System.Text.Encoding.UTF8.GetBytes(TestAudioBase64),
MimeType = "audio/wav"
};
}
@@ -83,7 +83,7 @@ public class ConformanceTools
return
[
new TextContentBlock { Text = "Multiple content types test:" },
- new ImageContentBlock { Data = TestImageBase64, MimeType = "image/png" },
+ new ImageContentBlock { Data = System.Text.Encoding.UTF8.GetBytes(TestImageBase64), MimeType = "image/png" },
new EmbeddedResourceBlock
{
Resource = new TextResourceContents
diff --git a/tests/ModelContextProtocol.TestServer/Program.cs b/tests/ModelContextProtocol.TestServer/Program.cs
index 7ac6513..a62ddf2 100644
--- a/tests/ModelContextProtocol.TestServer/Program.cs
+++ b/tests/ModelContextProtocol.TestServer/Program.cs
@@ -401,12 +401,7 @@ internal static class Program
Name = $"Resource {i + 1}",
MimeType = "application/octet-stream"
});
- resourceContents.Add(new BlobResourceContents
- {
- Uri = uri,
- MimeType = "application/octet-stream",
- Blob = Convert.ToBase64String(buffer)
- });
+ resourceContents.Add(BlobResourceContents.FromBytes(buffer, uri, "application/octet-stream"));
}
}
diff --git a/tests/ModelContextProtocol.TestSseServer/Program.cs b/tests/ModelContextProtocol.TestSseServer/Program.cs
index efd5b00..0a55169 100644
--- a/tests/ModelContextProtocol.TestSseServer/Program.cs
+++ b/tests/ModelContextProtocol.TestSseServer/Program.cs
@@ -85,12 +85,7 @@ public class Program
Name = $"Resource {i + 1}",
MimeType = "application/octet-stream"
});
- resourceContents.Add(new BlobResourceContents
- {
- Uri = uri,
- MimeType = "application/octet-stream",
- Blob = Convert.ToBase64String(buffer)
- });
+ resourceContents.Add(BlobResourceContents.FromBytes(buffer, uri, "application/octet-stream"));
}
}
@@ -381,7 +376,7 @@ public class Program
Role = Role.User,
Content = new ImageContentBlock
{
- Data = MCP_TINY_IMAGE,
+ Data = System.Text.Encoding.UTF8.GetBytes(MCP_TINY_IMAGE),
MimeType = "image/png"
}
});
diff --git a/tests/ModelContextProtocol.Tests/AIContentExtensionsTests.cs b/tests/ModelContextProtocol.Tests/AIContentExtensionsTests.cs
index 7d4d797..b46a342 100644
--- a/tests/ModelContextProtocol.Tests/AIContentExtensionsTests.cs
+++ b/tests/ModelContextProtocol.Tests/AIContentExtensionsTests.cs
@@ -95,7 +95,7 @@ public class AIContentExtensionsTests
Content =
[
new TextContentBlock { Text = "Text result" },
- new ImageContentBlock { Data = Convert.ToBase64String([1, 2, 3]), MimeType = "image/png" }
+ ImageContentBlock.FromBytes((byte[])[1, 2, 3], "image/png")
]
};
@@ -301,7 +301,7 @@ public class AIContentExtensionsTests
var contentBlock = dataContent.ToContentBlock();
var imageBlock = Assert.IsType(contentBlock);
- Assert.Equal(Convert.ToBase64String(imageData), imageBlock.Data);
+ Assert.Equal(imageData, imageBlock.DecodedData);
Assert.Equal("image/png", imageBlock.MimeType);
Assert.NotNull(imageBlock.Meta);
Assert.True(imageBlock.Meta.ContainsKey("dimensions"));
diff --git a/tests/ModelContextProtocol.Tests/Client/McpClientTests.cs b/tests/ModelContextProtocol.Tests/Client/McpClientTests.cs
index a465b47..26906f2 100644
--- a/tests/ModelContextProtocol.Tests/Client/McpClientTests.cs
+++ b/tests/ModelContextProtocol.Tests/Client/McpClientTests.cs
@@ -160,11 +160,7 @@ public class McpClientTests : ClientServerTestBase
new SamplingMessage
{
Role = Role.User,
- Content = [new ImageContentBlock
- {
- MimeType = "image/png",
- Data = Convert.ToBase64String(new byte[] { 1, 2, 3 })
- }],
+ Content = [ImageContentBlock.FromBytes((byte[])[1, 2, 3], "image/png")],
}
],
MaxTokens = 100
@@ -196,7 +192,8 @@ public class McpClientTests : ClientServerTestBase
// Assert
Assert.NotNull(result);
- Assert.Equal(expectedData, result.Content.OfType().FirstOrDefault()?.Data);
+ var imageData = result.Content.OfType().FirstOrDefault()?.Data.ToArray() ?? [];
+ Assert.Equal(expectedData, System.Text.Encoding.UTF8.GetString(imageData));
Assert.Equal("test-model", result.Model);
Assert.Equal(Role.Assistant, result.Role);
Assert.Equal("endTurn", result.StopReason);
@@ -211,7 +208,7 @@ public class McpClientTests : ClientServerTestBase
var mockChatClient = new Mock();
var resource = new BlobResourceContents
{
- Blob = data,
+ Blob = System.Text.Encoding.UTF8.GetBytes(data),
MimeType = "application/octet-stream",
Uri = "data:application/octet-stream"
};
diff --git a/tests/ModelContextProtocol.Tests/Client/McpClientToolTests.cs b/tests/ModelContextProtocol.Tests/Client/McpClientToolTests.cs
index 83d3aae..f6818fd 100644
--- a/tests/ModelContextProtocol.Tests/Client/McpClientToolTests.cs
+++ b/tests/ModelContextProtocol.Tests/Client/McpClientToolTests.cs
@@ -37,13 +37,13 @@ public class McpClientToolTests : ClientServerTestBase
[McpServerTool]
public static ImageContentBlock ImageTool() =>
new()
- { Data = Convert.ToBase64String(Encoding.UTF8.GetBytes("fake-image-data")), MimeType = "image/png" };
+ { Data = System.Text.Encoding.UTF8.GetBytes(Convert.ToBase64String(Encoding.UTF8.GetBytes("fake-image-data"))), MimeType = "image/png" };
// Tool that returns audio content as single ContentBlock
[McpServerTool]
public static AudioContentBlock AudioTool() =>
new()
- { Data = Convert.ToBase64String(Encoding.UTF8.GetBytes("fake-audio-data")), MimeType = "audio/mp3" };
+ { Data = System.Text.Encoding.UTF8.GetBytes(Convert.ToBase64String(Encoding.UTF8.GetBytes("fake-audio-data"))), MimeType = "audio/mp3" };
// Tool that returns embedded resource
[McpServerTool]
@@ -103,7 +103,7 @@ public class McpClientToolTests : ClientServerTestBase
[McpServerTool]
public static IEnumerable MixedWithNonConvertibleTool()
{
- yield return new ImageContentBlock { Data = Convert.ToBase64String(Encoding.UTF8.GetBytes("image-data")), MimeType = "image/png" };
+ yield return new ImageContentBlock { Data = System.Text.Encoding.UTF8.GetBytes(Convert.ToBase64String(Encoding.UTF8.GetBytes("image-data"))), MimeType = "image/png" };
yield return new ResourceLinkBlock { Uri = "file://linked.txt", Name = "linked.txt" };
}
@@ -152,7 +152,7 @@ public class McpClientToolTests : ClientServerTestBase
Resource = new BlobResourceContents
{
Uri = "data://blob",
- Blob = Convert.ToBase64String(Encoding.UTF8.GetBytes("binary-data")),
+ Blob = System.Text.Encoding.UTF8.GetBytes(Convert.ToBase64String(Encoding.UTF8.GetBytes("binary-data"))),
MimeType = "application/octet-stream"
}
};
diff --git a/tests/ModelContextProtocol.Tests/ClientIntegrationTests.cs b/tests/ModelContextProtocol.Tests/ClientIntegrationTests.cs
index 3eba027..079be04 100644
--- a/tests/ModelContextProtocol.Tests/ClientIntegrationTests.cs
+++ b/tests/ModelContextProtocol.Tests/ClientIntegrationTests.cs
@@ -260,7 +260,7 @@ public partial class ClientIntegrationTests : LoggedTest, IClassFixture(result.Contents[0]);
- Assert.NotNull(blobResource.Blob);
+ Assert.False(blobResource.Blob.IsEmpty);
}
// Not supported by "everything" server version on npx
diff --git a/tests/ModelContextProtocol.Tests/Protocol/ContentBlockTests.cs b/tests/ModelContextProtocol.Tests/Protocol/ContentBlockTests.cs
index 0113b77..f901d7e 100644
--- a/tests/ModelContextProtocol.Tests/Protocol/ContentBlockTests.cs
+++ b/tests/ModelContextProtocol.Tests/Protocol/ContentBlockTests.cs
@@ -183,7 +183,7 @@ public class ContentBlockTests
Content =
[
new TextContentBlock { Text = "Result data" },
- new ImageContentBlock { Data = "base64data", MimeType = "image/png" }
+ new ImageContentBlock { Data = System.Text.Encoding.UTF8.GetBytes("base64data"), MimeType = "image/png" }
],
StructuredContent = JsonElement.Parse("""{"temperature":18,"condition":"cloudy"}"""),
IsError = false
@@ -198,7 +198,7 @@ public class ContentBlockTests
var textBlock = Assert.IsType(result.Content[0]);
Assert.Equal("Result data", textBlock.Text);
var imageBlock = Assert.IsType(result.Content[1]);
- Assert.Equal("base64data", imageBlock.Data);
+ Assert.Equal("base64data", System.Text.Encoding.UTF8.GetString(imageBlock.Data.ToArray()));
Assert.Equal("image/png", imageBlock.MimeType);
Assert.NotNull(result.StructuredContent);
Assert.Equal(18, result.StructuredContent.Value.GetProperty("temperature").GetInt32());
diff --git a/tests/ModelContextProtocol.Tests/Protocol/CreateMessageResultTests.cs b/tests/ModelContextProtocol.Tests/Protocol/CreateMessageResultTests.cs
index 6648116..fb657d0 100644
--- a/tests/ModelContextProtocol.Tests/Protocol/CreateMessageResultTests.cs
+++ b/tests/ModelContextProtocol.Tests/Protocol/CreateMessageResultTests.cs
@@ -116,11 +116,7 @@ public class CreateMessageResultTests
Model = "test-model",
Content =
[
- new ImageContentBlock
- {
- Data = Convert.ToBase64String([1, 2, 3, 4, 5]),
- MimeType = "image/png"
- }
+ ImageContentBlock.FromBytes((byte[])[1, 2, 3, 4, 5], "image/png")
],
StopReason = "endTurn"
};
diff --git a/tests/ModelContextProtocol.Tests/Protocol/PromptMessageTests.cs b/tests/ModelContextProtocol.Tests/Protocol/PromptMessageTests.cs
index 5e4a7f8..b89f2a4 100644
--- a/tests/ModelContextProtocol.Tests/Protocol/PromptMessageTests.cs
+++ b/tests/ModelContextProtocol.Tests/Protocol/PromptMessageTests.cs
@@ -29,7 +29,7 @@ public static class PromptMessageTests
var original = new PromptMessage
{
Role = Role.Assistant,
- Content = new ImageContentBlock { Data = "base64data", MimeType = "image/png" }
+ Content = new ImageContentBlock { Data = System.Text.Encoding.UTF8.GetBytes("base64data"), MimeType = "image/png" }
};
string json = JsonSerializer.Serialize(original, McpJsonUtilities.DefaultOptions);
@@ -38,7 +38,7 @@ public static class PromptMessageTests
Assert.NotNull(deserialized);
Assert.Equal(Role.Assistant, deserialized.Role);
var imageBlock = Assert.IsType(deserialized.Content);
- Assert.Equal("base64data", imageBlock.Data);
+ Assert.Equal("base64data", System.Text.Encoding.UTF8.GetString(imageBlock.Data.ToArray()));
Assert.Equal("image/png", imageBlock.MimeType);
}
}
diff --git a/tests/ModelContextProtocol.Tests/Protocol/ReadResourceResultTests.cs b/tests/ModelContextProtocol.Tests/Protocol/ReadResourceResultTests.cs
index 9607a75..fadfba6 100644
--- a/tests/ModelContextProtocol.Tests/Protocol/ReadResourceResultTests.cs
+++ b/tests/ModelContextProtocol.Tests/Protocol/ReadResourceResultTests.cs
@@ -23,7 +23,7 @@ public static class ReadResourceResultTests
{
Uri = "file:///image.png",
MimeType = "image/png",
- Blob = "base64data"
+ Blob = System.Text.Encoding.UTF8.GetBytes("base64data")
}
],
Meta = new JsonObject { ["key"] = "value" }
@@ -43,7 +43,7 @@ public static class ReadResourceResultTests
var blobContent = Assert.IsType(deserialized.Contents[1]);
Assert.Equal("file:///image.png", blobContent.Uri);
Assert.Equal("image/png", blobContent.MimeType);
- Assert.Equal("base64data", blobContent.Blob);
+ Assert.Equal("base64data", System.Text.Encoding.UTF8.GetString(blobContent.Blob.ToArray()));
Assert.NotNull(deserialized.Meta);
Assert.Equal("value", (string)deserialized.Meta["key"]!);
diff --git a/tests/ModelContextProtocol.Tests/Protocol/ResourceContentsTests.cs b/tests/ModelContextProtocol.Tests/Protocol/ResourceContentsTests.cs
index 4f9890f..0343970 100644
--- a/tests/ModelContextProtocol.Tests/Protocol/ResourceContentsTests.cs
+++ b/tests/ModelContextProtocol.Tests/Protocol/ResourceContentsTests.cs
@@ -70,7 +70,7 @@ public static class ResourceContentsTests
var blobResource = Assert.IsType(result);
Assert.Equal("file:///test.bin", blobResource.Uri);
Assert.Equal("application/octet-stream", blobResource.MimeType);
- Assert.Equal("AQIDBA==", blobResource.Blob);
+ Assert.Equal("AQIDBA==", System.Text.Encoding.UTF8.GetString(blobResource.Blob.ToArray()));
}
[Fact]
@@ -134,7 +134,7 @@ public static class ResourceContentsTests
Assert.NotNull(result);
var blobResource = Assert.IsType(result);
Assert.Equal("blob://test", blobResource.Uri);
- Assert.Equal("SGVsbG8=", blobResource.Blob);
+ Assert.Equal("SGVsbG8=", System.Text.Encoding.UTF8.GetString(blobResource.Blob.ToArray()));
Assert.Equal("application/custom", blobResource.MimeType);
}
@@ -193,7 +193,7 @@ public static class ResourceContentsTests
Assert.NotNull(result);
var blobResource = Assert.IsType(result);
Assert.Equal("http://example.com/blob", blobResource.Uri);
- Assert.Equal("Zm9v", blobResource.Blob);
+ Assert.Equal("Zm9v", System.Text.Encoding.UTF8.GetString(blobResource.Blob.ToArray()));
}
[Fact]
@@ -239,7 +239,7 @@ public static class ResourceContentsTests
Assert.NotNull(result);
var blobResource = Assert.IsType(result);
Assert.Equal("test://blob", blobResource.Uri);
- Assert.Equal("YmFy", blobResource.Blob);
+ Assert.Equal("YmFy", System.Text.Encoding.UTF8.GetString(blobResource.Blob.ToArray()));
}
[Fact]
@@ -301,7 +301,7 @@ public static class ResourceContentsTests
Assert.NotNull(result);
var blobResource = Assert.IsType(result);
Assert.Equal("deep://blob", blobResource.Uri);
- Assert.Equal("ZGVlcA==", blobResource.Blob);
+ Assert.Equal("ZGVlcA==", System.Text.Encoding.UTF8.GetString(blobResource.Blob.ToArray()));
}
[Fact]
@@ -363,7 +363,7 @@ public static class ResourceContentsTests
{
Uri = "file:///test.bin",
MimeType = "application/octet-stream",
- Blob = "AQIDBA=="
+ Blob = System.Text.Encoding.UTF8.GetBytes("AQIDBA==")
};
var json = JsonSerializer.Serialize(original, McpJsonUtilities.DefaultOptions);
@@ -373,7 +373,7 @@ public static class ResourceContentsTests
var blobResource = Assert.IsType(deserialized);
Assert.Equal(original.Uri, blobResource.Uri);
Assert.Equal(original.MimeType, blobResource.MimeType);
- Assert.Equal(original.Blob, blobResource.Blob);
+ Assert.True(original.Blob.Span.SequenceEqual(blobResource.Blob.Span));
}
[Fact]
@@ -415,7 +415,7 @@ public static class ResourceContentsTests
Assert.NotNull(result);
var blobResource = Assert.IsType(result);
Assert.Equal("test://both", blobResource.Uri);
- Assert.Equal("YmxvYg==", blobResource.Blob);
+ Assert.Equal("YmxvYg==", System.Text.Encoding.UTF8.GetString(blobResource.Blob.ToArray()));
}
[Fact]
@@ -457,6 +457,6 @@ public static class ResourceContentsTests
Assert.NotNull(result);
var blobResource = Assert.IsType(result);
Assert.Equal(string.Empty, blobResource.Uri);
- Assert.Equal("YmxvYg==", blobResource.Blob);
+ Assert.Equal("YmxvYg==", System.Text.Encoding.UTF8.GetString(blobResource.Blob.ToArray()));
}
}
diff --git a/tests/ModelContextProtocol.Tests/Protocol/UnknownPropertiesTests.cs b/tests/ModelContextProtocol.Tests/Protocol/UnknownPropertiesTests.cs
index fd3117d..1501553 100644
--- a/tests/ModelContextProtocol.Tests/Protocol/UnknownPropertiesTests.cs
+++ b/tests/ModelContextProtocol.Tests/Protocol/UnknownPropertiesTests.cs
@@ -75,7 +75,7 @@ public class UnknownPropertiesTests
// Assert
Assert.NotNull(deserialized);
var imageBlock = Assert.IsType(deserialized);
- Assert.Equal("base64data", imageBlock.Data);
+ Assert.Equal("base64data", System.Text.Encoding.UTF8.GetString(imageBlock.Data.ToArray()));
Assert.Equal("image/png", imageBlock.MimeType);
}
diff --git a/tests/ModelContextProtocol.Tests/Server/McpServerResourceTests.cs b/tests/ModelContextProtocol.Tests/Server/McpServerResourceTests.cs
index 920d415..695bbf3 100644
--- a/tests/ModelContextProtocol.Tests/Server/McpServerResourceTests.cs
+++ b/tests/ModelContextProtocol.Tests/Server/McpServerResourceTests.cs
@@ -575,7 +575,7 @@ public partial class McpServerResourceTests
return (IList)
[
new TextResourceContents { Text = "hello", Uri = "" },
- new BlobResourceContents { Blob = Convert.ToBase64String(new byte[] { 1, 2, 3 }), Uri = "" },
+ BlobResourceContents.FromBytes((byte[])[1, 2, 3], ""),
];
}, new() { Name = "Test" });
var result = await resource.ReadAsync(
@@ -584,7 +584,7 @@ public partial class McpServerResourceTests
Assert.NotNull(result);
Assert.Equal(2, result.Contents.Count);
Assert.Equal("hello", ((TextResourceContents)result.Contents[0]).Text);
- Assert.Equal(Convert.ToBase64String(new byte[] { 1, 2, 3 }), ((BlobResourceContents)result.Contents[1]).Blob);
+ Assert.Equal(Convert.ToBase64String(new byte[] { 1, 2, 3 }), System.Text.Encoding.UTF8.GetString(((BlobResourceContents)result.Contents[1]).Blob.ToArray()));
}
[Fact]
@@ -636,7 +636,7 @@ public partial class McpServerResourceTests
TestContext.Current.CancellationToken);
Assert.NotNull(result);
Assert.Single(result.Contents);
- Assert.Equal(Convert.ToBase64String(new byte[] { 0, 1, 2 }), ((BlobResourceContents)result.Contents[0]).Blob);
+ Assert.Equal(Convert.ToBase64String(new byte[] { 0, 1, 2 }), System.Text.Encoding.UTF8.GetString(((BlobResourceContents)result.Contents[0]).Blob.ToArray()));
Assert.Equal("application/octet-stream", ((BlobResourceContents)result.Contents[0]).MimeType);
}
@@ -659,7 +659,7 @@ public partial class McpServerResourceTests
Assert.NotNull(result);
Assert.Equal(2, result.Contents.Count);
Assert.Equal("hello!", ((TextResourceContents)result.Contents[0]).Text);
- Assert.Equal(Convert.ToBase64String(new byte[] { 4, 5, 6 }), ((BlobResourceContents)result.Contents[1]).Blob);
+ Assert.Equal(Convert.ToBase64String(new byte[] { 4, 5, 6 }), System.Text.Encoding.UTF8.GetString(((BlobResourceContents)result.Contents[1]).Blob.ToArray()));
Assert.Equal("application/json", ((BlobResourceContents)result.Contents[1]).MimeType);
}
diff --git a/tests/ModelContextProtocol.Tests/Server/McpServerToolTests.cs b/tests/ModelContextProtocol.Tests/Server/McpServerToolTests.cs
index 2e826d5..552eaed 100644
--- a/tests/ModelContextProtocol.Tests/Server/McpServerToolTests.cs
+++ b/tests/ModelContextProtocol.Tests/Server/McpServerToolTests.cs
@@ -270,10 +270,10 @@ public partial class McpServerToolTests
Assert.Equal("text", (result.Content[0] as TextContentBlock)?.Text);
- Assert.Equal("1234", (result.Content[1] as ImageContentBlock)?.Data);
+ Assert.Equal("1234", System.Text.Encoding.UTF8.GetString((result.Content[1] as ImageContentBlock)?.Data.ToArray() ?? []));
Assert.Equal("image/png", (result.Content[1] as ImageContentBlock)?.MimeType);
- Assert.Equal("1234", (result.Content[2] as AudioContentBlock)?.Data);
+ Assert.Equal("1234", System.Text.Encoding.UTF8.GetString((result.Content[2] as AudioContentBlock)?.Data.ToArray() ?? []));
Assert.Equal("audio/wav", (result.Content[2] as AudioContentBlock)?.MimeType);
}
@@ -309,12 +309,12 @@ public partial class McpServerToolTests
}
else if (result.Content[0] is ImageContentBlock ic)
{
- Assert.Equal(data.Split(',').Last(), ic.Data);
+ Assert.Equal(data.Split(',').Last(), System.Text.Encoding.UTF8.GetString(ic.Data.ToArray()));
Assert.Equal("image/png", ic.MimeType);
}
else if (result.Content[0] is AudioContentBlock ac)
{
- Assert.Equal(data.Split(',').Last(), ac.Data);
+ Assert.Equal(data.Split(',').Last(), System.Text.Encoding.UTF8.GetString(ac.Data.ToArray()));
Assert.Equal("audio/wav", ac.MimeType);
}
else
@@ -397,7 +397,7 @@ public partial class McpServerToolTests
return (IList)
[
new TextContentBlock { Text = "42" },
- new ImageContentBlock { Data = "1234", MimeType = "image/png" }
+ ImageContentBlock.FromBytes((byte[])[1, 2, 3, 4], "image/png")
];
});
var result = await tool.InvokeAsync(
@@ -405,7 +405,7 @@ public partial class McpServerToolTests
TestContext.Current.CancellationToken);
Assert.Equal(2, result.Content.Count);
Assert.Equal("42", Assert.IsType(result.Content[0]).Text);
- Assert.Equal("1234", Assert.IsType(result.Content[1]).Data);
+ Assert.Equal((byte[])[1, 2, 3, 4], Assert.IsType(result.Content[1]).DecodedData.ToArray());
Assert.Equal("image/png", Assert.IsType(result.Content[1]).MimeType);
}
@@ -414,7 +414,7 @@ public partial class McpServerToolTests
{
CallToolResult response = new()
{
- Content = [new TextContentBlock { Text = "text" }, new ImageContentBlock { Data = "1234", MimeType = "image/png" }]
+ Content = [new TextContentBlock { Text = "text" }, ImageContentBlock.FromBytes((byte[])[1, 2, 3, 4], "image/png")]
};
Mock mockServer = new();
@@ -431,7 +431,7 @@ public partial class McpServerToolTests
Assert.Equal(2, result.Content.Count);
Assert.Equal("text", Assert.IsType(result.Content[0]).Text);
- Assert.Equal("1234", Assert.IsType(result.Content[1]).Data);
+ Assert.Equal((byte[])[1, 2, 3, 4], Assert.IsType(result.Content[1]).DecodedData.ToArray());
}
[Fact]