Add tools support to sampling (#976)
This commit is contained in:
parent
ad86b53347
commit
15139cc569
10 changed files with 1623 additions and 425 deletions
|
|
@ -1,4 +1,3 @@
|
|||
using Microsoft.Extensions.AI;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
|
@ -37,7 +36,7 @@ public abstract class ContentBlock
|
|||
/// When overridden in a derived class, gets the type of content.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This determines the structure of the content object. Valid values include "image", "audio", "text", "resource", and "resource_link".
|
||||
/// This determines the structure of the content object. Valid values include "image", "audio", "text", "resource", "resource_link", "tool_use", and "tool_result".
|
||||
/// </remarks>
|
||||
[JsonPropertyName("type")]
|
||||
public abstract string Type { get; }
|
||||
|
|
@ -52,6 +51,15 @@ public abstract class ContentBlock
|
|||
[JsonPropertyName("annotations")]
|
||||
public Annotations? Annotations { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets metadata reserved by MCP for protocol-level metadata.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Implementations must not make assumptions about its contents.
|
||||
/// </remarks>
|
||||
[JsonPropertyName("_meta")]
|
||||
public JsonObject? Meta { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Provides a <see cref="JsonConverter"/> for <see cref="ContentBlock"/>.
|
||||
/// </summary>
|
||||
|
|
@ -84,6 +92,12 @@ public abstract class ContentBlock
|
|||
ResourceContents? resource = null;
|
||||
Annotations? annotations = null;
|
||||
JsonObject? meta = null;
|
||||
string? id = null;
|
||||
JsonElement? input = null;
|
||||
string? toolUseId = null;
|
||||
List<ContentBlock>? content = null;
|
||||
JsonElement? structuredContent = null;
|
||||
bool? isError = null;
|
||||
|
||||
while (reader.Read() && reader.TokenType != JsonTokenType.EndObject)
|
||||
{
|
||||
|
|
@ -142,42 +156,71 @@ public abstract class ContentBlock
|
|||
meta = JsonSerializer.Deserialize(ref reader, McpJsonUtilities.JsonContext.Default.JsonObject);
|
||||
break;
|
||||
|
||||
case "id":
|
||||
id = reader.GetString();
|
||||
break;
|
||||
|
||||
case "input":
|
||||
input = JsonSerializer.Deserialize(ref reader, McpJsonUtilities.JsonContext.Default.JsonElement);
|
||||
break;
|
||||
|
||||
case "toolUseId":
|
||||
toolUseId = reader.GetString();
|
||||
break;
|
||||
|
||||
case "content":
|
||||
if (reader.TokenType == JsonTokenType.StartArray)
|
||||
{
|
||||
content = [];
|
||||
while (reader.Read() && reader.TokenType != JsonTokenType.EndArray)
|
||||
{
|
||||
content.Add(Read(ref reader, typeof(ContentBlock), options) ??
|
||||
throw new JsonException("Unexpected null item in content array."));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
content = [Read(ref reader, typeof(ContentBlock), options) ??
|
||||
throw new JsonException("Unexpected null content item.")];
|
||||
}
|
||||
break;
|
||||
|
||||
case "structuredContent":
|
||||
structuredContent = JsonSerializer.Deserialize(ref reader, McpJsonUtilities.JsonContext.Default.JsonElement);
|
||||
break;
|
||||
|
||||
case "isError":
|
||||
isError = reader.GetBoolean();
|
||||
break;
|
||||
|
||||
default:
|
||||
reader.Skip();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return type switch
|
||||
ContentBlock block = type switch
|
||||
{
|
||||
"text" => new TextContentBlock
|
||||
{
|
||||
Text = text ?? throw new JsonException("Text contents must be provided for 'text' type."),
|
||||
Annotations = annotations,
|
||||
Meta = meta,
|
||||
},
|
||||
|
||||
"image" => new ImageContentBlock
|
||||
{
|
||||
Data = data ?? throw new JsonException("Image data must be provided for 'image' type."),
|
||||
MimeType = mimeType ?? throw new JsonException("MIME type must be provided for 'image' type."),
|
||||
Annotations = annotations,
|
||||
Meta = meta,
|
||||
},
|
||||
|
||||
"audio" => new AudioContentBlock
|
||||
{
|
||||
Data = data ?? throw new JsonException("Audio data must be provided for 'audio' type."),
|
||||
MimeType = mimeType ?? throw new JsonException("MIME type must be provided for 'audio' type."),
|
||||
Annotations = annotations,
|
||||
Meta = meta,
|
||||
},
|
||||
|
||||
"resource" => new EmbeddedResourceBlock
|
||||
{
|
||||
Resource = resource ?? throw new JsonException("Resource contents must be provided for 'resource' type."),
|
||||
Annotations = annotations,
|
||||
Meta = meta,
|
||||
},
|
||||
|
||||
"resource_link" => new ResourceLinkBlock
|
||||
|
|
@ -187,11 +230,30 @@ public abstract class ContentBlock
|
|||
Description = description,
|
||||
MimeType = mimeType,
|
||||
Size = size,
|
||||
Annotations = annotations,
|
||||
},
|
||||
|
||||
"tool_use" => new ToolUseContentBlock
|
||||
{
|
||||
Id = id ?? throw new JsonException("ID must be provided for 'tool_use' type."),
|
||||
Name = name ?? throw new JsonException("Name must be provided for 'tool_use' type."),
|
||||
Input = input ?? throw new JsonException("Input must be provided for 'tool_use' type."),
|
||||
},
|
||||
|
||||
"tool_result" => new ToolResultContentBlock
|
||||
{
|
||||
ToolUseId = toolUseId ?? throw new JsonException("ToolUseId must be provided for 'tool_result' type."),
|
||||
Content = content ?? throw new JsonException("Content must be provided for 'tool_result' type."),
|
||||
StructuredContent = structuredContent,
|
||||
IsError = isError,
|
||||
},
|
||||
|
||||
_ => throw new JsonException($"Unknown content type: '{type}'"),
|
||||
};
|
||||
|
||||
block.Annotations = annotations;
|
||||
block.Meta = meta;
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
|
|
@ -211,41 +273,21 @@ public abstract class ContentBlock
|
|||
{
|
||||
case TextContentBlock textContent:
|
||||
writer.WriteString("text", textContent.Text);
|
||||
if (textContent.Meta is not null)
|
||||
{
|
||||
writer.WritePropertyName("_meta");
|
||||
JsonSerializer.Serialize(writer, textContent.Meta, McpJsonUtilities.JsonContext.Default.JsonObject);
|
||||
}
|
||||
break;
|
||||
|
||||
case ImageContentBlock imageContent:
|
||||
writer.WriteString("data", imageContent.Data);
|
||||
writer.WriteString("mimeType", imageContent.MimeType);
|
||||
if (imageContent.Meta is not null)
|
||||
{
|
||||
writer.WritePropertyName("_meta");
|
||||
JsonSerializer.Serialize(writer, imageContent.Meta, McpJsonUtilities.JsonContext.Default.JsonObject);
|
||||
}
|
||||
break;
|
||||
|
||||
case AudioContentBlock audioContent:
|
||||
writer.WriteString("data", audioContent.Data);
|
||||
writer.WriteString("mimeType", audioContent.MimeType);
|
||||
if (audioContent.Meta is not null)
|
||||
{
|
||||
writer.WritePropertyName("_meta");
|
||||
JsonSerializer.Serialize(writer, audioContent.Meta, McpJsonUtilities.JsonContext.Default.JsonObject);
|
||||
}
|
||||
break;
|
||||
|
||||
case EmbeddedResourceBlock embeddedResource:
|
||||
writer.WritePropertyName("resource");
|
||||
JsonSerializer.Serialize(writer, embeddedResource.Resource, McpJsonUtilities.JsonContext.Default.ResourceContents);
|
||||
if (embeddedResource.Meta is not null)
|
||||
{
|
||||
writer.WritePropertyName("_meta");
|
||||
JsonSerializer.Serialize(writer, embeddedResource.Meta, McpJsonUtilities.JsonContext.Default.JsonObject);
|
||||
}
|
||||
break;
|
||||
|
||||
case ResourceLinkBlock resourceLink:
|
||||
|
|
@ -264,6 +306,33 @@ public abstract class ContentBlock
|
|||
writer.WriteNumber("size", resourceLink.Size.Value);
|
||||
}
|
||||
break;
|
||||
|
||||
case ToolUseContentBlock toolUse:
|
||||
writer.WriteString("id", toolUse.Id);
|
||||
writer.WriteString("name", toolUse.Name);
|
||||
writer.WritePropertyName("input");
|
||||
JsonSerializer.Serialize(writer, toolUse.Input, McpJsonUtilities.JsonContext.Default.JsonElement);
|
||||
break;
|
||||
|
||||
case ToolResultContentBlock toolResult:
|
||||
writer.WriteString("toolUseId", toolResult.ToolUseId);
|
||||
writer.WritePropertyName("content");
|
||||
writer.WriteStartArray();
|
||||
foreach (var item in toolResult.Content)
|
||||
{
|
||||
Write(writer, item, options);
|
||||
}
|
||||
writer.WriteEndArray();
|
||||
if (toolResult.StructuredContent.HasValue)
|
||||
{
|
||||
writer.WritePropertyName("structuredContent");
|
||||
JsonSerializer.Serialize(writer, toolResult.StructuredContent.Value, McpJsonUtilities.JsonContext.Default.JsonElement);
|
||||
}
|
||||
if (toolResult.IsError.HasValue)
|
||||
{
|
||||
writer.WriteBoolean("isError", toolResult.IsError.Value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (value.Annotations is { } annotations)
|
||||
|
|
@ -272,6 +341,12 @@ public abstract class ContentBlock
|
|||
JsonSerializer.Serialize(writer, annotations, McpJsonUtilities.JsonContext.Default.Annotations);
|
||||
}
|
||||
|
||||
if (value.Meta is not null)
|
||||
{
|
||||
writer.WritePropertyName("_meta");
|
||||
JsonSerializer.Serialize(writer, value.Meta, McpJsonUtilities.JsonContext.Default.JsonObject);
|
||||
}
|
||||
|
||||
writer.WriteEndObject();
|
||||
}
|
||||
}
|
||||
|
|
@ -288,15 +363,6 @@ public sealed class TextContentBlock : ContentBlock
|
|||
/// </summary>
|
||||
[JsonPropertyName("text")]
|
||||
public required string Text { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets metadata reserved by MCP for protocol-level metadata.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Implementations must not make assumptions about its contents.
|
||||
/// </remarks>
|
||||
[JsonPropertyName("_meta")]
|
||||
public JsonObject? Meta { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>Represents an image provided to or from an LLM.</summary>
|
||||
|
|
@ -321,15 +387,6 @@ public sealed class ImageContentBlock : ContentBlock
|
|||
/// </remarks>
|
||||
[JsonPropertyName("mimeType")]
|
||||
public required string MimeType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets metadata reserved by MCP for protocol-level metadata.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Implementations must not make assumptions about its contents.
|
||||
/// </remarks>
|
||||
[JsonPropertyName("_meta")]
|
||||
public JsonObject? Meta { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>Represents audio provided to or from an LLM.</summary>
|
||||
|
|
@ -354,15 +411,6 @@ public sealed class AudioContentBlock : ContentBlock
|
|||
/// </remarks>
|
||||
[JsonPropertyName("mimeType")]
|
||||
public required string MimeType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets metadata reserved by MCP for protocol-level metadata.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Implementations must not make assumptions about its contents.
|
||||
/// </remarks>
|
||||
[JsonPropertyName("_meta")]
|
||||
public JsonObject? Meta { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>Represents the contents of a resource, embedded into a prompt or tool call result.</summary>
|
||||
|
|
@ -386,15 +434,6 @@ public sealed class EmbeddedResourceBlock : ContentBlock
|
|||
/// </remarks>
|
||||
[JsonPropertyName("resource")]
|
||||
public required ResourceContents Resource { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets metadata reserved by MCP for protocol-level metadata.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Implementations must not make assumptions about its contents.
|
||||
/// </remarks>
|
||||
[JsonPropertyName("_meta")]
|
||||
public JsonObject? Meta { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>Represents a resource that the server is capable of reading, included in a prompt or tool call result.</summary>
|
||||
|
|
@ -463,3 +502,76 @@ public sealed class ResourceLinkBlock : ContentBlock
|
|||
[JsonPropertyName("size")]
|
||||
public long? Size { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>Represents a request from the assistant to call a tool.</summary>
|
||||
public sealed class ToolUseContentBlock : ContentBlock
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override string Type => "tool_use";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a unique identifier for this tool use.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This ID is used to match tool results to their corresponding tool uses.
|
||||
/// </remarks>
|
||||
[JsonPropertyName("id")]
|
||||
public required string Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name of the tool to call.
|
||||
/// </summary>
|
||||
[JsonPropertyName("name")]
|
||||
public required string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the arguments to pass to the tool, conforming to the tool's input schema.
|
||||
/// </summary>
|
||||
[JsonPropertyName("input")]
|
||||
public required JsonElement Input { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>Represents the result of a tool use, provided by the user back to the assistant.</summary>
|
||||
public sealed class ToolResultContentBlock : ContentBlock
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override string Type => "tool_result";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the ID of the tool use this result corresponds to.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This must match the ID from a previous <see cref="ToolUseContentBlock"/>.
|
||||
/// </remarks>
|
||||
[JsonPropertyName("toolUseId")]
|
||||
public required string ToolUseId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the unstructured result content of the tool use.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This has the same format as CallToolResult.Content and can include text, images,
|
||||
/// audio, resource links, and embedded resources.
|
||||
/// </remarks>
|
||||
[JsonPropertyName("content")]
|
||||
public required List<ContentBlock> Content { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets an optional structured result object.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If the tool defined an outputSchema, this should conform to that schema.
|
||||
/// </remarks>
|
||||
[JsonPropertyName("structuredContent")]
|
||||
public JsonElement? StructuredContent { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the tool use resulted in an error.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If true, the content typically describes the error that occurred.
|
||||
/// Default: false
|
||||
/// </remarks>
|
||||
[JsonPropertyName("isError")]
|
||||
public bool? IsError { get; set; }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,14 @@ namespace ModelContextProtocol.Protocol;
|
|||
/// Specifies the context inclusion options for a request in the Model Context Protocol (MCP).
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// See the <see href="https://github.com/modelcontextprotocol/specification/blob/main/schema/">schema</see> for details.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// <see cref="ContextInclusion"/>, and in particular <see cref="ThisServer"/> and <see cref="AllServers"/>, are deprecated.
|
||||
/// Servers should only use these values if the client declares <see cref="ClientCapabilities.Sampling"/> with
|
||||
/// <see cref="SamplingCapability.Context"/> set. These values may be removed in future spec releases.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
[JsonConverter(typeof(JsonStringEnumConverter<ContextInclusion>))]
|
||||
public enum ContextInclusion
|
||||
|
|
@ -20,12 +27,20 @@ public enum ContextInclusion
|
|||
/// <summary>
|
||||
/// Indicates that context from the server that sent the request should be included.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This value is soft-deprecated. Servers should only use this value if the client
|
||||
/// declares ClientCapabilities.Sampling.Context.
|
||||
/// </remarks>
|
||||
[JsonStringEnumMemberName("thisServer")]
|
||||
ThisServer,
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that context from all servers that the client is connected to should be included.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This value is soft-deprecated. Servers should only use this value if the client
|
||||
/// declares ClientCapabilities.Sampling.Context.
|
||||
/// </remarks>
|
||||
[JsonStringEnumMemberName("allServers")]
|
||||
AllServers
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,15 @@ public sealed class CreateMessageRequestParams : RequestParams
|
|||
/// Gets or sets an indication as to which server contexts should be included in the prompt.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// The client may ignore this request.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// <see cref="ContextInclusion"/>, and in particular <see cref="ContextInclusion.ThisServer"/> and
|
||||
/// <see cref="ContextInclusion.AllServers"/>, are deprecated. Servers should only use these values if the client
|
||||
/// declares <see cref="ClientCapabilities.Sampling"/> with <see cref="SamplingCapability.Context"/> set.
|
||||
/// These values may be removed in future spec releases.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
[JsonPropertyName("includeContext")]
|
||||
public ContextInclusion? IncludeContext { get; set; }
|
||||
|
|
@ -100,4 +108,16 @@ public sealed class CreateMessageRequestParams : RequestParams
|
|||
/// </summary>
|
||||
[JsonPropertyName("temperature")]
|
||||
public float? Temperature { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets tools that the model may use during generation.
|
||||
/// </summary>
|
||||
[JsonPropertyName("tools")]
|
||||
public IList<Tool>? Tools { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets controls for how the model uses tools.
|
||||
/// </summary>
|
||||
[JsonPropertyName("toolChoice")]
|
||||
public ToolChoice? ToolChoice { get; set; }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,10 +11,14 @@ namespace ModelContextProtocol.Protocol;
|
|||
public sealed class CreateMessageResult : Result
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the content of the message.
|
||||
/// Gets or sets the content of the assistant's response.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// In the corresponding JSON, this may be a single content block or an array of content blocks.
|
||||
/// </remarks>
|
||||
[JsonPropertyName("content")]
|
||||
public required ContentBlock Content { get; set; }
|
||||
[JsonConverter(typeof(SingleItemOrListConverter<ContentBlock>))]
|
||||
public required IList<ContentBlock> Content { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name of the model that generated the message.
|
||||
|
|
@ -35,12 +39,14 @@ public sealed class CreateMessageResult : Result
|
|||
/// Gets or sets the reason why message generation (sampling) stopped, if known.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Common values include:
|
||||
/// Standard values include:
|
||||
/// <list type="bullet">
|
||||
/// <item><term>endTurn</term><description>The model naturally completed its response.</description></item>
|
||||
/// <item><term>maxTokens</term><description>The response was truncated due to reaching token limits.</description></item>
|
||||
/// <item><term>stopSequence</term><description>A specific stop sequence was encountered during generation.</description></item>
|
||||
/// <item><term>toolUse</term><description>The model wants to use one or more tools.</description></item>
|
||||
/// </list>
|
||||
/// This field is an open string to allow for provider-specific stop reasons.
|
||||
/// </remarks>
|
||||
[JsonPropertyName("stopReason")]
|
||||
public string? StopReason { get; set; }
|
||||
|
|
@ -49,5 +55,17 @@ public sealed class CreateMessageResult : Result
|
|||
/// Gets or sets the role of the user who generated the message.
|
||||
/// </summary>
|
||||
[JsonPropertyName("role")]
|
||||
public required Role Role { get; set; }
|
||||
public Role Role { get; set; } = Role.Assistant;
|
||||
|
||||
/// <summary>The stop reason "endTurn".</summary>
|
||||
internal const string StopReasonEndTurn = "endTurn";
|
||||
|
||||
/// <summary>The stop reason "maxTokens".</summary>
|
||||
internal const string StopReasonMaxTokens = "maxTokens";
|
||||
|
||||
/// <summary>The stop reason "stopSequence".</summary>
|
||||
internal const string StopReasonStopSequence = "stopSequence";
|
||||
|
||||
/// <summary>The stop reason "toolUse".</summary>
|
||||
internal const string StopReasonToolUse = "toolUse";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,11 +16,6 @@ namespace ModelContextProtocol.Protocol;
|
|||
/// When this capability is enabled, an MCP server can request the client to generate content
|
||||
/// using an AI model. The client must set a <see cref="McpClientHandlers.SamplingHandler"/> to process these requests.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// This class is intentionally empty as the Model Context Protocol specification does not
|
||||
/// currently define additional properties for sampling capabilities. Future versions of the
|
||||
/// specification may extend this capability with additional configuration options.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public sealed class SamplingCapability
|
||||
{
|
||||
|
|
@ -38,7 +33,7 @@ public sealed class SamplingCapability
|
|||
/// generated content.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// You can create a handler using the <see cref="McpClient.CreateSamplingHandler"/> extension
|
||||
/// You can create a handler using the <see cref="AIContentExtensions.CreateSamplingHandler"/> extension
|
||||
/// method with any implementation of <see cref="IChatClient"/>.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
|
|
@ -46,4 +41,20 @@ public sealed class SamplingCapability
|
|||
[Obsolete($"Use {nameof(McpClientOptions.Handlers.SamplingHandler)} instead. This member will be removed in a subsequent release.")] // See: https://github.com/modelcontextprotocol/csharp-sdk/issues/774
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public Func<CreateMessageRequestParams?, IProgress<ProgressNotificationValue>, CancellationToken, ValueTask<CreateMessageResult>>? SamplingHandler { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the client supports context inclusion via includeContext parameter.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If not declared, servers should only use includeContext: "none".
|
||||
/// </remarks>
|
||||
[JsonPropertyName("context")]
|
||||
public SamplingContextCapability? Context { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the client supports tool use via tools and toolChoice parameters.
|
||||
/// </summary>
|
||||
[JsonPropertyName("tools")]
|
||||
public SamplingToolsCapability? Tools { get; set; }
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
namespace ModelContextProtocol.Protocol;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the sampling context capability.
|
||||
/// </summary>
|
||||
public sealed class SamplingContextCapability;
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
using System.Text.Json.Nodes;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace ModelContextProtocol.Protocol;
|
||||
|
|
@ -8,7 +9,8 @@ namespace ModelContextProtocol.Protocol;
|
|||
/// <remarks>
|
||||
/// <para>
|
||||
/// A <see cref="SamplingMessage"/> encapsulates content sent to or received from AI models in the Model Context Protocol.
|
||||
/// Each message has a specific role (<see cref="Role.User"/> or <see cref="Role.Assistant"/>) and contains content which can be text or images.
|
||||
/// The message has a role (<see cref="Role.User"/> or <see cref="Role.Assistant"/>) and content which can be text, images,
|
||||
/// audio, tool uses, or tool results.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// <see cref="SamplingMessage"/> objects are typically used in collections within <see cref="CreateMessageRequestParams"/>
|
||||
|
|
@ -16,8 +18,9 @@ namespace ModelContextProtocol.Protocol;
|
|||
/// within the Model Context Protocol.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// While similar to <see cref="PromptMessage"/>, the <see cref="SamplingMessage"/> is focused on direct LLM sampling
|
||||
/// operations rather than the enhanced resource embedding capabilities provided by <see cref="PromptMessage"/>.
|
||||
/// If content contains any <see cref="ToolResultContentBlock"/>, then all content items
|
||||
/// must be <see cref="ToolResultContentBlock"/>. Tool results cannot be mixed with text, image, or
|
||||
/// audio content in the same message.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// See the <see href="https://github.com/modelcontextprotocol/specification/blob/main/schema/">schema</see> for details.
|
||||
|
|
@ -29,11 +32,21 @@ public sealed class SamplingMessage
|
|||
/// Gets or sets the content of the message.
|
||||
/// </summary>
|
||||
[JsonPropertyName("content")]
|
||||
public required ContentBlock Content { get; set; }
|
||||
[JsonConverter(typeof(SingleItemOrListConverter<ContentBlock>))]
|
||||
public required IList<ContentBlock> Content { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the role of the message sender, indicating whether it's from a "user" or an "assistant".
|
||||
/// Gets or sets the role of the message sender.
|
||||
/// </summary>
|
||||
[JsonPropertyName("role")]
|
||||
public required Role Role { get; set; }
|
||||
public Role Role { get; set; } = Role.User;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets metadata reserved by MCP for protocol-level metadata.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Implementations must not make assumptions about its contents.
|
||||
/// </remarks>
|
||||
[JsonPropertyName("_meta")]
|
||||
public JsonObject? Meta { get; set; }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
namespace ModelContextProtocol.Protocol;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the sampling tools capability.
|
||||
/// </summary>
|
||||
public sealed class SamplingToolsCapability;
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
using System.ComponentModel;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace ModelContextProtocol.Protocol;
|
||||
|
||||
/// <summary>
|
||||
/// JSON converter for <see cref="IList{T}"/> that handles both array and single object representations.
|
||||
/// </summary>
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public sealed class SingleItemOrListConverter<T> : JsonConverter<IList<T>>
|
||||
where T : class
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override IList<T>? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
if (reader.TokenType == JsonTokenType.Null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (reader.TokenType == JsonTokenType.StartArray)
|
||||
{
|
||||
List<T> list = [];
|
||||
while (reader.Read() && reader.TokenType != JsonTokenType.EndArray)
|
||||
{
|
||||
if (JsonSerializer.Deserialize(ref reader, options.GetTypeInfo(typeof(T))) is T item)
|
||||
{
|
||||
list.Add(item);
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
if (reader.TokenType == JsonTokenType.StartObject)
|
||||
{
|
||||
return JsonSerializer.Deserialize(ref reader, options.GetTypeInfo(typeof(T))) is T item ? [item] : [];
|
||||
}
|
||||
|
||||
throw new JsonException($"Unexpected token type: {reader.TokenType}. Expected StartArray or StartObject.");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Write(Utf8JsonWriter writer, IList<T> value, JsonSerializerOptions options)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case null:
|
||||
writer.WriteNullValue();
|
||||
return;
|
||||
|
||||
case { Count: 1 }:
|
||||
JsonSerializer.Serialize(writer, value[0], options.GetTypeInfo(typeof(object)));
|
||||
return;
|
||||
|
||||
default:
|
||||
writer.WriteStartArray();
|
||||
foreach (var item in value)
|
||||
{
|
||||
JsonSerializer.Serialize(writer, item, options.GetTypeInfo(typeof(object)));
|
||||
}
|
||||
writer.WriteEndArray();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
32
src/ModelContextProtocol.Core/Protocol/ToolChoice.cs
Normal file
32
src/ModelContextProtocol.Core/Protocol/ToolChoice.cs
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace ModelContextProtocol.Protocol;
|
||||
|
||||
/// <summary>
|
||||
/// Controls tool selection behavior for sampling requests.
|
||||
/// </summary>
|
||||
public sealed class ToolChoice
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the mode controlling which tools the model can call.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <list type="bullet">
|
||||
/// <item><term>"auto"</term><description>Model decides whether to call tools (default)</description></item>
|
||||
/// <item><term>"required"</term><description>Model must call at least one tool</description></item>
|
||||
/// <item><term>"none"</term><description>Model must not call any tools</description></item>
|
||||
/// </list>
|
||||
/// </remarks>
|
||||
[JsonPropertyName("mode")]
|
||||
public string? Mode { get; set; }
|
||||
|
||||
/// <summary>The mode value "auto".</summary>
|
||||
internal const string ModeAuto = "auto";
|
||||
|
||||
/// <summary>The mode value "required".</summary>
|
||||
internal const string ModeRequired = "required";
|
||||
|
||||
/// <summary>The mode value "none".</summary>
|
||||
internal const string ModeNone = "none";
|
||||
}
|
||||
|
||||
Loading…
Reference in a new issue