Turn your ASP.NET Core API controllers into MCP (Model Context Protocol) tools with simple attributes. Includes role-based authorization and tool visibility filtering.
| Component | Description |
|---|---|
| Zero.Mcp.Extensions | NuGet library - add MCP capabilities to any ASP.NET Core API |
| McpPoc.Api | Demo API showing full integration with Keycloak authentication |
- Attribute-based tool registration - Mark controllers with
[McpServerToolType]and methods with[McpServerTool] - ActionResult unwrapping - Automatic conversion of
ActionResult<T>responses - Authorization integration - Full support for
[Authorize]policies and[AllowAnonymous] - Role-based tool filtering -
tools/listonly returns tools the user can invoke - Keycloak integration - JWT authentication with role mapping
dotnet add package Zero.Mcp.Extensions[ApiController]
[Route("api/[controller]")]
[McpServerToolType] // Enable MCP for this controller
[Authorize]
public class UsersController : ControllerBase
{
[HttpGet("{id}")]
[McpServerTool] // Expose as MCP tool
[Description("Gets a user by ID")]
public async Task<ActionResult<User>> GetById(int id) { ... }
[HttpPost]
[McpServerTool]
[Description("Creates a new user")]
[Authorize(Policy = "RequireMember")] // Role-based access
public async Task<ActionResult<User>> Create(CreateUserRequest request) { ... }
}// Program.cs
builder.Services.AddScoped<IAuthForMcpSupplier, YourAuthSupplier>();
builder.Services.AddScoped<IUserRoleResolver, YourRoleResolver>();
builder.Services.AddZeroMcpExtensions(options =>
{
options.RequireAuthentication = true;
options.UseAuthorization = true;
options.FilterToolsByPermissions = true; // Hide unauthorized tools
options.McpEndpointPath = "/mcp";
});app.MapZeroMcp();That's it! Your API now speaks MCP at /mcp.
Users only see tools they're authorized to use:
| User Role | Visible Tools |
|---|---|
| Viewer | get_by_id, get_all (read-only) |
| Member | Above + create |
| Manager | Above + update |
| Admin | All tools including promote_to_manager |
- .NET 9.0 SDK
- Docker & Docker Compose
cd docker
docker-compose up -dThis starts:
- Keycloak (localhost:8080) - Identity provider
- PostgreSQL - Database for Keycloak
dotnet run --project src/McpPoc.ApiAPI available at http://localhost:5001
# As admin
TOKEN=$(./get-token.sh admin admin123)
# As member
TOKEN=$(./get-token.sh alice@example.com alice123)
# As viewer
TOKEN=$(./get-token.sh viewer viewer123)# List available tools
curl -X POST http://localhost:5001/mcp \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"tools/list","id":1}'| Username | Password | Role | Can Do |
|---|---|---|---|
viewer |
viewer123 |
Viewer | Read only |
alice@example.com |
alice123 |
Member | Read + Create |
bob@example.com |
bob123 |
Manager | Read + Create + Update |
carol@example.com |
carol123 |
Admin | Everything |
builder.Services.AddZeroMcpExtensions(options =>
{
// Require JWT authentication (default: true)
options.RequireAuthentication = true;
// Enforce [Authorize] policies (default: true)
options.UseAuthorization = true;
// Filter tools/list by user permissions (default: true)
options.FilterToolsByPermissions = true;
// MCP endpoint path (default: "/mcp")
options.McpEndpointPath = "/mcp";
// Assembly to scan for tools (default: calling assembly)
options.ToolAssembly = typeof(MyController).Assembly;
// JSON serialization options
options.SerializerOptions = new JsonSerializerOptions { ... };
});├── src/
│ ├── Zero.Mcp.Extensions/ # NuGet library
│ └── McpPoc.Api/ # Demo API
├── tests/
│ ├── Zero.Mcp.Extensions.Tests/
│ └── McpPoc.Api.Tests/
├── docker/ # Keycloak + Postgres
└── docs/ # Additional documentation
- Zero.Mcp.Extensions README - Library details
- Users and Permissions - Role system explained
- MCP Authorization Guide - Deep dive
Apache 2.0 - Ladislav Sopko / 0ics srl