Minimal APIs

Route handlers publish the same way controller actions do, through an endpoint convention instead of an attribute.

app.MapGet("/customers/{id}", (int id, ICustomerService customers) => customers.Get(id))
   .McpTool();

app.MapPost("/orders", (CreateOrder order) => ...)
   .McpTool("orders_create", "Places a new order.");

Everything works exactly as it does for controllers, because the machinery is the same: a tool call is replayed as a synthetic HTTP request through the whole pipeline, so RequireAuthorization(), [Authorize], endpoint filters, rate limiting and validation all keep running, and ToolVisibility reads the endpoint's own authorization metadata when tailoring tools/list to the caller. An application without any controllers at all — AddNabuMcp() plus UseNabuMcp() on a plain WebApplication — is fully supported.

How inputs are inferred

Inputs are inferred with Minimal API binding rules, not MVC's:

Handler parameterBecomes
Route token match, [FromRoute]A URL segment.
string, primitives, parsables, and collections of them; [FromQuery]A query-string entry.
Complex types (and collections of them), [FromBody]The JSON request body, flattened like [FromBody].
[FromHeader]A request header (opt in with ExposeHeaderParameters).
Services (per IServiceProviderIsService), [FromServices], [FromKeyedServices]Skipped — resolved by the framework.
HttpContext, CancellationToken, ClaimsPrincipal, Stream, PipeReader, BindAsync typesSkipped — materialized from the request itself.

One real difference between the stacks is preserved rather than papered over: Minimal APIs answer 400 when a non-nullable parameter without a default value is missing, so such a parameter is required in the tool schema — where MVC model binding would have silently used the type's default.

The other [McpTool] surfaces work here too

Tool naming

Without an explicit name, tools are named from the endpoint name (.WithName(...)), the handler's method name when it is a real method rather than a lambda, or the verb and route — GET /customers/{id} becomes customers_get_by_id. NabuMcpOptions.ToolNameFactory sees these through the same McpToolNamingContext it sees controllers through.

Forms and [AsParameters]

[AsParameters] surface types are expanded member by member — constructor parameters and settable properties each get the same binding inference they would as a top-level handler parameter. Form binding works too: an IFormFile becomes a tool argument carrying the content as base64 (data, plus optional fileName and contentType), and the invoker replays it as a real multipart/form-data request, so the endpoint's own form binding, validation and size limits apply unchanged. On .NET 8+, form endpoints demand antiforgery by default — a tool call cannot carry an antiforgery token, so such endpoints need .DisableAntiforgery() exactly as any non-browser client does.

Current limitations

Minimal API discovery needs endpoint routing, so it exists on the modern targets only, not on the netstandard2.0 asset for ASP.NET Core 2.x.