Update content according to latest spec (#513)
* Update content according to latest spec * Add Title to a bunch of types * Add _meta to lots of types * Address feedback, more cleanup, fix NAOT test failure
This commit is contained in:
parent
28d65470f4
commit
2bcff315f8
158 changed files with 1700 additions and 853 deletions
|
|
@ -86,9 +86,9 @@ builder.Services
|
||||||
var @ref = @params.Ref;
|
var @ref = @params.Ref;
|
||||||
var argument = @params.Argument;
|
var argument = @params.Argument;
|
||||||
|
|
||||||
if (@ref.Type == "ref/resource")
|
if (@ref is ResourceTemplateReference rtr)
|
||||||
{
|
{
|
||||||
var resourceId = @ref.Uri?.Split("/").Last();
|
var resourceId = rtr.Uri?.Split("/").Last();
|
||||||
|
|
||||||
if (resourceId is null)
|
if (resourceId is null)
|
||||||
{
|
{
|
||||||
|
|
@ -103,7 +103,7 @@ builder.Services
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (@ref.Type == "ref/prompt")
|
if (@ref is PromptReference pr)
|
||||||
{
|
{
|
||||||
if (!exampleCompletions.TryGetValue(argument.Name, out IEnumerable<string>? value))
|
if (!exampleCompletions.TryGetValue(argument.Name, out IEnumerable<string>? value))
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -15,25 +15,22 @@ public class AnnotatedMessageTool
|
||||||
}
|
}
|
||||||
|
|
||||||
[McpServerTool(Name = "annotatedMessage"), Description("Generates an annotated message")]
|
[McpServerTool(Name = "annotatedMessage"), Description("Generates an annotated message")]
|
||||||
public static IEnumerable<Content> AnnotatedMessage(MessageType messageType, bool includeImage = true)
|
public static IEnumerable<ContentBlock> AnnotatedMessage(MessageType messageType, bool includeImage = true)
|
||||||
{
|
{
|
||||||
List<Content> contents = messageType switch
|
List<ContentBlock> contents = messageType switch
|
||||||
{
|
{
|
||||||
MessageType.Error => [new()
|
MessageType.Error => [new TextContentBlock
|
||||||
{
|
{
|
||||||
Type = "text",
|
|
||||||
Text = "Error: Operation failed",
|
Text = "Error: Operation failed",
|
||||||
Annotations = new() { Audience = [Role.User, Role.Assistant], Priority = 1.0f }
|
Annotations = new() { Audience = [Role.User, Role.Assistant], Priority = 1.0f }
|
||||||
}],
|
}],
|
||||||
MessageType.Success => [new()
|
MessageType.Success => [new TextContentBlock
|
||||||
{
|
{
|
||||||
Type = "text",
|
|
||||||
Text = "Operation completed successfully",
|
Text = "Operation completed successfully",
|
||||||
Annotations = new() { Audience = [Role.User], Priority = 0.7f }
|
Annotations = new() { Audience = [Role.User], Priority = 0.7f }
|
||||||
}],
|
}],
|
||||||
MessageType.Debug => [new()
|
MessageType.Debug => [new TextContentBlock
|
||||||
{
|
{
|
||||||
Type = "text",
|
|
||||||
Text = "Debug: Cache hit ratio 0.95, latency 150ms",
|
Text = "Debug: Cache hit ratio 0.95, latency 150ms",
|
||||||
Annotations = new() { Audience = [Role.Assistant], Priority = 0.3f }
|
Annotations = new() { Audience = [Role.Assistant], Priority = 0.3f }
|
||||||
}],
|
}],
|
||||||
|
|
@ -42,9 +39,8 @@ public class AnnotatedMessageTool
|
||||||
|
|
||||||
if (includeImage)
|
if (includeImage)
|
||||||
{
|
{
|
||||||
contents.Add(new()
|
contents.Add(new ImageContentBlock()
|
||||||
{
|
{
|
||||||
Type = "image",
|
|
||||||
Data = TinyImageTool.MCP_TINY_IMAGE.Split(",").Last(),
|
Data = TinyImageTool.MCP_TINY_IMAGE.Split(",").Last(),
|
||||||
MimeType = "image/png",
|
MimeType = "image/png",
|
||||||
Annotations = new() { Audience = [Role.User], Priority = 0.5f }
|
Annotations = new() { Audience = [Role.User], Priority = 0.5f }
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ public class LongRunningTool
|
||||||
int duration = 10,
|
int duration = 10,
|
||||||
int steps = 5)
|
int steps = 5)
|
||||||
{
|
{
|
||||||
var progressToken = context.Params?.Meta?.ProgressToken;
|
var progressToken = context.Params?.ProgressToken;
|
||||||
var stepDuration = duration / steps;
|
var stepDuration = duration / steps;
|
||||||
|
|
||||||
for (int i = 1; i <= steps + 1; i++)
|
for (int i = 1; i <= steps + 1; i++)
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ public class SampleLlmTool
|
||||||
var samplingParams = CreateRequestSamplingParams(prompt ?? string.Empty, "sampleLLM", maxTokens);
|
var samplingParams = CreateRequestSamplingParams(prompt ?? string.Empty, "sampleLLM", maxTokens);
|
||||||
var sampleResult = await server.SampleAsync(samplingParams, cancellationToken);
|
var sampleResult = await server.SampleAsync(samplingParams, cancellationToken);
|
||||||
|
|
||||||
return $"LLM sampling result: {sampleResult.Content.Text}";
|
return $"LLM sampling result: {(sampleResult.Content as TextContentBlock)?.Text}";
|
||||||
}
|
}
|
||||||
|
|
||||||
private static CreateMessageRequestParams CreateRequestSamplingParams(string context, string uri, int maxTokens = 100)
|
private static CreateMessageRequestParams CreateRequestSamplingParams(string context, string uri, int maxTokens = 100)
|
||||||
|
|
@ -27,11 +27,7 @@ public class SampleLlmTool
|
||||||
Messages = [new SamplingMessage()
|
Messages = [new SamplingMessage()
|
||||||
{
|
{
|
||||||
Role = Role.User,
|
Role = Role.User,
|
||||||
Content = new Content()
|
Content = new TextContentBlock { Text = $"Resource {uri} context: {context}" },
|
||||||
{
|
|
||||||
Type = "text",
|
|
||||||
Text = $"Resource {uri} context: {context}"
|
|
||||||
}
|
|
||||||
}],
|
}],
|
||||||
SystemPrompt = "You are a helpful test server.",
|
SystemPrompt = "You are a helpful test server.",
|
||||||
MaxTokens = maxTokens,
|
MaxTokens = maxTokens,
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ builder.Logging.AddConsole(options =>
|
||||||
|
|
||||||
builder.Services.AddSingleton(_ =>
|
builder.Services.AddSingleton(_ =>
|
||||||
{
|
{
|
||||||
var client = new HttpClient() { BaseAddress = new Uri("https://api.weather.gov") };
|
var client = new HttpClient { BaseAddress = new Uri("https://api.weather.gov") };
|
||||||
client.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue("weather-tool", "1.0"));
|
client.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue("weather-tool", "1.0"));
|
||||||
return client;
|
return client;
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ public sealed class SampleLlmTool
|
||||||
var samplingParams = CreateRequestSamplingParams(prompt ?? string.Empty, "sampleLLM", maxTokens);
|
var samplingParams = CreateRequestSamplingParams(prompt ?? string.Empty, "sampleLLM", maxTokens);
|
||||||
var sampleResult = await thisServer.SampleAsync(samplingParams, cancellationToken);
|
var sampleResult = await thisServer.SampleAsync(samplingParams, cancellationToken);
|
||||||
|
|
||||||
return $"LLM sampling result: {sampleResult.Content.Text}";
|
return $"LLM sampling result: {(sampleResult.Content as TextContentBlock)?.Text}";
|
||||||
}
|
}
|
||||||
|
|
||||||
private static CreateMessageRequestParams CreateRequestSamplingParams(string context, string uri, int maxTokens = 100)
|
private static CreateMessageRequestParams CreateRequestSamplingParams(string context, string uri, int maxTokens = 100)
|
||||||
|
|
@ -30,11 +30,7 @@ public sealed class SampleLlmTool
|
||||||
Messages = [new SamplingMessage()
|
Messages = [new SamplingMessage()
|
||||||
{
|
{
|
||||||
Role = Role.User,
|
Role = Role.User,
|
||||||
Content = new Content()
|
Content = new TextContentBlock { Text = $"Resource {uri} context: {context}" },
|
||||||
{
|
|
||||||
Type = "text",
|
|
||||||
Text = $"Resource {uri} context: {context}"
|
|
||||||
}
|
|
||||||
}],
|
}],
|
||||||
SystemPrompt = "You are a helpful test server.",
|
SystemPrompt = "You are a helpful test server.",
|
||||||
MaxTokens = maxTokens,
|
MaxTokens = maxTokens,
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ namespace System.Diagnostics.CodeAnalysis;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Indicates that the specified method requires dynamic access to code that is not referenced
|
/// Indicates that the specified method requires dynamic access to code that is not referenced
|
||||||
/// statically, for example through <see cref="System.Reflection"/>.
|
/// statically, for example through <see cref="Reflection"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// This allows tools to understand which methods are unsafe to call when removing unreferenced
|
/// This allows tools to understand which methods are unsafe to call when removing unreferenced
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,68 @@
|
||||||
|
// Licensed to the .NET Foundation under one or more agreements.
|
||||||
|
// The .NET Foundation licenses this file to you under the MIT license.
|
||||||
|
|
||||||
|
namespace System.Diagnostics.CodeAnalysis;
|
||||||
|
|
||||||
|
/// <summary>Specifies the syntax used in a string.</summary>
|
||||||
|
[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
|
||||||
|
internal sealed class StringSyntaxAttribute : Attribute
|
||||||
|
{
|
||||||
|
/// <summary>Initializes the <see cref="StringSyntaxAttribute"/> with the identifier of the syntax used.</summary>
|
||||||
|
/// <param name="syntax">The syntax identifier.</param>
|
||||||
|
public StringSyntaxAttribute(string syntax)
|
||||||
|
{
|
||||||
|
Syntax = syntax;
|
||||||
|
Arguments = Array.Empty<object?>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Initializes the <see cref="StringSyntaxAttribute"/> with the identifier of the syntax used.</summary>
|
||||||
|
/// <param name="syntax">The syntax identifier.</param>
|
||||||
|
/// <param name="arguments">Optional arguments associated with the specific syntax employed.</param>
|
||||||
|
public StringSyntaxAttribute(string syntax, params object?[] arguments)
|
||||||
|
{
|
||||||
|
Syntax = syntax;
|
||||||
|
Arguments = arguments;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Gets the identifier of the syntax used.</summary>
|
||||||
|
public string Syntax { get; }
|
||||||
|
|
||||||
|
/// <summary>Optional arguments associated with the specific syntax employed.</summary>
|
||||||
|
public object?[] Arguments { get; }
|
||||||
|
|
||||||
|
/// <summary>The syntax identifier for strings containing composite formats for string formatting.</summary>
|
||||||
|
public const string CompositeFormat = nameof(CompositeFormat);
|
||||||
|
|
||||||
|
/// <summary>The syntax identifier for strings containing date format specifiers.</summary>
|
||||||
|
public const string DateOnlyFormat = nameof(DateOnlyFormat);
|
||||||
|
|
||||||
|
/// <summary>The syntax identifier for strings containing date and time format specifiers.</summary>
|
||||||
|
public const string DateTimeFormat = nameof(DateTimeFormat);
|
||||||
|
|
||||||
|
/// <summary>The syntax identifier for strings containing <see cref="Enum"/> format specifiers.</summary>
|
||||||
|
public const string EnumFormat = nameof(EnumFormat);
|
||||||
|
|
||||||
|
/// <summary>The syntax identifier for strings containing <see cref="Guid"/> format specifiers.</summary>
|
||||||
|
public const string GuidFormat = nameof(GuidFormat);
|
||||||
|
|
||||||
|
/// <summary>The syntax identifier for strings containing JavaScript Object Notation (JSON).</summary>
|
||||||
|
public const string Json = nameof(Json);
|
||||||
|
|
||||||
|
/// <summary>The syntax identifier for strings containing numeric format specifiers.</summary>
|
||||||
|
public const string NumericFormat = nameof(NumericFormat);
|
||||||
|
|
||||||
|
/// <summary>The syntax identifier for strings containing regular expressions.</summary>
|
||||||
|
public const string Regex = nameof(Regex);
|
||||||
|
|
||||||
|
/// <summary>The syntax identifier for strings containing time format specifiers.</summary>
|
||||||
|
public const string TimeOnlyFormat = nameof(TimeOnlyFormat);
|
||||||
|
|
||||||
|
/// <summary>The syntax identifier for strings containing <see cref="TimeSpan"/> format specifiers.</summary>
|
||||||
|
public const string TimeSpanFormat = nameof(TimeSpanFormat);
|
||||||
|
|
||||||
|
/// <summary>The syntax identifier for strings containing URIs.</summary>
|
||||||
|
public const string Uri = nameof(Uri);
|
||||||
|
|
||||||
|
/// <summary>The syntax identifier for strings containing XML.</summary>
|
||||||
|
public const string Xml = nameof(Xml);
|
||||||
|
}
|
||||||
|
|
@ -29,11 +29,13 @@ public static class AIContentExtensions
|
||||||
{
|
{
|
||||||
Throw.IfNull(promptMessage);
|
Throw.IfNull(promptMessage);
|
||||||
|
|
||||||
|
AIContent? content = ToAIContent(promptMessage.Content);
|
||||||
|
|
||||||
return new()
|
return new()
|
||||||
{
|
{
|
||||||
RawRepresentation = promptMessage,
|
RawRepresentation = promptMessage,
|
||||||
Role = promptMessage.Role == Role.User ? ChatRole.User : ChatRole.Assistant,
|
Role = promptMessage.Role == Role.User ? ChatRole.User : ChatRole.Assistant,
|
||||||
Contents = [ToAIContent(promptMessage.Content)]
|
Contents = content is not null ? [content] : [],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -81,32 +83,32 @@ public static class AIContentExtensions
|
||||||
return messages;
|
return messages;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Creates a new <see cref="AIContent"/> from the content of a <see cref="Content"/>.</summary>
|
/// <summary>Creates a new <see cref="AIContent"/> from the content of a <see cref="ContentBlock"/>.</summary>
|
||||||
/// <param name="content">The <see cref="Content"/> to convert.</param>
|
/// <param name="content">The <see cref="ContentBlock"/> to convert.</param>
|
||||||
/// <returns>The created <see cref="AIContent"/>.</returns>
|
/// <returns>
|
||||||
|
/// The created <see cref="AIContent"/>. If the content can't be converted (such as when it's a resource link), <see langword="null"/> is returned.
|
||||||
|
/// </returns>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// This method converts Model Context Protocol content types to the equivalent Microsoft.Extensions.AI
|
/// This method converts Model Context Protocol content types to the equivalent Microsoft.Extensions.AI
|
||||||
/// content types, enabling seamless integration between the protocol and AI client libraries.
|
/// content types, enabling seamless integration between the protocol and AI client libraries.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public static AIContent ToAIContent(this Content content)
|
public static AIContent? ToAIContent(this ContentBlock content)
|
||||||
{
|
{
|
||||||
Throw.IfNull(content);
|
Throw.IfNull(content);
|
||||||
|
|
||||||
AIContent ac;
|
AIContent? ac = content switch
|
||||||
if (content is { Type: "image" or "audio", MimeType: not null, Data: not null })
|
|
||||||
{
|
{
|
||||||
ac = new DataContent(Convert.FromBase64String(content.Data), content.MimeType);
|
TextContentBlock textContent => new TextContent(textContent.Text),
|
||||||
}
|
ImageContentBlock imageContent => new DataContent(Convert.FromBase64String(imageContent.Data), imageContent.MimeType),
|
||||||
else if (content is { Type: "resource" } && content.Resource is { } resourceContents)
|
AudioContentBlock audioContent => new DataContent(Convert.FromBase64String(audioContent.Data), audioContent.MimeType),
|
||||||
{
|
EmbeddedResourceBlock resourceContent => resourceContent.Resource.ToAIContent(),
|
||||||
ac = resourceContents.ToAIContent();
|
_ => null,
|
||||||
}
|
};
|
||||||
else
|
|
||||||
{
|
|
||||||
ac = new TextContent(content.Text);
|
|
||||||
}
|
|
||||||
|
|
||||||
ac.RawRepresentation = content;
|
if (ac is not null)
|
||||||
|
{
|
||||||
|
ac.RawRepresentation = content;
|
||||||
|
}
|
||||||
|
|
||||||
return ac;
|
return ac;
|
||||||
}
|
}
|
||||||
|
|
@ -135,8 +137,8 @@ public static class AIContentExtensions
|
||||||
return ac;
|
return ac;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Creates a list of <see cref="AIContent"/> from a sequence of <see cref="Content"/>.</summary>
|
/// <summary>Creates a list of <see cref="AIContent"/> from a sequence of <see cref="ContentBlock"/>.</summary>
|
||||||
/// <param name="contents">The <see cref="Content"/> instances to convert.</param>
|
/// <param name="contents">The <see cref="ContentBlock"/> instances to convert.</param>
|
||||||
/// <returns>The created <see cref="AIContent"/> instances.</returns>
|
/// <returns>The created <see cref="AIContent"/> instances.</returns>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// <para>
|
/// <para>
|
||||||
|
|
@ -145,15 +147,15 @@ public static class AIContentExtensions
|
||||||
/// when processing the contents of a message or response.
|
/// when processing the contents of a message or response.
|
||||||
/// </para>
|
/// </para>
|
||||||
/// <para>
|
/// <para>
|
||||||
/// Each <see cref="Content"/> object is converted using <see cref="ToAIContent(Content)"/>,
|
/// Each <see cref="ContentBlock"/> object is converted using <see cref="ToAIContent(ContentBlock)"/>,
|
||||||
/// preserving the type-specific conversion logic for text, images, audio, and resources.
|
/// preserving the type-specific conversion logic for text, images, audio, and resources.
|
||||||
/// </para>
|
/// </para>
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public static IList<AIContent> ToAIContents(this IEnumerable<Content> contents)
|
public static IList<AIContent> ToAIContents(this IEnumerable<ContentBlock> contents)
|
||||||
{
|
{
|
||||||
Throw.IfNull(contents);
|
Throw.IfNull(contents);
|
||||||
|
|
||||||
return [.. contents.Select(ToAIContent)];
|
return [.. contents.Select(ToAIContent).OfType<AIContent>()];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Creates a list of <see cref="AIContent"/> from a sequence of <see cref="ResourceContents"/>.</summary>
|
/// <summary>Creates a list of <see cref="AIContent"/> from a sequence of <see cref="ResourceContents"/>.</summary>
|
||||||
|
|
@ -167,7 +169,7 @@ public static class AIContentExtensions
|
||||||
/// </para>
|
/// </para>
|
||||||
/// <para>
|
/// <para>
|
||||||
/// Each <see cref="ResourceContents"/> object is converted using <see cref="ToAIContent(ResourceContents)"/>,
|
/// Each <see cref="ResourceContents"/> object is converted using <see cref="ToAIContent(ResourceContents)"/>,
|
||||||
/// preserving the type-specific conversion logic: text resources become <see cref="TextContent"/> objects and
|
/// preserving the type-specific conversion logic: text resources become <see cref="TextContentBlock"/> objects and
|
||||||
/// binary resources become <see cref="DataContent"/> objects.
|
/// binary resources become <see cref="DataContent"/> objects.
|
||||||
/// </para>
|
/// </para>
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
|
|
@ -178,29 +180,38 @@ public static class AIContentExtensions
|
||||||
return [.. contents.Select(ToAIContent)];
|
return [.. contents.Select(ToAIContent)];
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static Content ToContent(this AIContent content) =>
|
internal static ContentBlock ToContent(this AIContent content) =>
|
||||||
content switch
|
content switch
|
||||||
{
|
{
|
||||||
TextContent textContent => new()
|
TextContent textContent => new TextContentBlock
|
||||||
{
|
{
|
||||||
Text = textContent.Text,
|
Text = textContent.Text,
|
||||||
Type = "text",
|
|
||||||
},
|
},
|
||||||
|
|
||||||
DataContent dataContent => new()
|
DataContent dataContent when dataContent.HasTopLevelMediaType("image") => new ImageContentBlock()
|
||||||
{
|
{
|
||||||
Data = dataContent.Base64Data.ToString(),
|
Data = dataContent.Base64Data.ToString(),
|
||||||
MimeType = dataContent.MediaType,
|
MimeType = dataContent.MediaType,
|
||||||
Type =
|
|
||||||
dataContent.HasTopLevelMediaType("image") ? "image" :
|
|
||||||
dataContent.HasTopLevelMediaType("audio") ? "audio" :
|
|
||||||
"resource",
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_ => new()
|
DataContent dataContent when dataContent.HasTopLevelMediaType("audio") => new AudioContentBlock()
|
||||||
|
{
|
||||||
|
Data = dataContent.Base64Data.ToString(),
|
||||||
|
MimeType = dataContent.MediaType,
|
||||||
|
},
|
||||||
|
|
||||||
|
DataContent dataContent => new EmbeddedResourceBlock()
|
||||||
|
{
|
||||||
|
Resource = new BlobResourceContents()
|
||||||
|
{
|
||||||
|
Blob = dataContent.Base64Data.ToString(),
|
||||||
|
MimeType = dataContent.MediaType,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_ => new TextContentBlock
|
||||||
{
|
{
|
||||||
Text = JsonSerializer.Serialize(content, McpJsonUtilities.DefaultOptions.GetTypeInfo(typeof(object))),
|
Text = JsonSerializer.Serialize(content, McpJsonUtilities.DefaultOptions.GetTypeInfo(typeof(object))),
|
||||||
Type = "text",
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using ModelContextProtocol.Protocol;
|
using ModelContextProtocol.Protocol;
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
|
|
||||||
namespace ModelContextProtocol.Client;
|
namespace ModelContextProtocol.Client;
|
||||||
|
|
@ -58,7 +57,7 @@ internal sealed partial class McpClient : McpEndpoint, IMcpClient
|
||||||
RequestMethods.SamplingCreateMessage,
|
RequestMethods.SamplingCreateMessage,
|
||||||
(request, _, cancellationToken) => samplingHandler(
|
(request, _, cancellationToken) => samplingHandler(
|
||||||
request,
|
request,
|
||||||
request?.Meta?.ProgressToken is { } token ? new TokenProgress(this, token) : NullProgress.Instance,
|
request?.ProgressToken is { } token ? new TokenProgress(this, token) : NullProgress.Instance,
|
||||||
cancellationToken),
|
cancellationToken),
|
||||||
McpJsonUtilities.JsonContext.Default.CreateMessageRequestParams,
|
McpJsonUtilities.JsonContext.Default.CreateMessageRequestParams,
|
||||||
McpJsonUtilities.JsonContext.Default.CreateMessageResult);
|
McpJsonUtilities.JsonContext.Default.CreateMessageResult);
|
||||||
|
|
@ -180,9 +179,12 @@ internal sealed partial class McpClient : McpEndpoint, IMcpClient
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send initialized notification
|
// Send initialized notification
|
||||||
await SendMessageAsync(
|
await this.SendNotificationAsync(
|
||||||
new JsonRpcNotification { Method = NotificationMethods.InitializedNotification },
|
NotificationMethods.InitializedNotification,
|
||||||
initializationCts.Token).ConfigureAwait(false);
|
new InitializedNotificationParams(),
|
||||||
|
McpJsonUtilities.JsonContext.Default.InitializedNotificationParams,
|
||||||
|
cancellationToken: initializationCts.Token).ConfigureAwait(false);
|
||||||
|
|
||||||
}
|
}
|
||||||
catch (OperationCanceledException oce) when (initializationCts.IsCancellationRequested && !cancellationToken.IsCancellationRequested)
|
catch (OperationCanceledException oce) when (initializationCts.IsCancellationRequested && !cancellationToken.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -639,11 +639,6 @@ public static class McpClientExtensions
|
||||||
Throw.IfNull(reference);
|
Throw.IfNull(reference);
|
||||||
Throw.IfNullOrWhiteSpace(argumentName);
|
Throw.IfNullOrWhiteSpace(argumentName);
|
||||||
|
|
||||||
if (!reference.Validate(out string? validationMessage))
|
|
||||||
{
|
|
||||||
throw new ArgumentException($"Invalid reference: {validationMessage}", nameof(reference));
|
|
||||||
}
|
|
||||||
|
|
||||||
return client.SendRequestAsync(
|
return client.SendRequestAsync(
|
||||||
RequestMethods.CompletionComplete,
|
RequestMethods.CompletionComplete,
|
||||||
new()
|
new()
|
||||||
|
|
@ -812,7 +807,7 @@ public static class McpClientExtensions
|
||||||
/// </param>
|
/// </param>
|
||||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
|
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
|
||||||
/// <returns>
|
/// <returns>
|
||||||
/// A task containing the <see cref="CallToolResponse"/> from the tool execution. The response includes
|
/// A task containing the <see cref="CallToolResult"/> from the tool execution. The response includes
|
||||||
/// the tool's output content, which may be structured data, text, or an error message.
|
/// the tool's output content, which may be structured data, text, or an error message.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
/// <exception cref="ArgumentNullException"><paramref name="client"/> is <see langword="null"/>.</exception>
|
/// <exception cref="ArgumentNullException"><paramref name="client"/> is <see langword="null"/>.</exception>
|
||||||
|
|
@ -829,7 +824,7 @@ public static class McpClientExtensions
|
||||||
/// });
|
/// });
|
||||||
/// </code>
|
/// </code>
|
||||||
/// </example>
|
/// </example>
|
||||||
public static ValueTask<CallToolResponse> CallToolAsync(
|
public static ValueTask<CallToolResult> CallToolAsync(
|
||||||
this IMcpClient client,
|
this IMcpClient client,
|
||||||
string toolName,
|
string toolName,
|
||||||
IReadOnlyDictionary<string, object?>? arguments = null,
|
IReadOnlyDictionary<string, object?>? arguments = null,
|
||||||
|
|
@ -855,10 +850,10 @@ public static class McpClientExtensions
|
||||||
Arguments = ToArgumentsDictionary(arguments, serializerOptions),
|
Arguments = ToArgumentsDictionary(arguments, serializerOptions),
|
||||||
},
|
},
|
||||||
McpJsonUtilities.JsonContext.Default.CallToolRequestParams,
|
McpJsonUtilities.JsonContext.Default.CallToolRequestParams,
|
||||||
McpJsonUtilities.JsonContext.Default.CallToolResponse,
|
McpJsonUtilities.JsonContext.Default.CallToolResult,
|
||||||
cancellationToken: cancellationToken);
|
cancellationToken: cancellationToken);
|
||||||
|
|
||||||
static async ValueTask<CallToolResponse> SendRequestWithProgressAsync(
|
static async ValueTask<CallToolResult> SendRequestWithProgressAsync(
|
||||||
IMcpClient client,
|
IMcpClient client,
|
||||||
string toolName,
|
string toolName,
|
||||||
IReadOnlyDictionary<string, object?>? arguments,
|
IReadOnlyDictionary<string, object?>? arguments,
|
||||||
|
|
@ -871,7 +866,7 @@ public static class McpClientExtensions
|
||||||
await using var _ = client.RegisterNotificationHandler(NotificationMethods.ProgressNotification,
|
await using var _ = client.RegisterNotificationHandler(NotificationMethods.ProgressNotification,
|
||||||
(notification, cancellationToken) =>
|
(notification, cancellationToken) =>
|
||||||
{
|
{
|
||||||
if (JsonSerializer.Deserialize(notification.Params, McpJsonUtilities.JsonContext.Default.ProgressNotification) is { } pn &&
|
if (JsonSerializer.Deserialize(notification.Params, McpJsonUtilities.JsonContext.Default.ProgressNotificationParams) is { } pn &&
|
||||||
pn.ProgressToken == progressToken)
|
pn.ProgressToken == progressToken)
|
||||||
{
|
{
|
||||||
progress.Report(pn.Progress);
|
progress.Report(pn.Progress);
|
||||||
|
|
@ -886,10 +881,10 @@ public static class McpClientExtensions
|
||||||
{
|
{
|
||||||
Name = toolName,
|
Name = toolName,
|
||||||
Arguments = ToArgumentsDictionary(arguments, serializerOptions),
|
Arguments = ToArgumentsDictionary(arguments, serializerOptions),
|
||||||
Meta = new() { ProgressToken = progressToken },
|
ProgressToken = progressToken,
|
||||||
},
|
},
|
||||||
McpJsonUtilities.JsonContext.Default.CallToolRequestParams,
|
McpJsonUtilities.JsonContext.Default.CallToolRequestParams,
|
||||||
McpJsonUtilities.JsonContext.Default.CallToolResponse,
|
McpJsonUtilities.JsonContext.Default.CallToolResult,
|
||||||
cancellationToken: cancellationToken).ConfigureAwait(false);
|
cancellationToken: cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -924,29 +919,12 @@ public static class McpClientExtensions
|
||||||
(options ??= new()).StopSequences = stopSequences.ToArray();
|
(options ??= new()).StopSequences = stopSequences.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
List<ChatMessage> messages = [];
|
List<ChatMessage> messages =
|
||||||
foreach (SamplingMessage sm in requestParams.Messages)
|
(from sm in requestParams.Messages
|
||||||
{
|
let aiContent = sm.Content.ToAIContent()
|
||||||
ChatMessage message = new()
|
where aiContent is not null
|
||||||
{
|
select new ChatMessage(sm.Role == Role.Assistant ? ChatRole.Assistant : ChatRole.User, [aiContent]))
|
||||||
Role = sm.Role == Role.User ? ChatRole.User : ChatRole.Assistant,
|
.ToList();
|
||||||
};
|
|
||||||
|
|
||||||
if (sm.Content is { Type: "text" })
|
|
||||||
{
|
|
||||||
message.Contents.Add(new TextContent(sm.Content.Text));
|
|
||||||
}
|
|
||||||
else if (sm.Content is { Type: "image" or "audio", MimeType: not null, Data: not null })
|
|
||||||
{
|
|
||||||
message.Contents.Add(new DataContent(Convert.FromBase64String(sm.Content.Data), sm.Content.MimeType));
|
|
||||||
}
|
|
||||||
else if (sm.Content is { Type: "resource", Resource: not null })
|
|
||||||
{
|
|
||||||
message.Contents.Add(sm.Content.Resource.ToAIContent());
|
|
||||||
}
|
|
||||||
|
|
||||||
messages.Add(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (messages, options);
|
return (messages, options);
|
||||||
}
|
}
|
||||||
|
|
@ -965,32 +943,21 @@ public static class McpClientExtensions
|
||||||
|
|
||||||
ChatMessage? lastMessage = chatResponse.Messages.LastOrDefault();
|
ChatMessage? lastMessage = chatResponse.Messages.LastOrDefault();
|
||||||
|
|
||||||
Content? content = null;
|
ContentBlock? content = null;
|
||||||
if (lastMessage is not null)
|
if (lastMessage is not null)
|
||||||
{
|
{
|
||||||
foreach (var lmc in lastMessage.Contents)
|
foreach (var lmc in lastMessage.Contents)
|
||||||
{
|
{
|
||||||
if (lmc is DataContent dc && (dc.HasTopLevelMediaType("image") || dc.HasTopLevelMediaType("audio")))
|
if (lmc is DataContent dc && (dc.HasTopLevelMediaType("image") || dc.HasTopLevelMediaType("audio")))
|
||||||
{
|
{
|
||||||
content = new()
|
content = dc.ToContent();
|
||||||
{
|
|
||||||
Type = dc.HasTopLevelMediaType("image") ? "image" : "audio",
|
|
||||||
MimeType = dc.MediaType,
|
|
||||||
Data = dc.Base64Data.ToString(),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
content ??= new()
|
|
||||||
{
|
|
||||||
Text = lastMessage?.Text ?? string.Empty,
|
|
||||||
Type = "text",
|
|
||||||
};
|
|
||||||
|
|
||||||
return new()
|
return new()
|
||||||
{
|
{
|
||||||
Content = content,
|
Content = content ?? new TextContentBlock { Text = lastMessage?.Text ?? string.Empty },
|
||||||
Model = chatResponse.ModelId ?? "unknown",
|
Model = chatResponse.ModelId ?? "unknown",
|
||||||
Role = lastMessage?.Role == ChatRole.User ? Role.User : Role.Assistant,
|
Role = lastMessage?.Role == ChatRole.User ? Role.User : Role.Assistant,
|
||||||
StopReason = chatResponse.FinishReason == ChatFinishReason.Length ? "maxTokens" : "endTurn",
|
StopReason = chatResponse.FinishReason == ChatFinishReason.Length ? "maxTokens" : "endTurn",
|
||||||
|
|
@ -1024,7 +991,7 @@ public static class McpClientExtensions
|
||||||
Throw.IfNull(requestParams);
|
Throw.IfNull(requestParams);
|
||||||
|
|
||||||
var (messages, options) = requestParams.ToChatClientArguments();
|
var (messages, options) = requestParams.ToChatClientArguments();
|
||||||
var progressToken = requestParams.Meta?.ProgressToken;
|
var progressToken = requestParams.ProgressToken;
|
||||||
|
|
||||||
List<ChatResponseUpdate> updates = [];
|
List<ChatResponseUpdate> updates = [];
|
||||||
await foreach (var update in chatClient.GetStreamingResponseAsync(messages, options, cancellationToken).ConfigureAwait(false))
|
await foreach (var update in chatClient.GetStreamingResponseAsync(messages, options, cancellationToken).ConfigureAwait(false))
|
||||||
|
|
@ -1107,7 +1074,7 @@ public static class McpClientExtensions
|
||||||
SetLoggingLevel(client, McpServer.ToLoggingLevel(level), cancellationToken);
|
SetLoggingLevel(client, McpServer.ToLoggingLevel(level), cancellationToken);
|
||||||
|
|
||||||
/// <summary>Convers a dictionary with <see cref="object"/> values to a dictionary with <see cref="JsonElement"/> values.</summary>
|
/// <summary>Convers a dictionary with <see cref="object"/> values to a dictionary with <see cref="JsonElement"/> values.</summary>
|
||||||
private static IReadOnlyDictionary<string, JsonElement>? ToArgumentsDictionary(
|
private static Dictionary<string, JsonElement>? ToArgumentsDictionary(
|
||||||
IReadOnlyDictionary<string, object?>? arguments, JsonSerializerOptions options)
|
IReadOnlyDictionary<string, object?>? arguments, JsonSerializerOptions options)
|
||||||
{
|
{
|
||||||
var typeInfo = options.GetTypeInfo<object?>();
|
var typeInfo = options.GetTypeInfo<object?>();
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ namespace ModelContextProtocol.Client;
|
||||||
/// These options are typically passed to <see cref="McpClientFactory.CreateAsync"/> when creating a client.
|
/// These options are typically passed to <see cref="McpClientFactory.CreateAsync"/> when creating a client.
|
||||||
/// They define client capabilities, protocol version, and other client-specific settings.
|
/// They define client capabilities, protocol version, and other client-specific settings.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public class McpClientOptions
|
public sealed class McpClientOptions
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets information about this client implementation, including its name and version.
|
/// Gets or sets information about this client implementation, including its name and version.
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,9 @@ public sealed class McpClientPrompt
|
||||||
/// <summary>Gets the name of the prompt.</summary>
|
/// <summary>Gets the name of the prompt.</summary>
|
||||||
public string Name => ProtocolPrompt.Name;
|
public string Name => ProtocolPrompt.Name;
|
||||||
|
|
||||||
|
/// <summary>Gets the title of the prompt.</summary>
|
||||||
|
public string? Title => ProtocolPrompt.Title;
|
||||||
|
|
||||||
/// <summary>Gets a description of the prompt.</summary>
|
/// <summary>Gets a description of the prompt.</summary>
|
||||||
public string? Description => ProtocolPrompt.Description;
|
public string? Description => ProtocolPrompt.Description;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,9 @@ public sealed class McpClientResource
|
||||||
/// <summary>Gets the name of the resource.</summary>
|
/// <summary>Gets the name of the resource.</summary>
|
||||||
public string Name => ProtocolResource.Name;
|
public string Name => ProtocolResource.Name;
|
||||||
|
|
||||||
|
/// <summary>Gets the title of the resource.</summary>
|
||||||
|
public string? Title => ProtocolResource.Title;
|
||||||
|
|
||||||
/// <summary>Gets a description of the resource.</summary>
|
/// <summary>Gets a description of the resource.</summary>
|
||||||
public string? Description => ProtocolResource.Description;
|
public string? Description => ProtocolResource.Description;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,9 @@ public sealed class McpClientResourceTemplate
|
||||||
/// <summary>Gets the name of the resource template.</summary>
|
/// <summary>Gets the name of the resource template.</summary>
|
||||||
public string Name => ProtocolResourceTemplate.Name;
|
public string Name => ProtocolResourceTemplate.Name;
|
||||||
|
|
||||||
|
/// <summary>Gets the title of the resource template.</summary>
|
||||||
|
public string? Title => ProtocolResourceTemplate.Title;
|
||||||
|
|
||||||
/// <summary>Gets a description of the resource template.</summary>
|
/// <summary>Gets a description of the resource template.</summary>
|
||||||
public string? Description => ProtocolResourceTemplate.Description;
|
public string? Description => ProtocolResourceTemplate.Description;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -68,6 +68,9 @@ public sealed class McpClientTool : AIFunction
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public override string Name => _name;
|
public override string Name => _name;
|
||||||
|
|
||||||
|
/// <summary>Gets the tool's title.</summary>
|
||||||
|
public string? Title => ProtocolTool.Title ?? ProtocolTool.Annotations?.Title;
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public override string Description => _description;
|
public override string Description => _description;
|
||||||
|
|
||||||
|
|
@ -84,8 +87,8 @@ public sealed class McpClientTool : AIFunction
|
||||||
protected async override ValueTask<object?> InvokeCoreAsync(
|
protected async override ValueTask<object?> InvokeCoreAsync(
|
||||||
AIFunctionArguments arguments, CancellationToken cancellationToken)
|
AIFunctionArguments arguments, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
CallToolResponse result = await CallAsync(arguments, _progress, JsonSerializerOptions, cancellationToken).ConfigureAwait(false);
|
CallToolResult result = await CallAsync(arguments, _progress, JsonSerializerOptions, cancellationToken).ConfigureAwait(false);
|
||||||
return JsonSerializer.SerializeToElement(result, McpJsonUtilities.JsonContext.Default.CallToolResponse);
|
return JsonSerializer.SerializeToElement(result, McpJsonUtilities.JsonContext.Default.CallToolResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -104,14 +107,14 @@ public sealed class McpClientTool : AIFunction
|
||||||
/// </param>
|
/// </param>
|
||||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
|
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
|
||||||
/// <returns>
|
/// <returns>
|
||||||
/// A task containing the <see cref="CallToolResponse"/> from the tool execution. The response includes
|
/// A task containing the <see cref="CallToolResult"/> from the tool execution. The response includes
|
||||||
/// the tool's output content, which may be structured data, text, or an error message.
|
/// the tool's output content, which may be structured data, text, or an error message.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// The base <see cref="AIFunction.InvokeAsync"/> method is overridden to invoke this <see cref="CallAsync"/> method.
|
/// The base <see cref="AIFunction.InvokeAsync"/> method is overridden to invoke this <see cref="CallAsync"/> method.
|
||||||
/// The only difference in behavior is <see cref="AIFunction.InvokeAsync"/> will serialize the resulting <see cref="CallToolResponse"/>"/>
|
/// The only difference in behavior is <see cref="AIFunction.InvokeAsync"/> will serialize the resulting <see cref="CallToolResult"/>"/>
|
||||||
/// such that the <see cref="object"/> returned is a <see cref="JsonElement"/> containing the serialized <see cref="CallToolResponse"/>.
|
/// such that the <see cref="object"/> returned is a <see cref="JsonElement"/> containing the serialized <see cref="CallToolResult"/>.
|
||||||
/// This <see cref="CallToolResponse"/> method is intended to be called directly by user code, whereas the base <see cref="AIFunction.InvokeAsync"/>
|
/// This <see cref="CallToolResult"/> method is intended to be called directly by user code, whereas the base <see cref="AIFunction.InvokeAsync"/>
|
||||||
/// is intended to be used polymorphically via the base class, typically as part of an <see cref="IChatClient"/> operation.
|
/// is intended to be used polymorphically via the base class, typically as part of an <see cref="IChatClient"/> operation.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
/// <exception cref="McpException">The server could not find the requested tool, or the server encountered an error while processing the request.</exception>
|
/// <exception cref="McpException">The server could not find the requested tool, or the server encountered an error while processing the request.</exception>
|
||||||
|
|
@ -124,7 +127,7 @@ public sealed class McpClientTool : AIFunction
|
||||||
/// });
|
/// });
|
||||||
/// </code>
|
/// </code>
|
||||||
/// </example>
|
/// </example>
|
||||||
public ValueTask<CallToolResponse> CallAsync(
|
public ValueTask<CallToolResult> CallAsync(
|
||||||
IReadOnlyDictionary<string, object?>? arguments = null,
|
IReadOnlyDictionary<string, object?>? arguments = null,
|
||||||
IProgress<ProgressNotificationValue>? progress = null,
|
IProgress<ProgressNotificationValue>? progress = null,
|
||||||
JsonSerializerOptions? serializerOptions = null,
|
JsonSerializerOptions? serializerOptions = null,
|
||||||
|
|
@ -156,7 +159,7 @@ public sealed class McpClientTool : AIFunction
|
||||||
/// </para>
|
/// </para>
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public McpClientTool WithName(string name) =>
|
public McpClientTool WithName(string name) =>
|
||||||
new McpClientTool(_client, ProtocolTool, JsonSerializerOptions, name, _description, _progress);
|
new(_client, ProtocolTool, JsonSerializerOptions, name, _description, _progress);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new instance of the tool but modified to return the specified description from its <see cref="Description"/> property.
|
/// Creates a new instance of the tool but modified to return the specified description from its <see cref="Description"/> property.
|
||||||
|
|
@ -180,7 +183,7 @@ public sealed class McpClientTool : AIFunction
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
/// <returns>A new instance of <see cref="McpClientTool"/> with the provided description.</returns>
|
/// <returns>A new instance of <see cref="McpClientTool"/> with the provided description.</returns>
|
||||||
public McpClientTool WithDescription(string description) =>
|
public McpClientTool WithDescription(string description) =>
|
||||||
new McpClientTool(_client, ProtocolTool, JsonSerializerOptions, _name, description, _progress);
|
new(_client, ProtocolTool, JsonSerializerOptions, _name, description, _progress);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new instance of the tool but modified to report progress via the specified <see cref="IProgress{T}"/>.
|
/// Creates a new instance of the tool but modified to report progress via the specified <see cref="IProgress{T}"/>.
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ namespace ModelContextProtocol.Client;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Provides options for configuring <see cref="SseClientTransport"/> instances.
|
/// Provides options for configuring <see cref="SseClientTransport"/> instances.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class SseClientTransportOptions
|
public sealed class SseClientTransportOptions
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the base address of the server for SSE connections.
|
/// Gets or sets the base address of the server for SSE connections.
|
||||||
|
|
@ -69,5 +69,5 @@ public class SseClientTransportOptions
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// Use this property to specify custom HTTP headers that should be sent with each request to the server.
|
/// Use this property to specify custom HTTP headers that should be sent with each request to the server.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public Dictionary<string, string>? AdditionalHeaders { get; set; }
|
public IDictionary<string, string>? AdditionalHeaders { get; set; }
|
||||||
}
|
}
|
||||||
|
|
@ -3,7 +3,7 @@ namespace ModelContextProtocol.Client;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Provides options for configuring <see cref="StdioClientTransport"/> instances.
|
/// Provides options for configuring <see cref="StdioClientTransport"/> instances.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class StdioClientTransportOptions
|
public sealed class StdioClientTransportOptions
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the command to execute to start the server process.
|
/// Gets or sets the command to execute to start the server process.
|
||||||
|
|
@ -53,7 +53,7 @@ public class StdioClientTransportOptions
|
||||||
/// That includes removing the variables for any of this collection's entries with a null value.
|
/// That includes removing the variables for any of this collection's entries with a null value.
|
||||||
/// </para>
|
/// </para>
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public Dictionary<string, string?>? EnvironmentVariables { get; set; }
|
public IDictionary<string, string?>? EnvironmentVariables { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the timeout to wait for the server to shut down gracefully.
|
/// Gets or sets the timeout to wait for the server to shut down gracefully.
|
||||||
|
|
|
||||||
|
|
@ -244,7 +244,7 @@ internal sealed partial class StreamableHttpClientSessionTransport : TransportBa
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static void CopyAdditionalHeaders(HttpRequestHeaders headers, Dictionary<string, string>? additionalHeaders, string? sessionId = null)
|
internal static void CopyAdditionalHeaders(HttpRequestHeaders headers, IDictionary<string, string>? additionalHeaders, string? sessionId = null)
|
||||||
{
|
{
|
||||||
if (sessionId is not null)
|
if (sessionId is not null)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ internal static class Diagnostics
|
||||||
internal static Meter Meter { get; } = new("Experimental.ModelContextProtocol");
|
internal static Meter Meter { get; } = new("Experimental.ModelContextProtocol");
|
||||||
|
|
||||||
internal static Histogram<double> CreateDurationHistogram(string name, string description, bool longBuckets) =>
|
internal static Histogram<double> CreateDurationHistogram(string name, string description, bool longBuckets) =>
|
||||||
Diagnostics.Meter.CreateHistogram<double>(name, "s", description
|
Meter.CreateHistogram<double>(name, "s", description
|
||||||
#if NET9_0_OR_GREATER
|
#if NET9_0_OR_GREATER
|
||||||
, advice: longBuckets ? LongSecondsBucketBoundaries : ShortSecondsBucketBoundaries
|
, advice: longBuckets ? LongSecondsBucketBoundaries : ShortSecondsBucketBoundaries
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue