-
Notifications
You must be signed in to change notification settings - Fork 562
Add middleware and authz support for server-side handlers #733
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
43deac5 to
58dc73a
Compare
- filters.md cleanup
|
@halter73 Looks fine, basic idea is the same. Like the pipeline building / middleware concept. Had to think a bit if having filters on the option class is the right choice ( i think that typical aspnet core patterns is to have it registered with DI directly and resolving IEnumerable<IFilter...> however i have no knowledge that otherwise indicate if thats better/worse or more correct way to do it. From user perspectiv i think the examples given is clean and understanable. This would be a fine way to do so. Should also be possible to do my sample from #703 adding all kind of filterings ontop using this approach. So to me the thing i would consider is if developers would like to be able to just do services.AddMCPFilter() and have it implement an interface, where thats not directly possibel right now as its handlers being added to the filter array on the options. But for me this works and solve the usecase i was going for. |
|
I really like the way the filters are done from a developer experience perspective. Easy to use and highly customizable. Only thing is that it's not really limited to filtering - you can freely modify, act, and/or add in the middleware. So I'm not sure the name should be "filter". Developers will figure it out soon enough if it is, but I think there's an argument for picking a name that reflects that this can be used for anything really. But this is a great improvement in terms of convenient handler flexibility. |
The nice thing about options is this can be reconfigured per-session in a This is why the middleware pipelines in ASP.NET Core is typically on some sort of builder or options type rather than registered directly as services. For example, the main middleware pipeline is typically stored in ApplicationBuilder._components, Endpoint filters are stored in EndpointBuilder._filterFactories, SignalR Hub filters are stored in HubOptions.HubFilters, and Kestrel connection middleware is stored in ListenOptions._middleware. |
src/ModelContextProtocol.AspNetCore/AuthorizationFilterSetup.cs
Outdated
Show resolved
Hide resolved
# Conflicts: # tests/ModelContextProtocol.Tests/Configuration/McpServerBuilderExtensionsToolsTests.cs
- Update filters.md to use DI and logging - Update filters.md Mention that uncaught McpExceptions get turned into JSON-RPC errors - Added newlines to McpServer between blocks - Remove TODO from AuthorizationFilterSetup now that an issue has been filed
|
I updated this PR to respond to your feedback @stephentoub. I filed follow up issues for some of it. |
|
I'm not sure if this is the correct location to ask this but I feel that I'm missing something. With this implementation it doesn't seem possible to expose/list the tools a user doesn't have access to since the auth filter is required. In my usecase I would like all tools to be listed. But when a tool which needs authorization gets used the unauthorized flow / authorization flow kicks in. With this implementation I don't think that's ever going to happen since the tool is 'hidden' from being used in the first place. => is there a way to influence/adapt how this authorization filter works? |
|
I'd like to repeat @msioen sentiments as well on exposing/listing tools the user doesn't have access to. On other MCP servers such as GitHub, if you are authorized you are allowed to see all tools - though you'll be prevented from using tools if you invoke them with missing permissions. From what I can tell the MCP specification doesn't have any hard rules on this behaviour, though there is mention that the way it was designed was that listing could be dynamic. Additionally, I've been using the latest main branch specifically for the Is there an estimated timeline for when a new NuGet package will be released? |
Why would you ever want to give the model tools that you know it cannot call? I can see if it was to trigger step-up authorization, but that's not something supported by the spec yet. Tools are generally a model-directed capability. The user may be able to see them in many clients, but they are invoked by the model, not the user, and giving the model tools it cannot call it wasteful in tokens and may confuse models. You can use dynamic listing still by doing the auth manually instead of with the attribute - through the session instead of in middleware. Also it is worth noting that client host support for tool list change notifications is not very strong - many clients will not pick it up and will simply cache the tools it got on the initial list call - or will do a tools/list call on every conversation turn. |
|
I support @PederHP here in the statement that it is best to avoid providing tools to an LLM, and this for more reasons that the tokens alone. In Gpt 5 model, we noticed that the reasoning tends to use tools you wouldn't even expect it to call given a certain prompt, but it "just does". Giving it more tools, especially those it can't call anyway, will only make it worse. The implementation as it is now does however lead to one problem, for which I do not know the solution to be honest so maybe somebody has an idea? Thing is that most clients trigger the authentication flow ( the "login popup") based on an authentication error where the MCP library challenges the client. However, if you connect now and call the list tools function, it just returns an empty list all the time because the attributes just filter the list, and an empty list is also a list. So the client assumes all went well, there are simply no tools. Using the MapMcp().RequireAuthentication() solves this issue because then authentication is defacto required and triggered, but, then you cannot use te concept of anonymous functions anymore. I understand this cannot be solved and the solution is that the authentication must be done manually in the client before connecting.... but most tools do not support that and are only reactive to the challenge request. That is an issue where making a server that combines both anonymous and protected functions hard. |
|
I misunderstood the original spec when I asked this question, in the meantime it's indeed clear to me that currently for MCP it's all or nothing with regards to authorization. |
|
Thanks for the perspectives. My original thinking came from what other MCP servers like GitHub and Figma do, where they expose the full capability surface and just block calls when the user isn’t authorized. There are workflow-style cases where it may useful for a model to understand all available actions up front but this isn't the most common case. But with how MCP clients currently behave, surfacing tools the model can’t invoke ends up creating more noise than value. Some MCP clients do provide options to disable specific tools, but not all support this so it isn't really an argument either. Given the specification has no opinion either way I’m happy with the current implementation, I personally didn't have a usecase for my comment as it was more of an observation of other MCP servers. On the topic of |
MCP Server Handler Filters
For each handler type in the MCP Server, there are corresponding
AddXXXFiltermethods inMcpServerBuilderExtensions.csthat allow you to add filters to the handler pipeline. The filters are stored inMcpServerOptions.Filtersand applied during server configuration.Available Filter Methods
The following filter methods are available:
AddListResourceTemplatesFilter- Filter for list resource templates handlersAddListToolsFilter- Filter for list tools handlersAddCallToolFilter- Filter for call tool handlersAddListPromptsFilter- Filter for list prompts handlersAddGetPromptFilter- Filter for get prompt handlersAddListResourcesFilter- Filter for list resources handlersAddReadResourceFilter- Filter for read resource handlersAddCompleteFilter- Filter for completion handlersAddSubscribeToResourcesFilter- Filter for resource subscription handlersAddUnsubscribeFromResourcesFilter- Filter for resource unsubscription handlersAddSetLoggingLevelFilter- Filter for logging level handlersUsage
Filters are functions that take a handler and return a new handler, allowing you to wrap the original handler with additional functionality:
Filter Execution Order
Execution flow:
filter1 -> filter2 -> filter3 -> baseHandler -> filter3 -> filter2 -> filter1[Authorize] attribute support
When using the ASP.NET Core integration (
ModelContextProtocol.AspNetCore), authorization filters are automatically configured byWithHttpTransport()that support[Authorize]and[AllowAnonymous]attributes on MCP server tools, prompts, and resources. Some of the attributes the MCP server automatically respects after this change include:[Authorize]- Requires authentication for access[Authorize(Roles = "RoleName")]- Requires specific roles[Authorize(Policy = "PolicyName")]- Requires specific authorization policies[AllowAnonymous]- Explicitly allows anonymous access (overrides[Authorize])Tool Authorization
Tools can be decorated with authorization attributes to control access:
Class-Level Authorization
You can apply authorization at the class level, which affects all tools in the class:
How Authorization Filters Work
The authorization filters work differently for list operations versus individual operations:
List Operations (ListTools, ListPrompts, ListResources)
For list operations, the filters automatically remove unauthorized items from the results. Users only see tools, prompts, or resources they have permission to access.
Individual Operations (CallTool, GetPrompt, ReadResource)
For individual operations, the filters return authorization errors when access is denied:
CallToolResultwithIsError = trueand an error messageMcpExceptionwith "Access forbidden" messageMcpExceptionwith "Access forbidden" messageSetup Requirements
To use authorization features, you must configure authentication and authorization in your ASP.NET Core application:
Custom Authorization Filters
You can also create custom authorization filters using the filter methods:
RequestContext
Within filters, you have access to:
context.User- The current user'sClaimsPrincipalcontext.Services- The request's service provider for resolving authorization servicescontext.MatchedPrimitive- The matched tool/prompt/resource with its metadata including authorization attributes