Propagate tool call exceptions through filters (#844)

* Add McpProtocolException specifically for JSON-RPC errors
This commit is contained in:
Stephen Halter 2025-10-14 21:10:55 -05:00 • committed by GitHub
commit 3255adb4d6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -36,7 +36,7 @@ public class AuthorizeAttributeTests(ITestOutputHelper testOutputHelper) : Kestr
var client = await ConnectAsync();
var exception = await Assert.ThrowsAsync<McpException>(async () =>
var exception = await Assert.ThrowsAsync<McpProtocolException>(async () =>
await client.CallToolAsync(
"authorized_tool",
new Dictionary<string, object?> { ["message"] = "test" },
@ -101,7 +101,7 @@ public class AuthorizeAttributeTests(ITestOutputHelper testOutputHelper) : Kestr
var client = await ConnectAsync();
var exception = await Assert.ThrowsAsync<McpException>(async () =>
var exception = await Assert.ThrowsAsync<McpProtocolException>(async () =>
await client.CallToolAsync(
"admin_tool",
new Dictionary<string, object?> { ["message"] = "test" },
@ -188,7 +188,7 @@ public class AuthorizeAttributeTests(ITestOutputHelper testOutputHelper) : Kestr
var client = await ConnectAsync();
var exception = await Assert.ThrowsAsync<McpException>(async () =>
var exception = await Assert.ThrowsAsync<McpProtocolException>(async () =>
await client.GetPromptAsync(
"authorized_prompt",
new Dictionary<string, object?> { ["message"] = "test" },
@ -235,7 +235,7 @@ public class AuthorizeAttributeTests(ITestOutputHelper testOutputHelper) : Kestr
var client = await ConnectAsync();
var exception = await Assert.ThrowsAsync<McpException>(async () =>
var exception = await Assert.ThrowsAsync<McpProtocolException>(async () =>
await client.ReadResourceAsync(
"resource://authorized",
cancellationToken: TestContext.Current.CancellationToken));
@ -277,7 +277,7 @@ public class AuthorizeAttributeTests(ITestOutputHelper testOutputHelper) : Kestr
await using var app = await StartServerWithoutAuthFilters(builder => builder.WithTools<AuthorizationTestTools>());
var client = await ConnectAsync();
var exception = await Assert.ThrowsAsync<McpException>(async () =>
var exception = await Assert.ThrowsAsync<McpProtocolException>(async () =>
await client.ListToolsAsync(cancellationToken: TestContext.Current.CancellationToken));
Assert.Equal("Request failed (remote): An error occurred.", exception.Message);
@ -289,21 +289,23 @@ public class AuthorizeAttributeTests(ITestOutputHelper testOutputHelper) : Kestr
}
[Fact]
public async Task CallTool_WithoutAuthFilters_ThrowsInvalidOperationException()
public async Task CallTool_WithoutAuthFilters_ReturnsError()
{
_mockLoggerProvider.LogMessages.Clear();
await using var app = await StartServerWithoutAuthFilters(builder => builder.WithTools<AuthorizationTestTools>());
var client = await ConnectAsync();
var exception = await Assert.ThrowsAsync<McpException>(async () =>
await client.CallToolAsync(
var toolResult = await client.CallToolAsync(
"authorized_tool",
new Dictionary<string, object?> { ["message"] = "test" },
cancellationToken: TestContext.Current.CancellationToken));
cancellationToken: TestContext.Current.CancellationToken);
Assert.Equal("Request failed (remote): An error occurred.", exception.Message);
Assert.True(toolResult.IsError);
var errorContent = Assert.IsType<TextContentBlock>(Assert.Single(toolResult.Content));
Assert.Equal("An error occurred invoking 'authorized_tool'.", errorContent.Text);
Assert.Contains(_mockLoggerProvider.LogMessages, log =>
log.LogLevel == LogLevel.Warning &&
log.LogLevel == LogLevel.Error &&
log.Exception is InvalidOperationException &&
log.Exception.Message.Contains("Authorization filter was not invoked for tools/call operation") &&
log.Exception.Message.Contains("Ensure that AddAuthorizationFilters() is called"));
@ -316,7 +318,7 @@ public class AuthorizeAttributeTests(ITestOutputHelper testOutputHelper) : Kestr
await using var app = await StartServerWithoutAuthFilters(builder => builder.WithPrompts<AuthorizationTestPrompts>());
var client = await ConnectAsync();
var exception = await Assert.ThrowsAsync<McpException>(async () =>
var exception = await Assert.ThrowsAsync<McpProtocolException>(async () =>
await client.ListPromptsAsync(cancellationToken: TestContext.Current.CancellationToken));
Assert.Equal("Request failed (remote): An error occurred.", exception.Message);
@ -334,7 +336,7 @@ public class AuthorizeAttributeTests(ITestOutputHelper testOutputHelper) : Kestr
await using var app = await StartServerWithoutAuthFilters(builder => builder.WithPrompts<AuthorizationTestPrompts>());
var client = await ConnectAsync();
var exception = await Assert.ThrowsAsync<McpException>(async () =>
var exception = await Assert.ThrowsAsync<McpProtocolException>(async () =>
await client.GetPromptAsync(
"authorized_prompt",
new Dictionary<string, object?> { ["message"] = "test" },
@ -355,7 +357,7 @@ public class AuthorizeAttributeTests(ITestOutputHelper testOutputHelper) : Kestr
await using var app = await StartServerWithoutAuthFilters(builder => builder.WithResources<AuthorizationTestResources>());
var client = await ConnectAsync();
var exception = await Assert.ThrowsAsync<McpException>(async () =>
var exception = await Assert.ThrowsAsync<McpProtocolException>(async () =>
await client.ListResourcesAsync(cancellationToken: TestContext.Current.CancellationToken));
Assert.Equal("Request failed (remote): An error occurred.", exception.Message);
@ -373,7 +375,7 @@ public class AuthorizeAttributeTests(ITestOutputHelper testOutputHelper) : Kestr
await using var app = await StartServerWithoutAuthFilters(builder => builder.WithResources<AuthorizationTestResources>());
var client = await ConnectAsync();
var exception = await Assert.ThrowsAsync<McpException>(async () =>
var exception = await Assert.ThrowsAsync<McpProtocolException>(async () =>
await client.ReadResourceAsync(
"resource://authorized",
cancellationToken: TestContext.Current.CancellationToken));
@ -393,7 +395,7 @@ public class AuthorizeAttributeTests(ITestOutputHelper testOutputHelper) : Kestr
await using var app = await StartServerWithoutAuthFilters(builder => builder.WithResources<AuthorizationTestResources>());
var client = await ConnectAsync();
var exception = await Assert.ThrowsAsync<McpException>(async () =>
var exception = await Assert.ThrowsAsync<McpProtocolException>(async () =>
await client.ListResourceTemplatesAsync(cancellationToken: TestContext.Current.CancellationToken));
Assert.Equal("Request failed (remote): An error occurred.", exception.Message);