Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
12 changes: 10 additions & 2 deletions src/ModelContextProtocol.Core/McpErrorCode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,16 @@ public enum McpErrorCode
/// Indicates that the requested resource could not be found.
/// </summary>
/// <remarks>
/// 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.
/// <para>
/// Deprecated per SEP-2164 (MCP spec 2026-06-30). Use <see cref="InvalidParams"/> (-32602) instead,
/// which is the standard JSON-RPC code for unknown or unresolvable resource URIs.
/// </para>
/// <para>
/// This value (-32002) is retained for backward compatibility with pre-2026-06-30 clients and servers,
/// but new code should use <see cref="InvalidParams"/>.
/// </para>
/// </remarks>
[Obsolete("ResourceNotFound (-32002) is deprecated per SEP-2164. Use McpErrorCode.InvalidParams (-32602) instead.")]
ResourceNotFound = -32002,

/// <summary>
Expand Down Expand Up @@ -65,6 +72,7 @@ public enum McpErrorCode
/// <list type="bullet">
/// <item><description><b>Tools</b>: Unknown tool name or invalid protocol-level tool arguments.</description></item>
/// <item><description><b>Prompts</b>: Unknown prompt name or missing required protocol-level arguments.</description></item>
/// <item><description><b>Resources</b>: Unknown or unresolvable resource URI.</description></item>
/// <item><description><b>Pagination</b>: Invalid or expired cursor values.</description></item>
/// <item><description><b>Logging</b>: Invalid log level.</description></item>
/// <item><description><b>Tasks</b>: Invalid or nonexistent task ID or invalid cursor.</description></item>
Expand Down
2 changes: 1 addition & 1 deletion src/ModelContextProtocol.Core/McpProtocolException.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ public McpProtocolException(string message, Exception? innerException, McpErrorC
/// <item><description>-32700: Parse error - Invalid JSON received</description></item>
/// <item><description>-32600: Invalid request - The JSON is not a valid Request object</description></item>
/// <item><description>-32601: Method not found - The method does not exist or is not available</description></item>
/// <item><description>-32602: Invalid params - Malformed request or unknown primitive name (tool/prompt/resource)</description></item>
/// <item><description>-32602: Invalid params - Malformed request, unknown primitive name (tool/prompt/resource), or unresolvable resource URI</description></item>
/// <item><description>-32603: Internal error - Internal JSON-RPC error</description></item>
/// </list>
/// </remarks>
Expand Down
2 changes: 1 addition & 1 deletion src/ModelContextProtocol.Core/Server/McpServerImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -421,7 +421,7 @@ subscribeHandler is null && unsubscribeHandler is null && resources is null &&

listResourcesHandler ??= (static async (_, __) => new ListResourcesResult());
listResourceTemplatesHandler ??= (static async (_, __) => new ListResourceTemplatesResult());
readResourceHandler ??= (static async (request, _) => throw new McpProtocolException($"Unknown resource URI: '{request.Params?.Uri}'", McpErrorCode.ResourceNotFound));
readResourceHandler ??= (static async (request, _) => throw new McpProtocolException($"Unknown resource URI: '{request.Params?.Uri}'", McpErrorCode.InvalidParams));
subscribeHandler ??= (static async (_, __) => new EmptyResult());
unsubscribeHandler ??= (static async (_, __) => new EmptyResult());
var listChanged = resourcesCapability?.ListChanged;
Expand Down
2 changes: 1 addition & 1 deletion tests/ModelContextProtocol.TestServer/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -503,7 +503,7 @@ private static void ConfigureResources(McpServerOptions options)
}

ResourceContents contents = resourceContents.FirstOrDefault(r => r.Uri == request.Params.Uri)
?? throw new McpProtocolException($"Resource not found: '{request.Params.Uri}'", McpErrorCode.ResourceNotFound);
?? throw new McpProtocolException($"Resource not found: '{request.Params.Uri}'", McpErrorCode.InvalidParams);

