Skip to content

Commit f7c35e3

Browse files
committed
Change design of RequestOptions
1 parent dd4016d commit f7c35e3

File tree

5 files changed

+198
-402
lines changed

5 files changed

+198
-402
lines changed

src/ModelContextProtocol.Core/Client/McpClient.Methods.cs

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ public ValueTask<PingResult> PingAsync(RequestOptions? options = null, Cancellat
9393
return PingAsync(
9494
new PingRequestParams
9595
{
96-
Meta = options?.Meta
96+
Meta = options?.GetMetaForRequest()
9797
},
9898
cancellationToken);
9999
}
@@ -130,7 +130,7 @@ public async ValueTask<IList<McpClientTool>> ListToolsAsync(
130130
CancellationToken cancellationToken = default)
131131
{
132132
List<McpClientTool>? tools = null;
133-
ListToolsRequestParams requestParams = new() { Meta = options?.Meta };
133+
ListToolsRequestParams requestParams = new() { Meta = options?.GetMetaForRequest() };
134134
do
135135
{
136136
var toolResults = await ListToolsAsync(requestParams, cancellationToken).ConfigureAwait(false);
@@ -184,7 +184,7 @@ public async ValueTask<IList<McpClientPrompt>> ListPromptsAsync(
184184
CancellationToken cancellationToken = default)
185185
{
186186
List<McpClientPrompt>? prompts = null;
187-
ListPromptsRequestParams requestParams = new() { Meta = options?.Meta };
187+
ListPromptsRequestParams requestParams = new() { Meta = options?.GetMetaForRequest() };
188188
do
189189
{
190190
var promptResults = await ListPromptsAsync(requestParams, cancellationToken).ConfigureAwait(false);
@@ -251,7 +251,7 @@ public ValueTask<GetPromptResult> GetPromptAsync(
251251
{
252252
Name = name,
253253
Arguments = ToArgumentsDictionary(arguments, serializerOptions),
254-
Meta = options?.Meta,
254+
Meta = options?.GetMetaForRequest(),
255255
},
256256
cancellationToken);
257257
}
@@ -288,7 +288,7 @@ public async ValueTask<IList<McpClientResourceTemplate>> ListResourceTemplatesAs
288288
CancellationToken cancellationToken = default)
289289
{
290290
List<McpClientResourceTemplate>? resourceTemplates = null;
291-
ListResourceTemplatesRequestParams requestParams = new() { Meta = options?.Meta };
291+
ListResourceTemplatesRequestParams requestParams = new() { Meta = options?.GetMetaForRequest() };
292292
do
293293
{
294294
var templateResults = await ListResourceTemplatesAsync(requestParams, cancellationToken).ConfigureAwait(false);
@@ -342,7 +342,7 @@ public async ValueTask<IList<McpClientResource>> ListResourcesAsync(
342342
CancellationToken cancellationToken = default)
343343
{
344344
List<McpClientResource>? resources = null;
345-
ListResourcesRequestParams requestParams = new() { Meta = options?.Meta };
345+
ListResourcesRequestParams requestParams = new() { Meta = options?.GetMetaForRequest() };
346346
do
347347
{
348348
var resourceResults = await ListResourcesAsync(requestParams, cancellationToken).ConfigureAwait(false);
@@ -413,7 +413,7 @@ public ValueTask<ReadResourceResult> ReadResourceAsync(
413413
return ReadResourceAsync(new ReadResourceRequestParams
414414
{
415415
Uri = uri,
416-
Meta = options?.Meta,
416+
Meta = options?.GetMetaForRequest(),
417417
}, cancellationToken);
418418
}
419419

@@ -434,7 +434,7 @@ public ValueTask<ReadResourceResult> ReadResourceAsync(
434434
new ReadResourceRequestParams
435435
{
436436
Uri = UriTemplate.FormatUri(uriTemplate, arguments),
437-
Meta = options?.Meta,
437+
Meta = options?.GetMetaForRequest(),
438438
},
439439
cancellationToken);
440440
}
@@ -481,7 +481,7 @@ public ValueTask<CompleteResult> CompleteAsync(
481481
{
482482
Ref = reference,
483483
Argument = new() { Name = argumentName, Value = argumentValue },
484-
Meta = options?.Meta,
484+
Meta = options?.GetMetaForRequest(),
485485
},
486486
cancellationToken);
487487
}
@@ -536,7 +536,7 @@ public Task SubscribeToResourceAsync(string uri, RequestOptions? options = null,
536536
new SubscribeRequestParams
537537
{
538538
Uri = uri,
539-
Meta = options?.Meta,
539+
Meta = options?.GetMetaForRequest(),
540540
},
541541
cancellationToken);
542542
}
@@ -591,7 +591,7 @@ public Task UnsubscribeFromResourceAsync(string uri, RequestOptions? options = n
591591
new UnsubscribeRequestParams
592592
{
593593
Uri = uri,
594-
Meta = options?.Meta
594+
Meta = options?.GetMetaForRequest()
595595
},
596596
cancellationToken);
597597
}
@@ -645,12 +645,12 @@ public ValueTask<CallToolResult> CallToolAsync(
645645
{
646646
Name = toolName,
647647
Arguments = ToArgumentsDictionary(arguments, serializerOptions),
648-
Meta = options?.Meta,
648+
Meta = options?.GetMetaForRequest(),
649649
},
650650
cancellationToken);
651651
}
652652

653-
return SendRequestWithProgressAsync(toolName, arguments, progress, options?.Meta, serializerOptions, cancellationToken);
653+
return SendRequestWithProgressAsync(toolName, arguments, progress, options?.GetMetaForRequest(), serializerOptions, cancellationToken);
654654

655655
async ValueTask<CallToolResult> SendRequestWithProgressAsync(
656656
string toolName,
@@ -732,7 +732,7 @@ public Task SetLoggingLevelAsync(LoggingLevel level, RequestOptions? options = n
732732
new SetLevelRequestParams
733733
{
734734
Level = level,
735-
Meta = options?.Meta
735+
Meta = options?.GetMetaForRequest()
736736
},
737737
cancellationToken);
738738
}

src/ModelContextProtocol.Core/McpSession.Methods.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ public Task NotifyProgressAsync(
183183
{
184184
ProgressToken = progressToken,
185185
Progress = progress,
186-
Meta = options?.Meta,
186+
Meta = options?.GetMetaForRequest(),
187187
},
188188
cancellationToken);
189189
}

