Overhaul of Resources (#125)
* Supporting resource templates with read resource callback Fixes #123 * Aligning the resource data structures with the 2024-11-05 schema - Introducing separate classes for Text and Blob ResourceContents - Custom JSON converter to allow those types to be returned, making it easier to follow the type system against the spec - Updated tests * Apply suggestions from code review Co-authored-by: Stephen Toub <stoub@microsoft.com> Co-authored-by: Peder Holdgaard Pedersen <127606677+PederHP@users.noreply.github.com> * Putting files back where they belong * Adding an internal constructor to prevent overloads * Addressing feedback * Apply suggestions from code review Co-authored-by: Stephen Toub <stoub@microsoft.com> * Update src/ModelContextProtocol/Protocol/Types/ResourceContents.cs * Addressing feedback --------- Co-authored-by: Stephen Toub <stoub@microsoft.com> Co-authored-by: Peder Holdgaard Pedersen <127606677+PederHP@users.noreply.github.com>
This commit is contained in:
parent
2ab9db0878
commit
3c0679e92b
4 changed files with 290 additions and 49 deletions
|
|
@ -0,0 +1,16 @@
|
|||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace ModelContextProtocol.Protocol.Types;
|
||||
|
||||
/// <summary>
|
||||
/// Binary contents of a resource.
|
||||
/// <see href="https://github.com/modelcontextprotocol/specification/blob/main/schema/">See the schema for details</see>
|
||||
/// </summary>
|
||||
public class BlobResourceContents : ResourceContents
|
||||
{
|
||||
/// <summary>
|
||||
/// The base64-encoded string representing the binary data of the item.
|
||||
/// </summary>
|
||||
[JsonPropertyName("blob")]
|
||||
public string Blob { get; set; } = string.Empty;
|
||||
}
|
||||
|
|
@ -16,12 +16,16 @@ public record Resource
|
|||
|
||||
/// <summary>
|
||||
/// A human-readable name for this resource.
|
||||
///
|
||||
/// This can be used by clients to populate UI elements.
|
||||
/// </summary>
|
||||
[JsonPropertyName("name")]
|
||||
public required string Name { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// A description of what this resource represents.
|
||||
///
|
||||
/// This can be used by clients to improve the LLM's understanding of available resources. It can be thought of like a \"hint\" to the model.
|
||||
/// </summary>
|
||||
[JsonPropertyName("description")]
|
||||
public string? Description { get; init; }
|
||||
|
|
@ -32,6 +36,14 @@ public record Resource
|
|||
[JsonPropertyName("mimeType")]
|
||||
public string? MimeType { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// The size of the raw resource content, in bytes (i.e., before base64 encoding or any tokenization), if known.
|
||||
///
|
||||
/// This can be used by Hosts to display file sizes and estimate context window usage.
|
||||
/// </summary>
|
||||
[JsonPropertyName("size")]
|
||||
public long? Size { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Optional annotations for the resource.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,7 @@
|
|||
using System.Text.Json.Serialization;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace ModelContextProtocol.Protocol.Types;
|
||||
|
||||
|
|
@ -6,8 +9,13 @@ namespace ModelContextProtocol.Protocol.Types;
|
|||
/// Represents the content of a resource.
|
||||
/// <see href="https://github.com/modelcontextprotocol/specification/blob/main/schema/">See the schema for details</see>
|
||||
/// </summary>
|
||||
public class ResourceContents
|
||||
[JsonConverter(typeof(Converter))]
|
||||
public abstract class ResourceContents
|
||||
{
|
||||
internal ResourceContents()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The URI of the resource.
|
||||
/// </summary>
|
||||
|
|
@ -20,16 +28,104 @@ public class ResourceContents
|
|||
[JsonPropertyName("mimeType")]
|
||||
public string? MimeType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The text content of the resource.
|
||||
/// </summary>
|
||||
[JsonPropertyName("text")]
|
||||
public string? Text { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The base64-encoded binary content of the resource.
|
||||
/// Converter for <see cref="ResourceContents"/>.
|
||||
/// </summary>
|
||||
[JsonPropertyName("blob")]
|
||||
public string? Blob { get; set; }
|
||||
}
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public class Converter : JsonConverter<ResourceContents>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override ResourceContents? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
if (reader.TokenType == JsonTokenType.Null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (reader.TokenType != JsonTokenType.StartObject)
|
||||
{
|
||||
throw new JsonException();
|
||||
}
|
||||
|
||||
string? uri = null;
|
||||
string? mimeType = null;
|
||||
string? blob = null;
|
||||
string? text = null;
|
||||
|
||||
while (reader.Read() && reader.TokenType != JsonTokenType.EndObject)
|
||||
{
|
||||
if (reader.TokenType != JsonTokenType.PropertyName)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
string? propertyName = reader.GetString();
|
||||
|
||||
switch (propertyName)
|
||||
{
|
||||
case "uri":
|
||||
uri = reader.GetString();
|
||||
break;
|
||||
case "mimeType":
|
||||
mimeType = reader.GetString();
|
||||
break;
|
||||
case "blob":
|
||||
blob = reader.GetString();
|
||||
break;
|
||||
case "text":
|
||||
text = reader.GetString();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (blob is not null)
|
||||
{
|
||||
return new BlobResourceContents
|
||||
{
|
||||
Uri = uri ?? string.Empty,
|
||||
MimeType = mimeType,
|
||||
Blob = blob
|
||||
};
|
||||
}
|
||||
|
||||
if (text is not null)
|
||||
{
|
||||
return new TextResourceContents
|
||||
{
|
||||
Uri = uri ?? string.Empty,
|
||||
MimeType = mimeType,
|
||||
Text = text
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Write(Utf8JsonWriter writer, ResourceContents value, JsonSerializerOptions options)
|
||||
{
|
||||
if (value is null)
|
||||
{
|
||||
writer.WriteNullValue();
|
||||
return;
|
||||
}
|
||||
|
||||
writer.WriteStartObject();
|
||||
writer.WriteString("uri", value.Uri);
|
||||
writer.WriteString("mimeType", value.MimeType);
|
||||
Debug.Assert(value is BlobResourceContents or TextResourceContents);
|
||||
if (value is BlobResourceContents blobResource)
|
||||
{
|
||||
writer.WriteString("blob", blobResource.Blob);
|
||||
}
|
||||
else if (value is TextResourceContents textResource)
|
||||
{
|
||||
writer.WriteString("text", textResource.Text);
|
||||
}
|
||||
writer.WriteEndObject();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace ModelContextProtocol.Protocol.Types;
|
||||
|
||||
/// <summary>
|
||||
/// Text contents of a resource.
|
||||
/// <see href="https://github.com/modelcontextprotocol/specification/blob/main/schema/">See the schema for details</see>
|
||||
/// </summary>
|
||||
public class TextResourceContents : ResourceContents
|
||||
{
|
||||
/// <summary>
|
||||
/// The text of the item. This must only be set if the item can actually be represented as text (not binary data).
|
||||
/// </summary>
|
||||
[JsonPropertyName("text")]
|
||||
public string Text { get; set; } = string.Empty;
|
||||
}
|
||||
Loading…
Reference in a new issue