Reauthorize tools changed by call-tool filters (#1737)
Copilot-Session: 5feb0906-73fc-4e1a-8cf2-c9a99c588487
This commit is contained in:
parent
be55536a91
commit
c7e4b820fe
3 changed files with 177 additions and 15 deletions
|
|
@ -20,6 +20,7 @@ internal sealed class AuthorizationFilterSetup(
|
|||
public void Configure(McpServerOptions options)
|
||||
{
|
||||
ConfigureListToolsFilter(options);
|
||||
ConfigureOrdinaryCallToolFilter(options);
|
||||
|
||||
ConfigureListResourcesFilter(options);
|
||||
ConfigureListResourceTemplatesFilter(options);
|
||||
|
|
@ -85,6 +86,22 @@ internal sealed class AuthorizationFilterSetup(
|
|||
});
|
||||
}
|
||||
|
||||
private void ConfigureOrdinaryCallToolFilter(McpServerOptions options)
|
||||
{
|
||||
options.Filters.Request.CallToolFilters.Add(next => async (context, cancellationToken) =>
|
||||
{
|
||||
var authResult = await GetAuthorizationResultAsync(context.User, context.MatchedPrimitive, context.Services, context);
|
||||
if (!authResult.Succeeded)
|
||||
{
|
||||
throw new McpProtocolException("Access forbidden: This tool requires authorization.", McpErrorCode.InvalidRequest);
|
||||
}
|
||||
|
||||
context.Items[AuthorizationFilterInvokedKey] = true;
|
||||
|
||||
return await next(context, cancellationToken);
|
||||
});
|
||||
}
|
||||
|
||||
private void ConfigureCallToolFilter(McpServerOptions options)
|
||||
{
|
||||
#pragma warning disable MCPEXP002 // Authorization must run in the alternate-result pipeline before task dispatch.
|
||||
|
|
|
|||
|
|
@ -58,6 +58,9 @@ public static class HttpMcpServerBuilderExtensions
|
|||
/// authorization attributes such as <see cref="AuthorizeAttribute"/>
|
||||
/// and <see cref="AllowAnonymousAttribute"/>. Tool authorization runs in the alternate-result pipeline before
|
||||
/// the Tasks extension dispatches background execution, so an unauthorized tool call does not create a task.
|
||||
/// Each call to this method also adds an ordinary call-tool authorization checkpoint at that point in the filter
|
||||
/// pipeline. Call this method again after any call-tool filter that changes the matched tool or user to authorize
|
||||
/// the replacement using the updated context.
|
||||
/// </remarks>
|
||||
public static IMcpServerBuilder AddAuthorizationFilters(this IMcpServerBuilder builder)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@ public static class McpTasksBuilderExtensions
|
|||
/// Tasks are implemented as an alternate-result call-tool filter. Alternate-result filters registered before
|
||||
/// the Tasks filter run before task creation. Filters registered after it, along with all ordinary call-tool
|
||||
/// filters, run in the background before the tool.
|
||||
/// Register Tasks before configuring ordinary call-tool filters.
|
||||
/// </remarks>
|
||||
/// <param name="builder">The server builder.</param>
|
||||
/// <param name="store">The task store.</param>
|
||||
|
|
@ -120,13 +119,6 @@ public static class McpTasksBuilderExtensions
|
|||
Handler = HandleCancelTask,
|
||||
});
|
||||
|
||||
if (options.Filters.Request.CallToolFilters.Count > 0)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"{nameof(WithTasks)} must be configured before ordinary call-tool filters because " +
|
||||
"the Tasks filter must execute outside the ordinary call-tool pipeline.");
|
||||
}
|
||||
|
||||
// Use a filter rather than a handler so it wraps around Core's tool dispatch.
|
||||
// This ensures it intercepts tool calls BEFORE the tool is invoked, allowing
|
||||
// it to spawn background execution and return the task alternate immediately.
|
||||
|
|
|
|||
Loading…
Reference in a new issue