Add 3-arg RequestContext constructor and obsolete 2-arg to eliminate null-forgiving operator usage (#1462)

This commit is contained in:
Copilot 2026-03-27 12:33:23 -07:00 • committed by GitHub
commit 7dcdbafe9d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
33 changed files with 190 additions and 219 deletions

View file

@ -317,7 +317,7 @@ Execution flow: `filter1 -> filter2 -> filter3 -> baseHandler -> filter3 -> filt
{ {
var logger = context.Services?.GetService<ILogger<Program>>(); var logger = context.Services?.GetService<ILogger<Program>>();
logger?.LogInformation($"Processing request from {context.Params?.ProgressToken}"); logger?.LogInformation($"Processing request from {context.Params.ProgressToken}");
var result = await next(context, cancellationToken); var result = await next(context, cancellationToken);
logger?.LogInformation($"Returning {result.Tools?.Count ?? 0} tools"); logger?.LogInformation($"Returning {result.Tools?.Count ?? 0} tools");
return result; return result;
@ -339,7 +339,7 @@ Execution flow: `filter1 -> filter2 -> filter3 -> baseHandler -> filter3 -> filt
catch (Exception ex) catch (Exception ex)
{ {
var logger = context.Services?.GetService<ILogger<Program>>(); var logger = context.Services?.GetService<ILogger<Program>>();
logger?.LogError(ex, "Error while processing CallTool request for {ProgressToken}", context.Params?.ProgressToken); logger?.LogError(ex, "Error while processing CallTool request for {ProgressToken}", context.Params.ProgressToken);
return new CallToolResult return new CallToolResult
{ {

View file

@ -13,7 +13,7 @@ public class LoggingTools
int duration = 10, int duration = 10,
int steps = 10) int steps = 10)
{ {
var progressToken = context.Params?.ProgressToken; var progressToken = context.Params.ProgressToken;
var stepDuration = duration / steps; var stepDuration = duration / steps;
// <snippet_LoggingConfiguration > // <snippet_LoggingConfiguration >

View file

@ -77,7 +77,7 @@ builder.Services.AddMcpServer()
int startIndex = 0; int startIndex = 0;
// Parse cursor to determine starting position // Parse cursor to determine starting position
if (ctx.Params?.Cursor is { } cursor) if (ctx.Params.Cursor is { } cursor)
{ {
startIndex = int.Parse(cursor); startIndex = int.Parse(cursor);
} }

View file

@ -15,7 +15,7 @@ public class LongRunningTools
int duration = 10, int duration = 10,
int steps = 5) int steps = 5)
{ {
var progressToken = context.Params?.ProgressToken; var progressToken = context.Params.ProgressToken;
var stepDuration = duration / steps; var stepDuration = duration / steps;
for (int i = 1; i <= steps; i++) for (int i = 1; i <= steps; i++)

View file

@ -212,7 +212,7 @@ builder.Services.AddMcpServer()
.WithResources<MyResources>() .WithResources<MyResources>()
.WithSubscribeToResourcesHandler(async (ctx, ct) => .WithSubscribeToResourcesHandler(async (ctx, ct) =>
{ {
if (ctx.Params?.Uri is { } uri) if (ctx.Params.Uri is { } uri)
{ {
// Track the subscription (e.g., in a concurrent dictionary) // Track the subscription (e.g., in a concurrent dictionary)
subscriptions[ctx.Server.SessionId].TryAdd(uri, 0); subscriptions[ctx.Server.SessionId].TryAdd(uri, 0);
@ -221,7 +221,7 @@ builder.Services.AddMcpServer()
}) })
.WithUnsubscribeFromResourcesHandler(async (ctx, ct) => .WithUnsubscribeFromResourcesHandler(async (ctx, ct) =>
{ {
if (ctx.Params?.Uri is { } uri) if (ctx.Params.Uri is { } uri)
{ {
subscriptions[ctx.Server.SessionId].TryRemove(uri, out _); subscriptions[ctx.Server.SessionId].TryRemove(uri, out _);
} }

View file

@ -36,3 +36,4 @@ When APIs are marked as obsolete, a diagnostic is emitted to warn users that the
| :------------ | :----- | :---------- | | :------------ | :----- | :---------- |
| `MCP9001` | In place | The `EnumSchema` and `LegacyTitledEnumSchema` APIs are deprecated as of specification version 2025-11-25. Use the current schema APIs instead. | | `MCP9001` | In place | The `EnumSchema` and `LegacyTitledEnumSchema` APIs are deprecated as of specification version 2025-11-25. Use the current schema APIs instead. |
| `MCP9002` | Removed | The `AddXxxFilter` extension methods on `IMcpServerBuilder` (e.g., `AddListToolsFilter`, `AddCallToolFilter`, `AddIncomingMessageFilter`) were superseded by `WithRequestFilters()` and `WithMessageFilters()`. | | `MCP9002` | Removed | The `AddXxxFilter` extension methods on `IMcpServerBuilder` (e.g., `AddListToolsFilter`, `AddCallToolFilter`, `AddIncomingMessageFilter`) were superseded by `WithRequestFilters()` and `WithMessageFilters()`. |
| `MCP9003` | In place | The `RequestContext<TParams>(McpServer, JsonRpcRequest)` constructor is obsolete. Use the overload that accepts a `parameters` argument: `RequestContext<TParams>(McpServer, JsonRpcRequest, TParams)`. |

View file

@ -130,7 +130,7 @@ builder.Services
{ {
throw new McpException("Cannot add subscription for server with null SessionId"); throw new McpException("Cannot add subscription for server with null SessionId");
} }
if (ctx.Params?.Uri is { } uri) if (ctx.Params.Uri is { } uri)
{ {
subscriptions[ctx.Server.SessionId].TryAdd(uri, 0); subscriptions[ctx.Server.SessionId].TryAdd(uri, 0);
@ -154,7 +154,7 @@ builder.Services
{ {
throw new McpException("Cannot remove subscription for server with null SessionId"); throw new McpException("Cannot remove subscription for server with null SessionId");
} }
if (ctx.Params?.Uri is { } uri) if (ctx.Params.Uri is { } uri)
{ {
subscriptions[ctx.Server.SessionId].TryRemove(uri, out _); subscriptions[ctx.Server.SessionId].TryRemove(uri, out _);
} }
@ -212,11 +212,6 @@ builder.Services
}) })
.WithSetLoggingLevelHandler(async (ctx, ct) => .WithSetLoggingLevelHandler(async (ctx, ct) =>
{ {
if (ctx.Params?.Level is null)
{
throw new McpProtocolException("Missing required argument 'level'", McpErrorCode.InvalidParams);
}
// The SDK updates the LoggingLevel field of the IMcpServer // The SDK updates the LoggingLevel field of the IMcpServer
await ctx.Server.SendNotificationAsync("notifications/message", new await ctx.Server.SendNotificationAsync("notifications/message", new

View file

@ -19,7 +19,7 @@ public class SimpleResourceType
int index = id - 1; int index = id - 1;
if ((uint)index >= ResourceGenerator.Resources.Count) if ((uint)index >= ResourceGenerator.Resources.Count)
{ {
throw new NotSupportedException($"Unknown resource: {requestContext.Params?.Uri}"); throw new NotSupportedException($"Unknown resource: {requestContext.Params.Uri}");
} }
var resource = ResourceGenerator.Resources[index]; var resource = ResourceGenerator.Resources[index];

View file

@ -15,7 +15,7 @@ public class LongRunningTool
int duration = 10, int duration = 10,
int steps = 5) int steps = 5)
{ {
var progressToken = context.Params?.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++)

View file

@ -25,4 +25,8 @@ internal static class Obsoletions
// MCP9002 was used for the AddXxxFilter extension methods on IMcpServerBuilder that were superseded by // MCP9002 was used for the AddXxxFilter extension methods on IMcpServerBuilder that were superseded by
// WithMessageFilters() and WithRequestFilters(). The APIs were removed; do not reuse this diagnostic ID. // WithMessageFilters() and WithRequestFilters(). The APIs were removed; do not reuse this diagnostic ID.
public const string RequestContextParamsConstructor_DiagnosticId = "MCP9003";
public const string RequestContextParamsConstructor_Message = "Use the constructor overload that accepts a parameters argument.";
public const string RequestContextParamsConstructor_Url = "https://github.com/modelcontextprotocol/csharp-sdk/blob/main/docs/list-of-diagnostics.md#mcp9003";
} }

View file

@ -29,7 +29,7 @@ internal sealed class RequestHandlers : Dictionary<string, Func<JsonRpcRequest,
/// </remarks> /// </remarks>
public void Set<TParams, TResult>( public void Set<TParams, TResult>(
string method, string method,
Func<TParams?, JsonRpcRequest, CancellationToken, ValueTask<TResult>> handler, Func<TParams, JsonRpcRequest, CancellationToken, ValueTask<TResult>> handler,
JsonTypeInfo<TParams> requestTypeInfo, JsonTypeInfo<TParams> requestTypeInfo,
JsonTypeInfo<TResult> responseTypeInfo) JsonTypeInfo<TResult> responseTypeInfo)
{ {
@ -40,7 +40,7 @@ internal sealed class RequestHandlers : Dictionary<string, Func<JsonRpcRequest,
this[method] = async (request, cancellationToken) => this[method] = async (request, cancellationToken) =>
{ {
TParams? typedRequest = JsonSerializer.Deserialize(request.Params, requestTypeInfo); TParams typedRequest = JsonSerializer.Deserialize(request.Params, requestTypeInfo)!;
object? result = await handler(typedRequest, request, cancellationToken).ConfigureAwait(false); object? result = await handler(typedRequest, request, cancellationToken).ConfigureAwait(false);
return JsonSerializer.SerializeToNode(result, responseTypeInfo); return JsonSerializer.SerializeToNode(result, responseTypeInfo);
}; };

View file

@ -386,14 +386,14 @@ internal sealed class AIFunctionMcpServerResource : McpServerResource
TextContent tc => new() TextContent tc => new()
{ {
Contents = [new TextResourceContents { Uri = request.Params!.Uri, MimeType = ProtocolResourceTemplate.MimeType, Text = tc.Text }], Contents = [new TextResourceContents { Uri = request.Params.Uri, MimeType = ProtocolResourceTemplate.MimeType, Text = tc.Text }],
}, },
DataContent dc => new() DataContent dc => new()
{ {
Contents = [new BlobResourceContents Contents = [new BlobResourceContents
{ {
Uri = request.Params!.Uri, Uri = request.Params.Uri,
MimeType = dc.MediaType, MimeType = dc.MediaType,
Blob = EncodingUtilities.GetUtf8Bytes(dc.Base64Data.Span) Blob = EncodingUtilities.GetUtf8Bytes(dc.Base64Data.Span)
}], }],
@ -401,7 +401,7 @@ internal sealed class AIFunctionMcpServerResource : McpServerResource
string text => new() string text => new()
{ {
Contents = [new TextResourceContents { Uri = request.Params!.Uri, MimeType = ProtocolResourceTemplate.MimeType, Text = text }], Contents = [new TextResourceContents { Uri = request.Params.Uri, MimeType = ProtocolResourceTemplate.MimeType, Text = text }],
}, },
IEnumerable<ResourceContents> contents => new() IEnumerable<ResourceContents> contents => new()
@ -416,14 +416,14 @@ internal sealed class AIFunctionMcpServerResource : McpServerResource
{ {
TextContent tc => new TextResourceContents TextContent tc => new TextResourceContents
{ {
Uri = request.Params!.Uri, Uri = request.Params.Uri,
MimeType = ProtocolResourceTemplate.MimeType, MimeType = ProtocolResourceTemplate.MimeType,
Text = tc.Text Text = tc.Text
}, },
DataContent dc => new BlobResourceContents DataContent dc => new BlobResourceContents
{ {
Uri = request.Params!.Uri, Uri = request.Params.Uri,
MimeType = dc.MediaType, MimeType = dc.MediaType,
Blob = EncodingUtilities.GetUtf8Bytes(dc.Base64Data.Span) Blob = EncodingUtilities.GetUtf8Bytes(dc.Base64Data.Span)
}, },
@ -436,7 +436,7 @@ internal sealed class AIFunctionMcpServerResource : McpServerResource
{ {
Contents = strings.Select<string, ResourceContents>(text => new TextResourceContents Contents = strings.Select<string, ResourceContents>(text => new TextResourceContents
{ {
Uri = request.Params!.Uri, Uri = request.Params.Uri,
MimeType = ProtocolResourceTemplate.MimeType, MimeType = ProtocolResourceTemplate.MimeType,
Text = text Text = text
}).ToList(), }).ToList(),

View file

@ -954,7 +954,7 @@ internal sealed partial class McpServerImpl : McpServer
// If a handler was provided, now delegate to it. // If a handler was provided, now delegate to it.
if (setLoggingLevelHandler is not null) if (setLoggingLevelHandler is not null)
{ {
return InvokeHandlerAsync(setLoggingLevelHandler, request, jsonRpcRequest, cancellationToken); return InvokeHandlerAsync(setLoggingLevelHandler, request!, jsonRpcRequest, cancellationToken);
} }
// Otherwise, consider it handled. // Otherwise, consider it handled.
@ -966,17 +966,17 @@ internal sealed partial class McpServerImpl : McpServer
private ValueTask<TResult> InvokeHandlerAsync<TParams, TResult>( private ValueTask<TResult> InvokeHandlerAsync<TParams, TResult>(
McpRequestHandler<TParams, TResult> handler, McpRequestHandler<TParams, TResult> handler,
TParams? args, TParams args,
JsonRpcRequest jsonRpcRequest, JsonRpcRequest jsonRpcRequest,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
return _servicesScopePerRequest ? return _servicesScopePerRequest ?
InvokeScopedAsync(handler, args, jsonRpcRequest, cancellationToken) : InvokeScopedAsync(handler, args, jsonRpcRequest, cancellationToken) :
handler(new(new DestinationBoundMcpServer(this, jsonRpcRequest.Context?.RelatedTransport), jsonRpcRequest) { Params = args }, cancellationToken); handler(new(new DestinationBoundMcpServer(this, jsonRpcRequest.Context?.RelatedTransport), jsonRpcRequest, args), cancellationToken);
async ValueTask<TResult> InvokeScopedAsync( async ValueTask<TResult> InvokeScopedAsync(
McpRequestHandler<TParams, TResult> handler, McpRequestHandler<TParams, TResult> handler,
TParams? args, TParams args,
JsonRpcRequest jsonRpcRequest, JsonRpcRequest jsonRpcRequest,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
@ -984,10 +984,9 @@ internal sealed partial class McpServerImpl : McpServer
try try
{ {
return await handler( return await handler(
new RequestContext<TParams>(new DestinationBoundMcpServer(this, jsonRpcRequest.Context?.RelatedTransport), jsonRpcRequest) new RequestContext<TParams>(new DestinationBoundMcpServer(this, jsonRpcRequest.Context?.RelatedTransport), jsonRpcRequest, args)
{ {
Services = scope?.ServiceProvider ?? Services, Services = scope?.ServiceProvider ?? Services,
Params = args
}, },
cancellationToken).ConfigureAwait(false); cancellationToken).ConfigureAwait(false);
} }

View file

@ -13,19 +13,34 @@ namespace ModelContextProtocol.Server;
/// </remarks> /// </remarks>
public sealed class RequestContext<TParams> : MessageContext public sealed class RequestContext<TParams> : MessageContext
{ {
/// <summary>
/// Initializes a new instance of the <see cref="RequestContext{TParams}"/> class with the specified server, JSON-RPC request, and request parameters.
/// </summary>
/// <param name="server">The server with which this instance is associated.</param>
/// <param name="jsonRpcRequest">The JSON-RPC request associated with this context.</param>
/// <param name="parameters">The parameters associated with this request.</param>
/// <exception cref="ArgumentNullException"><paramref name="server"/> or <paramref name="jsonRpcRequest"/> is <see langword="null"/>.</exception>
public RequestContext(McpServer server, JsonRpcRequest jsonRpcRequest, TParams parameters)
: base(server, jsonRpcRequest)
{
Params = parameters;
}
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="RequestContext{TParams}"/> class with the specified server and JSON-RPC request. /// Initializes a new instance of the <see cref="RequestContext{TParams}"/> class with the specified server and JSON-RPC request.
/// </summary> /// </summary>
/// <param name="server">The server with which this instance is associated.</param> /// <param name="server">The server with which this instance is associated.</param>
/// <param name="jsonRpcRequest">The JSON-RPC request associated with this context.</param> /// <param name="jsonRpcRequest">The JSON-RPC request associated with this context.</param>
/// <exception cref="ArgumentNullException"><paramref name="server"/> or <paramref name="jsonRpcRequest"/> is <see langword="null"/>.</exception> /// <exception cref="ArgumentNullException"><paramref name="server"/> or <paramref name="jsonRpcRequest"/> is <see langword="null"/>.</exception>
[Obsolete(Obsoletions.RequestContextParamsConstructor_Message, DiagnosticId = Obsoletions.RequestContextParamsConstructor_DiagnosticId, UrlFormat = Obsoletions.RequestContextParamsConstructor_Url)]
public RequestContext(McpServer server, JsonRpcRequest jsonRpcRequest) public RequestContext(McpServer server, JsonRpcRequest jsonRpcRequest)
: base(server, jsonRpcRequest) : base(server, jsonRpcRequest)
{ {
Params = default!;
} }
/// <summary>Gets or sets the parameters associated with this request.</summary> /// <summary>Gets or sets the parameters associated with this request.</summary>
public TParams? Params { get; set; } public TParams Params { get; set; }
/// <summary> /// <summary>
/// Gets or sets the primitive that matched the request. /// Gets or sets the primitive that matched the request.

View file

@ -39,7 +39,7 @@ public class Program
// For the test_reconnection tool, enable polling mode after the tool runs. // For the test_reconnection tool, enable polling mode after the tool runs.
// This stores the result and closes the SSE stream, so the client // This stores the result and closes the SSE stream, so the client
// must reconnect via GET with Last-Event-ID to retrieve the result. // must reconnect via GET with Last-Event-ID to retrieve the result.
if (request.Params?.Name == "test_reconnection") if (request.Params.Name == "test_reconnection")
{ {
await request.EnablePollingAsync(TimeSpan.FromMilliseconds(500), cancellationToken); await request.EnablePollingAsync(TimeSpan.FromMilliseconds(500), cancellationToken);
} }
@ -54,7 +54,7 @@ public class Program
{ {
throw new McpException("Cannot add subscription for server with null SessionId"); throw new McpException("Cannot add subscription for server with null SessionId");
} }
if (ctx.Params?.Uri is { } uri) if (ctx.Params.Uri is { } uri)
{ {
var sessionSubscriptions = subscriptions.GetOrAdd(ctx.Server.SessionId, _ => new()); var sessionSubscriptions = subscriptions.GetOrAdd(ctx.Server.SessionId, _ => new());
sessionSubscriptions.TryAdd(uri, 0); sessionSubscriptions.TryAdd(uri, 0);
@ -68,7 +68,7 @@ public class Program
{ {
throw new McpException("Cannot remove subscription for server with null SessionId"); throw new McpException("Cannot remove subscription for server with null SessionId");
} }
if (ctx.Params?.Uri is { } uri) if (ctx.Params.Uri is { } uri)
{ {
subscriptions[ctx.Server.SessionId].TryRemove(uri, out _); subscriptions[ctx.Server.SessionId].TryRemove(uri, out _);
} }
@ -91,11 +91,6 @@ public class Program
}) })
.WithSetLoggingLevelHandler(async (ctx, ct) => .WithSetLoggingLevelHandler(async (ctx, ct) =>
{ {
if (ctx.Params?.Level is null)
{
throw new McpProtocolException("Missing required argument 'level'", McpErrorCode.InvalidParams);
}
// The SDK updates the LoggingLevel field of the McpServer // The SDK updates the LoggingLevel field of the McpServer
// Send a log notification to confirm the level was set // Send a log notification to confirm the level was set
await ctx.Server.SendNotificationAsync("notifications/message", new LoggingMessageNotificationParams await ctx.Server.SendNotificationAsync("notifications/message", new LoggingMessageNotificationParams

View file

@ -132,7 +132,7 @@ public class ConformanceTools
RequestContext<CallToolRequestParams> context, RequestContext<CallToolRequestParams> context,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
var progressToken = context.Params?.ProgressToken; var progressToken = context.Params.ProgressToken;
if (progressToken is not null) if (progressToken is not null)
{ {

View file

@ -204,9 +204,9 @@ internal static class Program
}; };
options.Handlers.CallToolHandler = async (request, cancellationToken) => options.Handlers.CallToolHandler = async (request, cancellationToken) =>
{ {
if (request.Params?.Name == "echo") if (request.Params.Name == "echo")
{ {
if (request.Params?.Arguments is null || !request.Params.Arguments.TryGetValue("message", out var message)) if (request.Params.Arguments is null || !request.Params.Arguments.TryGetValue("message", out var message))
{ {
throw new McpProtocolException("Missing required argument 'message'", McpErrorCode.InvalidParams); throw new McpProtocolException("Missing required argument 'message'", McpErrorCode.InvalidParams);
} }
@ -215,16 +215,16 @@ internal static class Program
Content = [new TextContentBlock { Text = $"Echo: {message}" }] Content = [new TextContentBlock { Text = $"Echo: {message}" }]
}; };
} }
else if (request.Params?.Name == "echoSessionId") else if (request.Params.Name == "echoSessionId")
{ {
return new CallToolResult return new CallToolResult
{ {
Content = [new TextContentBlock { Text = request.Server.SessionId ?? string.Empty }] Content = [new TextContentBlock { Text = request.Server.SessionId ?? string.Empty }]
}; };
} }
else if (request.Params?.Name == "trigger-sampling-request") else if (request.Params.Name == "trigger-sampling-request")
{ {
if (request.Params?.Arguments is null || if (request.Params.Arguments is null ||
!request.Params.Arguments.TryGetValue("prompt", out var prompt) || !request.Params.Arguments.TryGetValue("prompt", out var prompt) ||
!request.Params.Arguments.TryGetValue("maxTokens", out var maxTokens)) !request.Params.Arguments.TryGetValue("maxTokens", out var maxTokens))
{ {
@ -238,16 +238,16 @@ internal static class Program
Content = [new TextContentBlock { Text = $"LLM sampling result: {sampleResult.Content.OfType<TextContentBlock>().FirstOrDefault()?.Text}" }] Content = [new TextContentBlock { Text = $"LLM sampling result: {sampleResult.Content.OfType<TextContentBlock>().FirstOrDefault()?.Text}" }]
}; };
} }
else if (request.Params?.Name == "echoCliArg") else if (request.Params.Name == "echoCliArg")
{ {
return new CallToolResult return new CallToolResult
{ {
Content = [new TextContentBlock { Text = cliArg ?? "null" }] Content = [new TextContentBlock { Text = cliArg ?? "null" }]
}; };
} }
else if (request.Params?.Name == "longRunning") else if (request.Params.Name == "longRunning")
{ {
if (request.Params?.Arguments is null || !request.Params.Arguments.TryGetValue("durationMs", out var durationMsValue)) if (request.Params.Arguments is null || !request.Params.Arguments.TryGetValue("durationMs", out var durationMsValue))
{ {
throw new McpProtocolException("Missing required argument 'durationMs'", McpErrorCode.InvalidParams); throw new McpProtocolException("Missing required argument 'durationMs'", McpErrorCode.InvalidParams);
} }
@ -258,9 +258,9 @@ internal static class Program
Content = [new TextContentBlock { Text = $"Long-running operation completed after {durationMs}ms" }] Content = [new TextContentBlock { Text = $"Long-running operation completed after {durationMs}ms" }]
}; };
} }
else if (request.Params?.Name == "crash") else if (request.Params.Name == "crash")
{ {
if (request.Params?.Arguments is null || !request.Params.Arguments.TryGetValue("exitCode", out var exitCodeValue)) if (request.Params.Arguments is null || !request.Params.Arguments.TryGetValue("exitCode", out var exitCodeValue))
{ {
throw new McpProtocolException("Missing required argument 'exitCode'", McpErrorCode.InvalidParams); throw new McpProtocolException("Missing required argument 'exitCode'", McpErrorCode.InvalidParams);
} }
@ -271,7 +271,7 @@ internal static class Program
} }
else else
{ {
throw new McpProtocolException($"Unknown tool: {request.Params?.Name}", McpErrorCode.InvalidParams); throw new McpProtocolException($"Unknown tool: {request.Params.Name}", McpErrorCode.InvalidParams);
} }
}; };
} }
@ -335,7 +335,7 @@ internal static class Program
options.Handlers.GetPromptHandler = async (request, cancellationToken) => options.Handlers.GetPromptHandler = async (request, cancellationToken) =>
{ {
List<PromptMessage> messages = []; List<PromptMessage> messages = [];
if (request.Params?.Name == "simple-prompt") if (request.Params.Name == "simple-prompt")
{ {
messages.Add(new PromptMessage messages.Add(new PromptMessage
{ {
@ -343,7 +343,7 @@ internal static class Program
Content = new TextContentBlock { Text = "This is a simple prompt without arguments." }, Content = new TextContentBlock { Text = "This is a simple prompt without arguments." },
}); });
} }
else if (request.Params?.Name == "args-prompt") else if (request.Params.Name == "args-prompt")
{ {
string city = request.Params.Arguments?["city"].ToString() ?? "unknown"; string city = request.Params.Arguments?["city"].ToString() ?? "unknown";
string state = request.Params.Arguments?["state"].ToString() ?? ""; string state = request.Params.Arguments?["state"].ToString() ?? "";
@ -354,7 +354,7 @@ internal static class Program
Content = new TextContentBlock { Text = $"What's weather in {location}?" }, Content = new TextContentBlock { Text = $"What's weather in {location}?" },
}); });
} }
else if (request.Params?.Name == "completable-prompt") else if (request.Params.Name == "completable-prompt")
{ {
string department = request.Params.Arguments?["department"].ToString() ?? "unknown"; string department = request.Params.Arguments?["department"].ToString() ?? "unknown";
string name = request.Params.Arguments?["name"].ToString() ?? "unknown"; string name = request.Params.Arguments?["name"].ToString() ?? "unknown";
@ -366,7 +366,7 @@ internal static class Program
} }
else else
{ {
throw new McpProtocolException($"Unknown prompt: {request.Params?.Name}", McpErrorCode.InvalidParams); throw new McpProtocolException($"Unknown prompt: {request.Params.Name}", McpErrorCode.InvalidParams);
} }
return new GetPromptResult return new GetPromptResult
@ -382,11 +382,6 @@ internal static class Program
{ {
options.Handlers.SetLoggingLevelHandler = async (request, cancellationToken) => options.Handlers.SetLoggingLevelHandler = async (request, cancellationToken) =>
{ {
if (request.Params?.Level is null)
{
throw new McpProtocolException("Missing required argument 'level'", McpErrorCode.InvalidParams);
}
_minimumLoggingLevel = request.Params.Level; _minimumLoggingLevel = request.Params.Level;
return new EmptyResult(); return new EmptyResult();
@ -452,7 +447,7 @@ internal static class Program
options.Handlers.ListResourcesHandler = async (request, cancellationToken) => options.Handlers.ListResourcesHandler = async (request, cancellationToken) =>
{ {
int startIndex = 0; int startIndex = 0;
if (request.Params?.Cursor is not null) if (request.Params.Cursor is not null)
{ {
try try
{ {
@ -481,7 +476,7 @@ internal static class Program
options.Handlers.ReadResourceHandler = async (request, cancellationToken) => options.Handlers.ReadResourceHandler = async (request, cancellationToken) =>
{ {
if (request.Params?.Uri is null) if (request.Params.Uri is null)
{ {
throw new McpProtocolException("Missing required argument 'uri'", McpErrorCode.InvalidParams); throw new McpProtocolException("Missing required argument 'uri'", McpErrorCode.InvalidParams);
} }
@ -518,7 +513,7 @@ internal static class Program
options.Handlers.SubscribeToResourcesHandler = async (request, cancellationToken) => options.Handlers.SubscribeToResourcesHandler = async (request, cancellationToken) =>
{ {
if (request?.Params?.Uri is null) if (request?.Params.Uri is null)
{ {
throw new McpProtocolException("Missing required argument 'uri'", McpErrorCode.InvalidParams); throw new McpProtocolException("Missing required argument 'uri'", McpErrorCode.InvalidParams);
} }
@ -535,7 +530,7 @@ internal static class Program
options.Handlers.UnsubscribeFromResourcesHandler = async (request, cancellationToken) => options.Handlers.UnsubscribeFromResourcesHandler = async (request, cancellationToken) =>
{ {
if (request?.Params?.Uri is null) if (request?.Params.Uri is null)
{ {
throw new McpProtocolException("Missing required argument 'uri'", McpErrorCode.InvalidParams); throw new McpProtocolException("Missing required argument 'uri'", McpErrorCode.InvalidParams);
} }
@ -563,7 +558,7 @@ internal static class Program
options.Handlers.CompleteHandler = async (request, cancellationToken) => options.Handlers.CompleteHandler = async (request, cancellationToken) =>
{ {
string[]? values; string[]? values;
switch (request.Params?.Ref) switch (request.Params.Ref)
{ {
case ResourceTemplateReference rtr: case ResourceTemplateReference rtr:
var resourceId = rtr.Uri?.Split('/').LastOrDefault(); var resourceId = rtr.Uri?.Split('/').LastOrDefault();
@ -571,7 +566,7 @@ internal static class Program
return new CompleteResult { Completion = new() { Values = [] } }; return new CompleteResult { Completion = new() { Values = [] } };
// Filter resource IDs that start with the input value // Filter resource IDs that start with the input value
values = sampleResourceIds.Where(id => id.StartsWith(request.Params!.Argument.Value)).ToArray(); values = sampleResourceIds.Where(id => id.StartsWith(request.Params.Argument.Value)).ToArray();
return new CompleteResult { Completion = new() { Values = values, HasMore = false, Total = values.Length } }; return new CompleteResult { Completion = new() { Values = values, HasMore = false, Total = values.Length } };
case PromptReference pr: case PromptReference pr:
@ -583,7 +578,7 @@ internal static class Program
return new CompleteResult { Completion = new() { Values = values, HasMore = false, Total = values.Length } }; return new CompleteResult { Completion = new() { Values = values, HasMore = false, Total = values.Length } };
default: default:
throw new McpProtocolException($"Unknown reference type: '{request.Params?.Ref.Type}'", McpErrorCode.InvalidParams); throw new McpProtocolException($"Unknown reference type: '{request.Params.Ref.Type}'", McpErrorCode.InvalidParams);
} }
}; };
} }

View file

@ -280,7 +280,7 @@ public class Program
ReadResourceHandler = async (request, cancellationToken) => ReadResourceHandler = async (request, cancellationToken) =>
{ {
if (request.Params?.Uri is null) if (request.Params.Uri is null)
{ {
throw new McpProtocolException("Missing required argument 'uri'", McpErrorCode.InvalidParams); throw new McpProtocolException("Missing required argument 'uri'", McpErrorCode.InvalidParams);
} }

View file

@ -37,7 +37,7 @@ public class McpClientMetaTests : ClientServerTestBase
async (RequestContext<CallToolRequestParams> context) => async (RequestContext<CallToolRequestParams> context) =>
{ {
// Access the foo property of _meta field from the request parameters // Access the foo property of _meta field from the request parameters
var metaFoo = context.Params?.Meta?["foo"]?.ToString(); var metaFoo = context.Params.Meta?["foo"]?.ToString();
// Assert that the meta foo is correctly passed // Assert that the meta foo is correctly passed
Assert.NotNull(metaFoo); Assert.NotNull(metaFoo);
@ -73,7 +73,7 @@ public class McpClientMetaTests : ClientServerTestBase
(RequestContext<ReadResourceRequestParams> context) => (RequestContext<ReadResourceRequestParams> context) =>
{ {
// Access the foo property of _meta field from the request parameters // Access the foo property of _meta field from the request parameters
var metaFoo = context.Params?.Meta?["foo"]?.ToString(); var metaFoo = context.Params.Meta?["foo"]?.ToString();
// Assert that the meta foo is correctly passed // Assert that the meta foo is correctly passed
Assert.NotNull(metaFoo); Assert.NotNull(metaFoo);
@ -109,7 +109,7 @@ public class McpClientMetaTests : ClientServerTestBase
(RequestContext<GetPromptRequestParams> context) => (RequestContext<GetPromptRequestParams> context) =>
{ {
// Access the foo property of _meta field from the request parameters // Access the foo property of _meta field from the request parameters
var metaFoo = context.Params?.Meta?["foo"]?.ToString(); var metaFoo = context.Params.Meta?["foo"]?.ToString();
// Assert that the meta foo is correctly passed // Assert that the meta foo is correctly passed
Assert.NotNull(metaFoo); Assert.NotNull(metaFoo);

View file

@ -29,7 +29,7 @@ public class McpClientPromptTests : ClientServerTestBase
[McpServerPrompt, Description("Echoes back the metadata it receives")] [McpServerPrompt, Description("Echoes back the metadata it receives")]
public static ChatMessage MetadataEcho(RequestContext<GetPromptRequestParams> context) => public static ChatMessage MetadataEcho(RequestContext<GetPromptRequestParams> context) =>
new(ChatRole.User, context.Params?.Meta?.ToJsonString() ?? "{}"); new(ChatRole.User, context.Params.Meta?.ToJsonString() ?? "{}");
} }
[Fact] [Fact]

Some files were not shown because too many files have changed in this diff Show more