Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 20 additions & 7 deletions src/ModelContextProtocol.Core/Client/McpClient.Methods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using ModelContextProtocol.Server;
using System.Runtime.CompilerServices;
using System.Text.Json;
using System.Text.Json.Nodes;

namespace ModelContextProtocol.Client;

Expand Down Expand Up @@ -491,13 +492,15 @@ public Task UnsubscribeFromResourceAsync(Uri uri, CancellationToken cancellation
/// <param name="arguments">An optional dictionary of arguments to pass to the tool.</param>
/// <param name="progress">Optional progress reporter for server notifications.</param>
/// <param name="serializerOptions">JSON serializer options.</param>
/// <param name="meta">Optional metadata to include in the request. This will be serialized as the <c>_meta</c> field in the JSON-RPC request parameters.</param>
/// <param name="cancellationToken">A cancellation token.</param>
/// <returns>The <see cref="CallToolResult"/> from the tool execution.</returns>
public ValueTask<CallToolResult> CallToolAsync(
string toolName,
IReadOnlyDictionary<string, object?>? arguments = null,
IProgress<ProgressNotificationValue>? progress = null,
JsonSerializerOptions? serializerOptions = null,
JsonObject? meta = null,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like this isn't going to conflict with @mikekistler's pending change. Let's get that one in first and then rebase this one on top of it.

CancellationToken cancellationToken = default)
{
Throw.IfNull(toolName);
Expand All @@ -506,7 +509,7 @@ public ValueTask<CallToolResult> CallToolAsync(

if (progress is not null)
{
return SendRequestWithProgressAsync(toolName, arguments, progress, serializerOptions, cancellationToken);
return SendRequestWithProgressAsync(toolName, arguments, progress, serializerOptions, meta, cancellationToken);
}

return SendRequestAsync(
Expand All @@ -515,6 +518,7 @@ public ValueTask<CallToolResult> CallToolAsync(
{
Name = toolName,
Arguments = ToArgumentsDictionary(arguments, serializerOptions),
Meta = meta,
},
McpJsonUtilities.JsonContext.Default.CallToolRequestParams,
McpJsonUtilities.JsonContext.Default.CallToolResult,
Expand All @@ -525,6 +529,7 @@ async ValueTask<CallToolResult> SendRequestWithProgressAsync(
IReadOnlyDictionary<string, object?>? arguments,
IProgress<ProgressNotificationValue> progress,
JsonSerializerOptions serializerOptions,
JsonObject? meta,
CancellationToken cancellationToken)
{
ProgressToken progressToken = new(Guid.NewGuid().ToString("N"));
Expand All @@ -541,14 +546,22 @@ async ValueTask<CallToolResult> SendRequestWithProgressAsync(
return default;
}).ConfigureAwait(false);

// Clone the meta object if provided, as we need to add the progress token to it without mutating the original
JsonObject? metaWithProgress = meta is not null ? JsonNode.Parse(meta.ToJsonString())?.AsObject() : null;

var requestParams = new CallToolRequestParams
{
Name = toolName,
Arguments = ToArgumentsDictionary(arguments, serializerOptions),
Meta = metaWithProgress,
};

// Use the ProgressToken property setter which handles creating Meta if needed
requestParams.ProgressToken = progressToken;

return await SendRequestAsync(
RequestMethods.ToolsCall,
new()
{
Name = toolName,
Arguments = ToArgumentsDictionary(arguments, serializerOptions),
ProgressToken = progressToken,
},
requestParams,
McpJsonUtilities.JsonContext.Default.CallToolRequestParams,
McpJsonUtilities.JsonContext.Default.CallToolResult,
cancellationToken: cancellationToken).ConfigureAwait(false);
Expand Down
5 changes: 4 additions & 1 deletion src/ModelContextProtocol.Core/Client/McpClientExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Text.Json;
using System.Text.Json.Nodes;

namespace ModelContextProtocol.Client;

Expand Down Expand Up @@ -556,6 +557,7 @@ public static Task UnsubscribeFromResourceAsync(this IMcpClient client, Uri uri,
/// <param name="serializerOptions">
/// The JSON serialization options governing argument serialization. If <see langword="null"/>, the default serialization options will be used.
/// </param>
/// <param name="meta">Optional metadata to include in the request. This will be serialized as the <c>_meta</c> field in the JSON-RPC request parameters.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>
/// A task containing the <see cref="CallToolResult"/> from the tool execution. The response includes
Expand Down Expand Up @@ -583,8 +585,9 @@ public static ValueTask<CallToolResult> CallToolAsync(
IReadOnlyDictionary<string, object?>? arguments = null,
IProgress<ProgressNotificationValue>? progress = null,
JsonSerializerOptions? serializerOptions = null,
JsonObject? meta = null,
CancellationToken cancellationToken = default)
=> AsClientOrThrow(client).CallToolAsync(toolName, arguments, progress, serializerOptions, cancellationToken);
=> AsClientOrThrow(client).CallToolAsync(toolName, arguments, progress, serializerOptions, meta, cancellationToken);

[MethodImpl(MethodImplOptions.AggressiveInlining)]
#pragma warning disable CS0618 // Type or member is obsolete
Expand Down
44 changes: 39 additions & 5 deletions src/ModelContextProtocol.Core/Client/McpClientTool.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using ModelContextProtocol.Protocol;
using System.Collections.ObjectModel;
using System.Text.Json;
using System.Text.Json.Nodes;

namespace ModelContextProtocol.Client;

Expand Down Expand Up @@ -36,6 +37,7 @@ public sealed class McpClientTool : AIFunction
private readonly string _name;
private readonly string _description;
private readonly IProgress<ProgressNotificationValue>? _progress;
private readonly JsonObject? _metadata;

/// <summary>
/// Initializes a new instance of the <see cref="McpClientTool"/> class.
Expand Down Expand Up @@ -74,6 +76,7 @@ public McpClientTool(
_name = tool.Name;
_description = tool.Description ?? string.Empty;
_progress = null;
_metadata = null;
}

internal McpClientTool(
Expand All @@ -82,14 +85,16 @@ internal McpClientTool(
JsonSerializerOptions serializerOptions,
string? name = null,
string? description = null,
IProgress<ProgressNotificationValue>? progress = null)
IProgress<ProgressNotificationValue>? progress = null,
JsonObject? metadata = null)
{
_client = client;
ProtocolTool = tool;
JsonSerializerOptions = serializerOptions;
_name = name ?? tool.Name;
_description = description ?? tool.Description ?? string.Empty;
_progress = progress;
_metadata = metadata;
}

/// <summary>
Expand Down Expand Up @@ -194,7 +199,7 @@ public ValueTask<CallToolResult> CallAsync(
IProgress<ProgressNotificationValue>? progress = null,
JsonSerializerOptions? serializerOptions = null,
CancellationToken cancellationToken = default) =>
_client.CallToolAsync(ProtocolTool.Name, arguments, progress, serializerOptions, cancellationToken);
_client.CallToolAsync(ProtocolTool.Name, arguments, progress, serializerOptions, _metadata, cancellationToken);

/// <summary>
/// Creates a new instance of the tool but modified to return the specified name from its <see cref="Name"/> property.
Expand All @@ -221,7 +226,7 @@ public ValueTask<CallToolResult> CallAsync(
/// </para>
/// </remarks>
public McpClientTool WithName(string name) =>
new(_client, ProtocolTool, JsonSerializerOptions, name, _description, _progress);
new(_client, ProtocolTool, JsonSerializerOptions, name, _description, _progress, _metadata);

/// <summary>
/// Creates a new instance of the tool but modified to return the specified description from its <see cref="Description"/> property.
Expand All @@ -245,7 +250,7 @@ public McpClientTool WithName(string name) =>
/// </remarks>
/// <returns>A new instance of <see cref="McpClientTool"/> with the provided description.</returns>
public McpClientTool WithDescription(string description) =>
new(_client, ProtocolTool, JsonSerializerOptions, _name, description, _progress);
new(_client, ProtocolTool, JsonSerializerOptions, _name, description, _progress, _metadata);

/// <summary>
/// Creates a new instance of the tool but modified to report progress via the specified <see cref="IProgress{T}"/>.
Expand All @@ -268,6 +273,35 @@ public McpClientTool WithProgress(IProgress<ProgressNotificationValue> progress)
{
Throw.IfNull(progress);

return new McpClientTool(_client, ProtocolTool, JsonSerializerOptions, _name, _description, progress);
return new McpClientTool(_client, ProtocolTool, JsonSerializerOptions, _name, _description, progress, _metadata);
}

/// <summary>
/// Creates a new instance of the tool but modified to include the specified metadata in tool call requests.
/// </summary>
/// <param name="metadata">
/// The metadata to include in tool call requests. This will be serialized as the <c>_meta</c> field
/// in the JSON-RPC request parameters.
/// </param>
/// <remarks>
/// <para>
/// Adding metadata to the tool allows you to pass additional protocol-level information with each tool call.
/// This can be useful for tracing, logging, or passing context information to the server.
/// </para>
/// <para>
/// Only one metadata object can be specified at a time. Calling <see cref="WithMetadata"/> again
/// will overwrite any previously specified metadata.
/// </para>
/// <para>
/// The metadata is passed through to the server as-is, merged with any protocol-level metadata
/// such as progress tokens when <see cref="WithProgress"/> is also used.
/// </para>
/// </remarks>
/// <returns>A new instance of <see cref="McpClientTool"/>, configured with the provided metadata.</returns>
public McpClientTool WithMetadata(JsonObject metadata)
{
Throw.IfNull(metadata);

return new McpClientTool(_client, ProtocolTool, JsonSerializerOptions, _name, _description, _progress, metadata);
}
}
Loading
Loading