Deprecate McpErrorCode.ResourceNotFound (-32002) and use McpErrorCode.InvalidParams (-32602) per SEP-2164 (#1558)

This commit is contained in:
Jayaraman Venkatesan 2026-05-26 10:59:03 -04:00 • committed by GitHub
commit b99d887b46
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 77 additions and 15 deletions

View file

@ -109,7 +109,7 @@ public partial class McpServerBuilderExtensionsResourcesTests : ClientServerTest
};
}
throw new McpProtocolException($"Resource not found: {request.Params.Uri}", McpErrorCode.ResourceNotFound);
throw new McpProtocolException($"Resource not found: {request.Params.Uri}", McpErrorCode.InvalidParams);
})
.WithResources<SimpleResources>();
}
@ -317,7 +317,7 @@ public partial class McpServerBuilderExtensionsResourcesTests : ClientServerTest
cancellationToken: TestContext.Current.CancellationToken));
Assert.Contains("Resource not found", e.Message);
Assert.Equal(McpErrorCode.ResourceNotFound, e.ErrorCode);
Assert.Equal(McpErrorCode.InvalidParams, e.ErrorCode);
}
[Fact]

View file

@ -1,4 +1,5 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using ModelContextProtocol.Client;
using ModelContextProtocol.Protocol;
using ModelContextProtocol.Server;
@ -22,6 +23,23 @@ public sealed class McpServerResourceRoutingTests(ITestOutputHelper testOutputHe
return await CreateMcpClientForServer();
}
/// <summary>
/// Starts the server with the specified resources, pins both the server's and the
/// client's protocol version to <paramref name="protocolVersion"/>, and returns a
/// connected client. Both ends must be pinned because <see cref="McpClient"/> strictly
/// compares the server's negotiated version against the client's requested version and
/// refuses to connect on mismatch.
/// </summary>
private async Task<McpClient> CreateClientWithResourcesAndServerVersionAsync(
string protocolVersion,
params McpServerResource[] resources)
{
McpServerBuilder.WithResources(resources);
McpServerBuilder.Services.Configure<McpServerOptions>(o => o.ProtocolVersion = protocolVersion);
StartServer();
return await CreateMcpClientForServer(new McpClientOptions { ProtocolVersion = protocolVersion });
}
/// <summary>
/// Asserts that the given URI matches the template and produces the expected text result.
/// </summary>
@ -56,6 +74,26 @@ public sealed class McpServerResourceRoutingTests(ITestOutputHelper testOutputHe
Assert.Equal(McpErrorCode.ResourceNotFound, ex.ErrorCode);
}
// Unknown-resource-URI responses are version-gated: older clients keep the legacy
// -32002 (McpErrorCode.ResourceNotFound), and clients on the draft protocol version that
// moves to the standard JSON-RPC code see -32602 (McpErrorCode.InvalidParams).
[Theory]
[InlineData("2025-11-25", McpErrorCode.ResourceNotFound)]
[InlineData("DRAFT-2026-v1", McpErrorCode.InvalidParams)]
public async Task ResourceNotFound_ErrorCode_IsVersionGated(string serverProtocolVersion, McpErrorCode expectedCode)
{
var resource = McpServerResource.Create(
options: new() { UriTemplate = "test://known/{id}" },
method: (string id) => $"ok: {id}");
var client = await CreateClientWithResourcesAndServerVersionAsync(serverProtocolVersion, resource);
var ex = await Assert.ThrowsAsync<McpProtocolException>(async () =>
await client.ReadResourceAsync("test://unknown", null, TestContext.Current.CancellationToken));
Assert.Equal(expectedCode, ex.ErrorCode);
}
/// <summary>
/// Verify that when multiple templated resources exist, the correct one is matched based on the URI pattern.
/// Regression test for https://github.com/modelcontextprotocol/csharp-sdk/issues/821.

View file

@ -33,7 +33,7 @@ public class McpProtocolExceptionDataTests : ClientServerTestBase
switch (toolName)
{
case "throw_with_serializable_data":
throw new McpProtocolException("Resource not found", McpErrorCode.ResourceNotFound)
throw new McpProtocolException("Resource not found", McpErrorCode.InvalidParams)
{
Data =
{
@ -43,7 +43,7 @@ public class McpProtocolExceptionDataTests : ClientServerTestBase
};
case "throw_with_nonserializable_data":
throw new McpProtocolException("Resource not found", McpErrorCode.ResourceNotFound)
throw new McpProtocolException("Resource not found", McpErrorCode.InvalidParams)
{
Data =
{
@ -55,7 +55,7 @@ public class McpProtocolExceptionDataTests : ClientServerTestBase
};
case "throw_with_only_nonserializable_data":
throw new McpProtocolException("Resource not found", McpErrorCode.ResourceNotFound)
throw new McpProtocolException("Resource not found", McpErrorCode.InvalidParams)
{
Data =
{
@ -79,7 +79,7 @@ public class McpProtocolExceptionDataTests : ClientServerTestBase
await client.CallToolAsync("throw_with_serializable_data", cancellationToken: TestContext.Current.CancellationToken));
Assert.Equal("Request failed (remote): Resource not found", exception.Message);
Assert.Equal(McpErrorCode.ResourceNotFound, exception.ErrorCode);
Assert.Equal(McpErrorCode.InvalidParams, exception.ErrorCode);
// Verify the data was propagated to the exception
// The Data collection should contain the expected keys
@ -113,7 +113,7 @@ public class McpProtocolExceptionDataTests : ClientServerTestBase
await client.CallToolAsync("throw_with_nonserializable_data", cancellationToken: TestContext.Current.CancellationToken));
Assert.Equal("Request failed (remote): Resource not found", exception.Message);
Assert.Equal(McpErrorCode.ResourceNotFound, exception.ErrorCode);
Assert.Equal(McpErrorCode.InvalidParams, exception.ErrorCode);
// Verify that only the serializable data was propagated (non-serializable was filtered out)
var hasUri = false;
@ -142,7 +142,7 @@ public class McpProtocolExceptionDataTests : ClientServerTestBase
await client.CallToolAsync("throw_with_only_nonserializable_data", cancellationToken: TestContext.Current.CancellationToken));
Assert.Equal("Request failed (remote): Resource not found", exception.Message);
Assert.Equal(McpErrorCode.ResourceNotFound, exception.ErrorCode);
Assert.Equal(McpErrorCode.InvalidParams, exception.ErrorCode);
// When all data is non-serializable, the Data collection should be empty
// (the server's ConvertExceptionData returns null when no serializable data exists)

View file

@ -1033,7 +1033,7 @@ public class McpServerTests : LoggedTest
public async Task Can_Handle_Call_Tool_Requests_With_McpProtocolException_And_Data()
{
const string ErrorMessage = "Resource not found";
const McpErrorCode ErrorCode = McpErrorCode.ResourceNotFound;
const McpErrorCode ErrorCode = McpErrorCode.InvalidParams;
const string ResourceUri = "file:///path/to/resource";
await using var transport = new TestServerTransport();