src/ModelContextProtocol.Core/RequestOptions.cs

Lines changed: 45 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,10 @@
55
namespace ModelContextProtocol;
66

77
/// <summary>
8-
/// Contains optional parameters for MCP requests.
8+
/// Provides a bag of optional parameters for use with MCP requests.
99
/// </summary>
1010
public sealed class RequestOptions
1111
{
12-
/// <summary>
13-
/// Optional metadata to include in the request.
14-
/// </summary>
15-
private JsonObject? _meta;
16-
1712
/// <summary>
1813
/// Initializes a new instance of the <see cref="RequestOptions"/> class.
1914
/// </summary>
@@ -22,75 +17,61 @@ public RequestOptions()
2217
}
2318

2419
/// <summary>
25-
/// Optional metadata to include in the request.
26-
/// When getting, automatically includes the progress token if set.
20+
/// Gets or sets optional metadata to include as the "_meta" property in a request.
2721
/// </summary>
28-
public JsonObject? Meta
29-
{
30-
get => _meta ??= [];
31-
set
32-
{
33-
// Capture the existing progressToken value if set.
34-
var existingProgressToken = _meta?["progressToken"];
35-
36-
if (value is not null)
37-
{
38-
if (existingProgressToken is not null)
39-
{
40-
value["progressToken"] ??= existingProgressToken;
41-
}
22+
/// <remarks>
23+
/// Although progress tokens are propagated in MCP "_meta" objects, the <see cref="ProgressToken"/>
24+
/// property and the <see cref="Meta"/> property do not interact (setting <see cref="Meta"/>
25+
/// does not affect <see cref="ProgressToken"/>, and the object returned from <see cref="Meta"/>
26+
/// is not impacting by the value of <see cref="ProgressToken"/>). To get the actual <see cref="JsonObject"/>
27+
/// that contains state from both <see cref="Meta"/> and <see cref="ProgressToken"/>, use the
28+
/// <see cref="GetMetaForRequest"/> method.
29+
/// </remarks>
30+
public JsonObject? Meta { get; set; }
4231

43-
_meta = value;
44-
}
45-
else if (existingProgressToken is not null)
46-
{
47-
_meta = new()
48-
{
49-
["progressToken"] = existingProgressToken,
50-
};
51-
}
52-
else
53-
{
54-
_meta = null;
55-
}
56-
}
57-
}
32+
/// <summary>
33+
/// Gets or sets an optional progress token to use for tracking long-running operations.
34+
/// </summary>
35+
/// <remarks>
36+
/// Although progress tokens are propagated in MCP "_meta" objects, the <see cref="ProgressToken"/>
37+
/// property and the <see cref="Meta"/> property do not interact (setting <see cref="ProgressToken"/>
38+
/// does not affect <see cref="Meta"/>, and getting <see cref="ProgressToken"/> does not read from
39+
/// <see cref="Meta"/>. To get the actual <see cref="JsonObject"/> that contains state from both
40+
/// <see cref="Meta"/> and <see cref="ProgressToken"/>, use the <see cref="GetMetaForRequest"/> method.
41+
/// </remarks>
42+
public ProgressToken? ProgressToken { get; set; }
5843

