From d76bc16bd177563a61558d341bb53a6c690c8b81 Mon Sep 17 00:00:00 2001
From: Copilot <198982749+Copilot@users.noreply.github.com>
Date: Fri, 5 Dec 2025 10:39:30 -0800
Subject: [PATCH] Add ResourceNotFound error code (-32002) (#1062)
---
src/ModelContextProtocol.Core/McpErrorCode.cs | 37 ++++++++++++-------
.../Server/McpServerImpl.cs | 2 +-
.../Program.cs | 2 +-
.../Program.cs | 2 +-
...cpServerBuilderExtensionsResourcesTests.cs | 3 +-
.../McpServerResourceRoutingTests.cs | 2 +-
.../McpProtocolExceptionDataTests.cs | 12 +++---
.../Server/McpServerTests.cs | 2 +-
8 files changed, 36 insertions(+), 26 deletions(-)
diff --git a/src/ModelContextProtocol.Core/McpErrorCode.cs b/src/ModelContextProtocol.Core/McpErrorCode.cs
index bd7f625..c6610d6 100644
--- a/src/ModelContextProtocol.Core/McpErrorCode.cs
+++ b/src/ModelContextProtocol.Core/McpErrorCode.cs
@@ -6,12 +6,29 @@ namespace ModelContextProtocol;
public enum McpErrorCode
{
///
- /// Indicates that the JSON received could not be parsed.
+ /// Indicates that the requested resource could not be found.
///
///
- /// This error occurs when the input contains malformed JSON or incorrect syntax.
+ /// This error should be used when a resource URI does not match any available resource on the server.
+ /// It allows clients to distinguish between missing resources and other types of errors.
///
- ParseError = -32700,
+ ResourceNotFound = -32002,
+
+ ///
+ /// Indicates that URL-mode elicitation is required to complete the requested operation.
+ ///
+ ///
+ ///
+ /// This error is returned when a server operation requires additional user input through URL-mode elicitation
+ /// before it can proceed. The error data must include the `data.elicitations` payload describing the pending
+ /// elicitation(s) for the client to present to the user.
+ ///
+ ///
+ /// Common scenarios include OAuth authorization and other out-of-band flows that cannot be completed inside
+ /// the MCP client.
+ ///
+ ///
+ UrlElicitationRequired = -32042,
///
/// Indicates that the JSON payload does not conform to the expected Request object structure.
@@ -58,18 +75,10 @@ public enum McpErrorCode
InternalError = -32603,
///
- /// Indicates that URL-mode elicitation is required to complete the requested operation.
+ /// Indicates that the JSON received could not be parsed.
///
///
- ///
- /// This error is returned when a server operation requires additional user input through URL-mode elicitation
- /// before it can proceed. The error data must include the `data.elicitations` payload describing the pending
- /// elicitation(s) for the client to present to the user.
- ///
- ///
- /// Common scenarios include OAuth authorization and other out-of-band flows that cannot be completed inside
- /// the MCP client.
- ///
+ /// This error occurs when the input contains malformed JSON or incorrect syntax.
///
- UrlElicitationRequired = -32042,
+ ParseError = -32700,
}
diff --git a/src/ModelContextProtocol.Core/Server/McpServerImpl.cs b/src/ModelContextProtocol.Core/Server/McpServerImpl.cs
index 706578d..7cea77f 100644
--- a/src/ModelContextProtocol.Core/Server/McpServerImpl.cs
+++ b/src/ModelContextProtocol.Core/Server/McpServerImpl.cs
@@ -273,7 +273,7 @@ internal sealed partial class McpServerImpl : McpServer
listResourcesHandler ??= (static async (_, __) => new ListResourcesResult());
listResourceTemplatesHandler ??= (static async (_, __) => new ListResourceTemplatesResult());
- readResourceHandler ??= (static async (request, _) => throw new McpProtocolException($"Unknown resource URI: '{request.Params?.Uri}'", McpErrorCode.InvalidParams));
+ readResourceHandler ??= (static async (request, _) => throw new McpProtocolException($"Unknown resource URI: '{request.Params?.Uri}'", McpErrorCode.ResourceNotFound));
subscribeHandler ??= (static async (_, __) => new EmptyResult());
unsubscribeHandler ??= (static async (_, __) => new EmptyResult());
var listChanged = resourcesCapability?.ListChanged;
diff --git a/tests/ModelContextProtocol.TestServer/Program.cs b/tests/ModelContextProtocol.TestServer/Program.cs
index 66fef57..1321c5f 100644
--- a/tests/ModelContextProtocol.TestServer/Program.cs
+++ b/tests/ModelContextProtocol.TestServer/Program.cs
@@ -434,7 +434,7 @@ internal static class Program
}
ResourceContents contents = resourceContents.FirstOrDefault(r => r.Uri == request.Params.Uri)
- ?? throw new McpProtocolException($"Resource not found: '{request.Params.Uri}'", McpErrorCode.InvalidParams);
+ ?? throw new McpProtocolException($"Resource not found: '{request.Params.Uri}'", McpErrorCode.ResourceNotFound);
return new ReadResourceResult
{
diff --git a/tests/ModelContextProtocol.TestSseServer/Program.cs b/tests/ModelContextProtocol.TestSseServer/Program.cs
index 0eb799e..a29c305 100644
--- a/tests/ModelContextProtocol.TestSseServer/Program.cs
+++ b/tests/ModelContextProtocol.TestSseServer/Program.cs
@@ -273,7 +273,7 @@ public class Program
}
ResourceContents? contents = resourceContents.FirstOrDefault(r => r.Uri == request.Params.Uri) ??
- throw new McpProtocolException($"Resource not found: '{request.Params.Uri}'", McpErrorCode.InvalidParams);
+ throw new McpProtocolException($"Resource not found: '{request.Params.Uri}'", McpErrorCode.ResourceNotFound);
return new ReadResourceResult
{
diff --git a/tests/ModelContextProtocol.Tests/Configuration/McpServerBuilderExtensionsResourcesTests.cs b/tests/ModelContextProtocol.Tests/Configuration/McpServerBuilderExtensionsResourcesTests.cs
index da53616..0dc6ed2 100644
--- a/tests/ModelContextProtocol.Tests/Configuration/McpServerBuilderExtensionsResourcesTests.cs
+++ b/tests/ModelContextProtocol.Tests/Configuration/McpServerBuilderExtensionsResourcesTests.cs
@@ -108,7 +108,7 @@ public partial class McpServerBuilderExtensionsResourcesTests : ClientServerTest
};
}
- throw new McpProtocolException($"Resource not found: {request.Params?.Uri}");
+ throw new McpProtocolException($"Resource not found: {request.Params?.Uri}", McpErrorCode.ResourceNotFound);
})
.WithResources();
}
@@ -249,6 +249,7 @@ public partial class McpServerBuilderExtensionsResourcesTests : ClientServerTest
cancellationToken: TestContext.Current.CancellationToken));
Assert.Contains("Resource not found", e.Message);
+ Assert.Equal(McpErrorCode.ResourceNotFound, e.ErrorCode);
}
[Fact]
diff --git a/tests/ModelContextProtocol.Tests/Configuration/McpServerResourceRoutingTests.cs b/tests/ModelContextProtocol.Tests/Configuration/McpServerResourceRoutingTests.cs
index ca82c64..951e065 100644
--- a/tests/ModelContextProtocol.Tests/Configuration/McpServerResourceRoutingTests.cs
+++ b/tests/ModelContextProtocol.Tests/Configuration/McpServerResourceRoutingTests.cs
@@ -36,7 +36,7 @@ public sealed class McpServerResourceRoutingTests(ITestOutputHelper testOutputHe
Assert.Equal("params: a, b, c", ((TextResourceContents)paramsResult.Contents[0]).Text);
var mcpEx = await Assert.ThrowsAsync(async () => await client.ReadResourceAsync("test://params{?a1,a2,a3}", null, TestContext.Current.CancellationToken));
- Assert.Equal(McpErrorCode.InvalidParams, mcpEx.ErrorCode);
+ Assert.Equal(McpErrorCode.ResourceNotFound, mcpEx.ErrorCode);
Assert.Equal("Request failed (remote): Unknown resource URI: 'test://params{?a1,a2,a3}'", mcpEx.Message);
}
}
diff --git a/tests/ModelContextProtocol.Tests/McpProtocolExceptionDataTests.cs b/tests/ModelContextProtocol.Tests/McpProtocolExceptionDataTests.cs
index e4579e6..6816f55 100644
--- a/tests/ModelContextProtocol.Tests/McpProtocolExceptionDataTests.cs
+++ b/tests/ModelContextProtocol.Tests/McpProtocolExceptionDataTests.cs
@@ -33,7 +33,7 @@ public class McpProtocolExceptionDataTests : ClientServerTestBase
switch (toolName)
{
case "throw_with_serializable_data":
- throw new McpProtocolException("Resource not found", (McpErrorCode)(-32002))
+ throw new McpProtocolException("Resource not found", McpErrorCode.ResourceNotFound)
{
Data =
{
@@ -43,7 +43,7 @@ public class McpProtocolExceptionDataTests : ClientServerTestBase
};
case "throw_with_nonserializable_data":
- throw new McpProtocolException("Resource not found", (McpErrorCode)(-32002))
+ throw new McpProtocolException("Resource not found", McpErrorCode.ResourceNotFound)
{
Data =
{
@@ -55,7 +55,7 @@ public class McpProtocolExceptionDataTests : ClientServerTestBase
};
case "throw_with_only_nonserializable_data":
- throw new McpProtocolException("Resource not found", (McpErrorCode)(-32002))
+ throw new McpProtocolException("Resource not found", McpErrorCode.ResourceNotFound)
{
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)(-32002), exception.ErrorCode);
+ Assert.Equal(McpErrorCode.ResourceNotFound, 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)(-32002), exception.ErrorCode);
+ Assert.Equal(McpErrorCode.ResourceNotFound, 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)(-32002), exception.ErrorCode);
+ Assert.Equal(McpErrorCode.ResourceNotFound, 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)
diff --git a/tests/ModelContextProtocol.Tests/Server/McpServerTests.cs b/tests/ModelContextProtocol.Tests/Server/McpServerTests.cs
index da75459..1ab91e1 100644
--- a/tests/ModelContextProtocol.Tests/Server/McpServerTests.cs
+++ b/tests/ModelContextProtocol.Tests/Server/McpServerTests.cs
@@ -681,7 +681,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)(-32002);
+ const McpErrorCode ErrorCode = McpErrorCode.ResourceNotFound;
const string ResourceUri = "file:///path/to/resource";
await using var transport = new TestServerTransport();