MCP Server Setup — ASP.NET Core HTTP Transport
This guide walks through installing Granit.Mcp and Granit.Mcp.Server to expose
your application’s capabilities to AI agents via the Model Context Protocol.
Prerequisites
Section titled “Prerequisites”- .NET 10 application using the Granit module system
Granit.Authorizationconfigured (for RBAC permission checks)- At least one JWT authentication scheme (Bearer, Keycloak, EntraID, etc.)
Installation
Section titled “Installation”-
Add packages
Terminal window dotnet add package Granit.Mcpdotnet add package Granit.Mcp.Server -
Register modules in
Program.csbuilder.AddGranit(granit =>{granit.AddModule<GranitMcpServerModule>();// GranitMcpModule is pulled in automatically via [DependsOn]});builder.AddGranitMcpServer(); -
Map the MCP endpoint
app.MapGranitMcpServer(); -
Create your first tool (see Creating tools)
[McpServerToolType, McpExposed]public sealed class AppointmentMcpTools(IAppointmentReader reader){[McpServerTool, Description("Lists upcoming appointments for the current tenant.")][Authorize(Policy = "Scheduling.Appointments.Read")][McpToolOptions(ReadOnly = true)]public async Task<string> ListUpcomingAsync([Description("Max number of results")] int limit,CancellationToken ct){var items = await reader.GetUpcomingAsync(limit, ct);return JsonSerializer.Serialize(items);}}
The MCP endpoint is now live at /mcp. Connect any MCP client (Claude Desktop,
Cursor, VS Code) to https://your-app/mcp.
What AddGranitMcpServer() wires
Section titled “What AddGranitMcpServer() wires”Behind the scenes, a single call registers:
| Component | SDK / Granit | Purpose |
|---|---|---|
AddMcpServer() | SDK | Core MCP server with Implementation { Name = "Granit" } |
WithHttpTransport() | SDK | Streamable HTTP + legacy SSE on one endpoint |
AddAuthorizationFilters() | SDK | Enables [Authorize] / [AllowAnonymous] on tools |
WithToolsFromAssembly() | SDK | Scans module assemblies for [McpServerToolType] classes |
WithPromptsFromAssembly() | SDK | Scans module assemblies for [McpServerPromptType] classes |
AddListToolsFilter | Granit | Tool visibility (tenant, module scope, explicit) |
AddCallToolFilter | Granit | Output sanitization + audit metrics |
| 3 visibility filters | Granit | ExplicitDiscoveryFilter, TenantAwareVisibilityFilter, ModuleScopeVisibilityFilter |
ErrorSanitizer | Granit | Strips stack traces and connection strings from errors |
McpPermissions | Granit | 5 RBAC permissions auto-registered in GranitAuthorizationModule |
What MapGranitMcpServer() maps
Section titled “What MapGranitMcpServer() maps”// Default: /mcp with auth requiredapp.MapGranitMcpServer();
// Custom route prefix, no admin endpoints:app.MapGranitMcpServer(options =>{ options.RoutePrefix = "/api/mcp"; options.MapAdminEndpoints = false;});| Route | Protocol | Auth | Purpose |
|---|---|---|---|
{prefix} | Streamable HTTP + SSE | Mcp.Server.Access | MCP protocol endpoint |
Configuration reference
Section titled “Configuration reference”All options are bindable from appsettings.json:
{ "Mcp": { "ServerName": "MyApp", "ServerVersion": "2.1.0", "ToolDiscovery": "Explicit", "EnableTenantFiltering": true, "MaxResponseSizeBytes": 51200, "Server": { "RoutePrefix": "/mcp", "RequireAuthentication": true, "MapAdminEndpoints": true, "AdminTagName": "MCP Admin", "EnabledModules": [] } }}GranitMcpOptions (section: Mcp)
| Property | Type | Default | Description |
|---|---|---|---|
ServerName | string | "Granit" | Server name in MCP protocol info |
ServerVersion | string? | null | Server version in MCP protocol info |
ToolDiscovery | McpToolDiscoveryMode | Explicit | Explicit requires [McpExposed]; Auto discovers all tools |
EnableTenantFiltering | bool | true | Enable [McpTenantScope] filtering |
MaxResponseSizeBytes | int | 51200 | Response truncation threshold (0 = disabled) |
GranitMcpServerOptions (section: Mcp:Server)
| Property | Type | Default | Description |
|---|---|---|---|
RoutePrefix | string | "/mcp" | HTTP endpoint route |
RequireAuthentication | bool | true | Enforce Mcp.Server.Access permission |
MapAdminEndpoints | bool | true | Expose tool listing admin API |
AdminTagName | string | "MCP Admin" | OpenAPI tag for admin endpoints |
EnabledModules | HashSet<string> | [] | Module whitelist (empty = all modules) |
RBAC permissions
Section titled “RBAC permissions”McpPermissionDefinitionProvider auto-registers these permissions in Granit’s
authorization system:
| Constant | Value | Purpose |
|---|---|---|
McpPermissions.Server.Access | Mcp.Server.Access | Access the MCP endpoint |
McpPermissions.Tools.Read | Mcp.Tools.Read | List tools (admin API) |
McpPermissions.Tools.Execute | Mcp.Tools.Execute | Execute MCP tools |
McpPermissions.Resources.Read | Mcp.Resources.Read | Read MCP resources |
McpPermissions.Prompts.Read | Mcp.Prompts.Read | Read MCP prompts |
Assign these permissions to roles via the Granit Authorization module, then use
them in [Authorize(Policy = "...")] on tool classes and methods.
Connecting MCP clients
Section titled “Connecting MCP clients”Add to claude_desktop_config.json:
{ "mcpServers": { "my-app": { "type": "url", "url": "https://localhost:5001/mcp", "headers": { "Authorization": "Bearer <your-jwt-token>" } } }}Add to .vscode/mcp.json:
{ "servers": { "my-app": { "type": "http", "url": "https://localhost:5001/mcp", "headers": { "Authorization": "Bearer <your-jwt-token>" } } }}Add to Cursor settings > MCP Servers:
{ "my-app": { "url": "https://localhost:5001/mcp", "headers": { "Authorization": "Bearer <your-jwt-token>" } }}See also
Section titled “See also”- Creating MCP tools — attribute reference, DI injection, return types
- Security & data protection — authorization, sanitization, DPoP
- Tool visibility — discovery modes, tenant scope, module scope
- MCP overview — architecture and design principles