1+ using System . Buffers ;
2+ using System . Buffers . Text ;
13using System . Diagnostics ;
4+ using System . Runtime . InteropServices ;
25using System . Text . Json . Serialization ;
36
47namespace ModelContextProtocol . Protocol ;
@@ -9,7 +12,7 @@ namespace ModelContextProtocol.Protocol;
912/// <remarks>
1013/// <para>
1114/// <see cref="BlobResourceContents"/> is used when binary data needs to be exchanged through
12- /// the Model Context Protocol. The binary data is represented as a base64-encoded string
15+ /// the Model Context Protocol. The binary data is represented as base64-encoded UTF-8 bytes
1316/// in the <see cref="Blob"/> property.
1417/// </para>
1518/// <para>
@@ -24,18 +27,76 @@ namespace ModelContextProtocol.Protocol;
2427[ DebuggerDisplay ( "{DebuggerDisplay,nq}" ) ]
2528public sealed class BlobResourceContents : ResourceContents
2629{
30+ private ReadOnlyMemory < byte > ? _decodedData ;
31+ private ReadOnlyMemory < byte > _blob ;
32+
33+ /// <summary>
34+ /// Creates an <see cref="BlobResourceContents"/> from raw data.
35+ /// </summary>
36+ /// <param name="bytes">The raw unencoded data.</param>
37+ /// <param name="uri">The URI of the blob resource.</param>
38+ /// <param name="mimeType">The optional MIME type of the data.</param>
39+ /// <returns>A new <see cref="BlobResourceContents"/> instance.</returns>
40+ /// <exception cref="InvalidOperationException"></exception>
41+ public static BlobResourceContents FromBytes ( ReadOnlyMemory < byte > bytes , string uri , string ? mimeType = null )
42+ {
43+ ReadOnlyMemory < byte > blob = EncodingUtilities . EncodeToBase64Utf8 ( bytes ) ;
44+
45+ return new ( )
46+ {
47+ _decodedData = bytes ,
48+ Blob = blob ,
49+ MimeType = mimeType ,
50+ Uri = uri
51+ } ;
52+ }
53+
2754 /// <summary>
28- /// Gets or sets the base64-encoded string representing the binary data of the item.
55+ /// Gets or sets the base64-encoded UTF-8 bytes representing the binary data of the item.
2956 /// </summary>
57+ /// <remarks>
58+ /// Setting this value will invalidate any cached value of <see cref="DecodedData"/>.
59+ /// </remarks>
3060 [ JsonPropertyName ( "blob" ) ]
31- public required string Blob { get ; set ; }
61+ public required ReadOnlyMemory < byte > Blob
62+ {
63+ get => _blob ;
64+ set
65+ {
66+ _blob = value ;
67+ _decodedData = null ; // Invalidate cache
68+ }
69+ }
70+
71+ /// <summary>
72+ /// Gets the decoded data represented by <see cref="Blob"/>.
73+ /// </summary>
74+ /// <remarks>
75+ /// <para>
76+ /// When getting, this member will decode the value in <see cref="Blob"/> and cache the result.
77+ /// Subsequent accesses return the cached value unless <see cref="Blob"/> is modified.
78+ /// </para>
79+ /// </remarks>
80+ [ JsonIgnore ]
81+ public ReadOnlyMemory < byte > DecodedData
82+ {
83+ get
84+ {
85+ if ( _decodedData is null )
86+ {
87+ _decodedData = EncodingUtilities . DecodeFromBase64Utf8 ( Blob ) ;
88+ }
89+
90+ return _decodedData . Value ;
91+ }
92+ }
3293
3394 [ DebuggerBrowsable ( DebuggerBrowsableState . Never ) ]
3495 private string DebuggerDisplay
3596 {
3697 get
3798 {
38- string lengthDisplay = DebuggerDisplayHelper . GetBase64LengthDisplay ( Blob ) ;
99+ string lengthDisplay = _decodedData is null ? DebuggerDisplayHelper . GetBase64LengthDisplay ( Blob ) : $ " { DecodedData . Length } bytes" ;
39100 string mimeInfo = MimeType is not null ? $ ", MimeType = { MimeType } " : "" ;
40101 return $ "Uri = \" { Uri } \" { mimeInfo } , Length = { lengthDisplay } ";
41102 }
0 commit comments