5944
/// <summary>
60-
/// The serializer options governing tool parameter serialization. If null, the default options are used.
45+
/// Gets or sets a <see cref="JsonSerializer"/> to use for any serialization of arguments or results in the request.
6146
/// </summary>
47+
/// <remarks>
48+
/// If <see langword="null"/>, <see cref="McpJsonUtilities.DefaultOptions"/> is used.
49+
/// </remarks>
6250
public JsonSerializerOptions? JsonSerializerOptions { get; set; }
6351

6452
/// <summary>
65-
/// The progress token for tracking long-running operations.
53+
/// Gets a <see cref="JsonObject"/> to use in requests for the "_meta" property.
6654
/// </summary>
67-
public ProgressToken? ProgressToken
55+
/// <returns>
56+
/// A <see cref="JsonObject"/> suitable for use in requests for the "_meta" property.
57+
/// </returns>
58+
/// <remarks>
59+
/// Progress tokens are part of MCP's _meta property. As such, if <see cref="ProgressToken"/>
60+
/// is non-<see langword="null"/> but <see cref="Meta"/> is <see langword="null"/>, <see cref="GetMetaForRequest"/> will
61+
/// manufacture and return a new <see cref="JsonObject"/> instance containing the token. If both <see cref="ProgressToken"/>
62+
/// and <see cref="Meta"/> are non-<see langword="null"/>, a new clone of <see cref="Meta"/> will be created and its
63+
/// "progressToken" property overwritten with <see cref="ProgressToken"/>. Otherwise, <see cref="GetMetaForRequest"/>
64+
/// will just return <see cref="Meta"/>.
65+
/// </remarks>
66+
public JsonObject? GetMetaForRequest()
6867
{
69-
get
68+
JsonObject? meta = Meta;
69+
if (ProgressToken is not null)
7070
{
71-
return _meta?["progressToken"] switch
72-
{
73-
JsonValue v when v.TryGetValue(out string? s) => new(s),
74-
JsonValue v when v.TryGetValue(out long l) => new(l),
75-
_ => null
76-
};
77-
}
78-
set
79-
{
80-
if (value?.Token is { } token)
81-
{
82-
_meta ??= [];
83-
_meta["progressToken"] = token switch
84-
{
85-
string s => s,
86-
long l => l,
87-
_ => throw new InvalidOperationException("ProgressToken must be a string or long"),
88-
};
89-
}
90-
else
91-
{
92-
_meta?.Remove("progressToken");
93-
}
71+
meta = (JsonObject?)meta?.DeepClone() ?? [];
72+
meta["progressToken"] = ProgressToken.ToString();
9473
}
74+
75+
return meta;
9576
}
9677
}

src/ModelContextProtocol.Core/Server/McpServer.Methods.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -297,7 +297,7 @@ public async ValueTask<ElicitResult<T>> ElicitAsync<T>(
297297
{
298298
Message = message,
299299
RequestedSchema = schema,
300-
Meta = options?.Meta,
300+
Meta = options?.GetMetaForRequest(),
301301
};
302302

303303
ThrowIfElicitationUnsupported(request);

0 commit comments

Comments
 (0)