return new ReadResourceResult
{
Expand Down
2 changes: 1 addition & 1 deletion tests/ModelContextProtocol.TestSseServer/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,7 @@ static CreateMessageRequestParams CreateRequestSamplingParams(string context, st
}

ResourceContents? contents = resourceContents.FirstOrDefault(r => r.Uri == request.Params.Uri) ??
throw new McpProtocolException($"Resource not found: '{request.Params.Uri}'", McpErrorCode.ResourceNotFound);
throw new McpProtocolException($"Resource not found: '{request.Params.Uri}'", McpErrorCode.InvalidParams);

return new ReadResourceResult
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ protected override void ConfigureServices(ServiceCollection services, IMcpServer
};
}

throw new McpProtocolException($"Resource not found: {request.Params.Uri}", McpErrorCode.ResourceNotFound);
throw new McpProtocolException($"Resource not found: {request.Params.Uri}", McpErrorCode.InvalidParams);
})
.WithResources<SimpleResources>();
}
Expand Down Expand Up @@ -317,7 +317,7 @@ public async Task Throws_Exception_On_Unknown_Resource()
cancellationToken: TestContext.Current.CancellationToken));

Assert.Contains("Resource not found", e.Message);
Assert.Equal(McpErrorCode.ResourceNotFound, e.ErrorCode);
Assert.Equal(McpErrorCode.InvalidParams, e.ErrorCode);
}

[Fact]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ private async Task AssertNoMatchAsync(
var ex = await Assert.ThrowsAsync<McpProtocolException>(async () =>
await client.ReadResourceAsync(uri, null, TestContext.Current.CancellationToken));

Assert.Equal(McpErrorCode.ResourceNotFound, ex.ErrorCode);
Assert.Equal(McpErrorCode.InvalidParams, ex.ErrorCode);
}

/// <summary>
Expand Down Expand Up @@ -92,7 +92,7 @@ public async Task MultipleTemplatedResources_MatchesCorrectResource()

// Literal template braces in URI should not match (template literal is not a valid URI)
var mcpEx = await Assert.ThrowsAsync<McpProtocolException>(async () => await client.ReadResourceAsync("test://params{?a1,a2,a3}", null, TestContext.Current.CancellationToken));
Assert.Equal(McpErrorCode.ResourceNotFound, mcpEx.ErrorCode);
Assert.Equal(McpErrorCode.InvalidParams, mcpEx.ErrorCode);
Assert.Equal("Request failed (remote): Unknown resource URI: 'test://params{?a1,a2,a3}'", mcpEx.Message);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ protected override void ConfigureServices(ServiceCollection services, IMcpServer
switch (toolName)
{
case "throw_with_serializable_data":
throw new McpProtocolException("Resource not found", McpErrorCode.ResourceNotFound)
throw new McpProtocolException("Resource not found", McpErrorCode.InvalidParams)
{
Data =
{
Expand All @@ -43,7 +43,7 @@ protected override void ConfigureServices(ServiceCollection services, IMcpServer
};

case "throw_with_nonserializable_data":
throw new McpProtocolException("Resource not found", McpErrorCode.ResourceNotFound)
throw new McpProtocolException("Resource not found", McpErrorCode.InvalidParams)
{
Data =
{
Expand All @@ -55,7 +55,7 @@ protected override void ConfigureServices(ServiceCollection services, IMcpServer
};

case "throw_with_only_nonserializable_data":
throw new McpProtocolException("Resource not found", McpErrorCode.ResourceNotFound)
throw new McpProtocolException("Resource not found", McpErrorCode.InvalidParams)
{
Data =
{
Expand All @@ -79,7 +79,7 @@ public async Task Exception_With_Serializable_Data_Propagates_To_Client()
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
Expand Down Expand Up @@ -113,7 +113,7 @@ public async Task Exception_With_NonSerializable_Data_Still_Propagates_Error_To_
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;
Expand Down Expand Up @@ -142,7 +142,7 @@ public async Task Exception_With_Only_NonSerializable_Data_Still_Propagates_Erro
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)
Expand Down
2 changes: 1 addition & 1 deletion tests/ModelContextProtocol.Tests/Server/McpServerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1033,7 +1033,7 @@ await transport.SendMessageAsync(
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();
Expand Down
Loading