Replies: 3 comments
-
You can return different set of tool to different clients based on their scopes (for example) Reuse [McpServerToolType]
[RequiredScope("Mcp.Extra")]
public class McpToolUser(IUserContext userContext)
{
...
} Implement custom builder
.Services.AddMcpServer()
.WithToolsFromAssembly()
.WithHttpTransport(options =>
{
var toolsFilter = McpToolsFilter.FromAssembly();
options.ConfigureSessionOptions = (httpContext, mcpServerOptions, _) =>
{
toolsFilter.ApplyFilter(httpContext, mcpServerOptions);
return Task.CompletedTask;
};
}); reference implementation for filtering public class McpToolsFilter(Dictionary<string, string[]> requiredScopesByTool)
{
public static McpToolsFilter FromAssembly(Assembly? toolAssembly = null)
{
var assembly = toolAssembly ?? Assembly.GetExecutingAssembly();
var mcpToolTypes = assembly
.GetTypes()
.Where(t => t.GetCustomAttribute<McpServerToolTypeAttribute>() is not null);
var requiredScopesByTool = new Dictionary<string, string[]>();
foreach (var type in mcpToolTypes)
{
var requiredScopeAttribute = type.GetCustomAttribute<RequiredScopeAttribute>();
var requiredScopes = requiredScopeAttribute?.AcceptedScope ?? [];
foreach (var method in type.GetMethods())
{
var toolAttr = method.GetCustomAttribute<McpServerToolAttribute>();
if (toolAttr is not null)
{
requiredScopesByTool.Add(toolAttr.Name!, requiredScopes);
}
}
}
return new McpToolsFilter(requiredScopesByTool);
}
public void ApplyFilter(HttpContext httpContext, McpServerOptions options)
{
ArgumentNullException.ThrowIfNull(httpContext);
ArgumentNullException.ThrowIfNull(options);
var scopeClaim = httpContext.User.FindFirst(ClaimConstants.Scope)?.Value ?? "";
var userScopes = scopeClaim.Split(' ', StringSplitOptions.RemoveEmptyEntries);
var tools = options.Capabilities?.Tools;
if (tools?.ToolCollection is null)
{
return;
}
var filteredTools = new McpServerPrimitiveCollection<McpServerTool>();
foreach (var tool in tools.ToolCollection)
{
if (!requiredScopesByTool.TryGetValue(tool.ProtocolTool.Name, out var requiredScopes))
{
requiredScopes = [];
}
if (requiredScopes.Length == 0 || requiredScopes.Any(rs => userScopes.Contains(rs)))
{
filteredTools.Add(tool);
}
}
tools.ToolCollection = filteredTools; // <-- Customized tool collection
}
} |
Beta Was this translation helpful? Give feedback.
-
In addition to what @sergey-tihon writes, you can also do as in the sample in this PR: #724 |
Beta Was this translation helpful? Give feedback.
-
Yeah, was thinking of something like @sergey-tihon mentioned. Would be nice if linking the tools to a server with an attribute was possible. app.Map("/public", p => p.UseMCP("public-mcp").AllowAnonymous()
app.Map("/internal", intern => intern.UseMCP("internal-mcp").RequireUser() [McpServerToolType]
[LinkedMcpServers(["public-mcp"])]
public class McpToolPublic { }
[McpServerToolType]
[LinkedMcpServers(["internal-mcp"])]
public class ToolForInternalsOnly(IUserContext user) { } NB! I'm not solely thinking about servers with different authorization needs.They could also just be two different sets of tools and capabilities: app.Map("/sales", p => p.UseMCP("public-sales")
app.Map("/billing", p => p.UseMCP("public-billing") |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Pre-submission Checklist
Your Idea
It would be nice if I could somehow differentiate what MCP tools/prompts/resources are linked to what MCP-endpoint within a single host.
Imagine I host an ASP.NET Core host at my.domain.com.
From the same host, I can have two MCP endpoints running:
my.domain.com/public/sse
=> publicly available MCP server, public tools for the external world, customersmy.domain.om/internal/sse
=> internal tools, in-house specific for employees (setup with auth, for example an API-key or similar).Workarounds:
Scope
Beta Was this translation helpful? Give feedback.
All reactions