![]() |
VOOZH | about |
dotnet add package McpIt --version 1.3.0
NuGet\Install-Package McpIt -Version 1.3.0
<PackageReference Include="McpIt" Version="1.3.0" />
<PackageVersion Include="McpIt" Version="1.3.0" />Directory.Packages.props
<PackageReference Include="McpIt" />Project file
paket add McpIt --version 1.3.0
#r "nuget: McpIt, 1.3.0"
#:package McpIt@1.3.0
#addin nuget:?package=McpIt&version=1.3.0Install as a Cake Addin
#tool nuget:?package=McpIt&version=1.3.0Install as a Cake Tool
<div align="center">
<img src="assets/icon.png" width="120" alt="McpIt" />
You already have a Web API. Expose it to AI agents as MCP tools at build time: one [McpTool] attribute, reflection-free for read tools, zero proxy, zero hand-written server.
๐ NuGet
๐ Downloads
๐ CI
</div>
McpIt is a build-time Roslyn source generator that turns your existing ASP.NET Core endpoints into Model Context Protocol tools. The official MCP C# SDK makes you hand-write [McpServerTool] classes for every operation you want an agent to use. McpIt generates those tool classes for you from the controller actions and minimal-API endpoints you already have: you mark an action with [McpTool], and at compile time McpIt emits the MCP tool on top of the official ModelContextProtocol.AspNetCore SDK. No runtime reflection for tool discovery, no internal HTTP self-call, no separate server to write and keep in sync.
dotnet add package McpIt
McpIt brings in the official MCP SDK transitively, so you do not need to add ModelContextProtocol.AspNetCore yourself. The [McpTool] and [McpToolOutput] attributes ship in the small McpIt.Abstractions package, which also comes in transitively.
Minimal setup in Program.cs:
using McpIt;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddMcpServer()
.WithHttpTransport(o => o.Stateless = true)
.WithToolsFromAssembly(); // discovers the tools McpIt generated
builder.Services.AddMcpEndpoints(); // in-process invoker for the generated tools
var app = builder.Build();
app.MapControllers();
app.MapMcp("/mcp"); // MCP server at /mcp; your API stays where it is
app.Run();
Your REST API runs unchanged, and an MCP server is now served at /mcp.
A normal ASP.NET Core controller action:
[ApiController]
[Route("orders")]
public class OrdersController : ControllerBase
{
[HttpGet("{id}")]
public string GetOrder(int id) => $"order-{id}";
}
The same action, exposed to AI agents. Add one attribute (and a <summary> for the description):
using McpIt;
using Microsoft.AspNetCore.Mvc;
[ApiController]
[Route("orders")]
public class OrdersController : ControllerBase
{
/// <summary>Gets an order by its id.</summary>
[HttpGet("{id}")]
[McpTool(Name = "getOrder")]
public string GetOrder(int id) => $"order-{id}";
}
At compile time McpIt generates an MCP tool class for getOrder. It reads the action's HTTP verb, route template, parameters, and description, and builds the tool's input schema and safety hints from them. The id route parameter becomes a typed tool argument, and the <summary> becomes the tool description. Nothing else changes in your project.
To an MCP client, a tools/list call now returns the tool:
{
"tools": [
{
"name": "getOrder",
"description": "Gets an order by its id.",
"inputSchema": {
"type": "object",
"properties": { "id": { "type": "integer" } },
"required": ["id"]
},
"annotations": { "readOnlyHint": true, "idempotentHint": true }
}
]
}
When the agent calls getOrder, McpIt invokes your real GetOrder action in-process, so your routing, model binding, validation, and business logic all run exactly as they do for an HTTP caller. There is no second HTTP request and no reflection at runtime for the call path (see the AOT note for request-body serialization).
Without McpIt you write and maintain a parallel [McpServerTool] class for every endpoint you want an agent to reach, keeping its parameters, schema, and description in sync with the controller by hand. McpIt removes that layer: the endpoints you already have become the tools.
The only comparable library, Api.ToMcp, performs an internal HTTP self-call at runtime and supports controllers only. McpIt differs on four points:
[McpTool].McpIt is marked IsAotCompatible (the trim and AOT analyzers gate it on every build). Note: tools that take a request body currently serialize it with reflection-based System.Text.Json, and the MCP SDK's WithToolsFromAssembly() registration is reflection-based, so use explicit .WithTools<...>() registration for a fully AOT-published app.Controllers and minimal APIs. Mark a controller action or a minimal-API endpoint with [McpTool] to opt it in. Exposure is opt-in: only annotated endpoints become tools.
Tool names. [McpTool] derives a camelCase name from the method, or set Name explicitly. Placed on a controller class, [McpTool] sets defaults (such as NamePrefix) for that class's annotated actions without exposing anything on its own.
Output shaping with [McpToolOutput]. Keep responses lean. Fields projects the response down to the top-level JSON properties you list (per object, or per array element), and MaxLength truncates the result. Shaping is best-effort: malformed JSON passes through untouched.
[HttpGet("{id}")]
[McpTool]
[McpToolOutput(Fields = new[] { "id", "status" }, MaxLength = 500)]
public Order GetOrder(int id) { ... }
Safety hints from HTTP verbs. MCP tool annotations are derived from the verb: GET and HEAD are read-only and idempotent; POST, PUT, PATCH, and DELETE are flagged destructive (PUT and DELETE also idempotent). Exposing a destructive operation raises a build warning until you acknowledge it with [McpTool(AllowDestructive = true)].
MCPGEN diagnostics. Build-time warnings keep your tool surface honest: MCPGEN001 when a tool has no description, MCPGEN002 when a destructive operation is exposed without acknowledgement, MCPGEN003 when a versioned route token is present but no API version can be resolved.
API versioning. URL-segment versioning (Asp.Versioning and the legacy Microsoft.AspNetCore.Mvc.Versioning) works out of the box. No changes to your controllers are required; see the API versioning section below.
Token-cost report. The mcp-token-report tool measures what your tool list costs the model and can fail a CI build over a budget (see below).
The generated tools reach your endpoints through an in-process loopback HTTP call. By default that call carries no headers, so if an endpoint is protected (Basic, Bearer, an API key) the loopback arrives unauthenticated and the endpoint returns 401. Forward the credentials the MCP client already sent:
builder.Services.AddMcpEndpoints(options =>
{
options.BaseAddress = new Uri("https://localhost:5001/"); // required when forwarding (see below)
options.ForwardAuthorization = true; // copy the incoming Authorization header
options.ForwardedHeaders.Add("X-Api-Key"); // and any other headers, by name (case-insensitive)
});
ForwardAuthorization copies the incoming request's Authorization header onto each loopback call; ForwardedHeaders is a general allowlist for anything else (API keys, cookies, tenant headers). Both are off by default, so existing apps are unaffected. Forwarding requires that the MCP client authenticated to reach /mcp in the first place (so there is an incoming header to copy); for service-to-service credentials independent of the caller, register a DelegatingHandler on the typed IMcpEndpointInvoker client instead.
Forwarding requires an explicit BaseAddress. Without forwarding, McpIt auto-detects the loopback host from the incoming request. That host comes from the client-controlled Host header, so forwarding credentials to an auto-detected host could leak them to a spoofed host. To prevent that, enabling ForwardAuthorization or ForwardedHeaders without setting BaseAddress throws at startup. Pin BaseAddress to the host you trust.
By default a non-2xx response from your endpoint is returned to the agent as-is, which can surface a 401 page as if it were a successful result. Turn that into a real error:
options.ThrowOnUnsuccessfulResponse = true; // non-2xx throws McpEndpointInvocationException
McpEndpointInvocationException carries the StatusCode and ResponseBody. This is opt-in to preserve the prior pass-through behavior.
McpIt supports URL-segment API versioning out of the box, for both the modern Asp.Versioning package and the legacy Microsoft.AspNetCore.Mvc.Versioning. No changes to your controllers are required.
When a route contains a {version:apiVersion} token, the generator resolves each endpoint's version from attributes already on your code and bakes a concrete segment into the loopback path it emits. Without this the loopback call would 404 on the literal token.
namespace Api.V1;
[ApiController]
[ApiVersion("1.0")]
[Route("v{version:apiVersion}/account")]
public class AccountController : ControllerBase
{
/// <summary>Returns account info.</summary>
[HttpGet("info")]
[McpTool]
public string Info() => "v1 account info";
}
namespace Api.V2;
[ApiController]
[ApiVersion("2.0")]
[Route("v{version:apiVersion}/account")]
public class AccountController : ControllerBase
{
/// <summary>Returns account info.</summary>
[HttpGet("info")]
[McpTool]
public string Info() => "v2 account info";
}
McpIt generates two distinct tools: info_v1 (loopback path /v1/account/info) and info_v2 (loopback path /v2/account/info). The version suffix keeps names unique so both Info actions appear in tools/list without collision, even though they share a class name and a method name across the two version namespaces. A single controller can also map several versions with [MapToApiVersion] on differently named actions; the resolved version is folded into each derived name the same way.
Version resolution order per action: [MapToApiVersion] on the method, then [ApiVersion] on the method, then [ApiVersion] on the controller. When several versions apply, the highest wins.
Segment format: a minor version of zero is dropped ([ApiVersion("1.0")] produces /v1/), matching the common convention. A non-zero minor is preserved (2.1 produces /v2.1/).
Name suffix opt-out: set an explicit name with [McpTool(Name = "myTool")] or a class-level NamePrefix, and McpIt uses that name as-is with no auto-suffix.
Build warning MCPGEN003: if a route contains {version:apiVersion} but no [ApiVersion] or [MapToApiVersion] can be found on the action or its controller, the build warns rather than silently emitting a tool that would 404.
mcp-token-report is an offline analyzer that shows how many tokens your tools/list surface spends in the model's context. AI agents load every tool's name, description, and input schema before the user asks anything, so a large tool surface is a real, recurring context cost. The tool reads a running MCP server or a saved tools/list JSON file, reports per-tool and total token counts, and can gate a build with --budget. It is fully offline and deterministic, so it is safe in CI.
dotnet tool install -g McpIt.TokenReport.Tool
mcp-token-report http://localhost:5199/mcp # or a saved tools-list.json
mcp-token-report http://localhost:5199/mcp --markdown # Markdown table for CI artifacts
mcp-token-report http://localhost:5199/mcp --budget 2000 # exit 1 if over budget
Token counts use an offline heuristic tokenizer (estimates, not exact billing): ideal for comparing tools and catching bloat.
ModelContextProtocol.AspNetCore 1.4.0. It generates the tool classes; the official SDK serves them over the MCP transport you configure (AddMcpServer().WithHttpTransport(...)).IsAotCompatible and the read path is reflection-free; see the note above for the request-body and tool-registration caveats.McpIt ยท McpIt.Abstractions ยท McpIt.TokenReport.ToolModelContextProtocol. Free for personal and commercial use, no warranty. Keep the copyright and license notice.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 net8.0 is compatible. net8.0-android net8.0-android was computed. net8.0-browser net8.0-browser was computed. net8.0-ios net8.0-ios was computed. net8.0-maccatalyst net8.0-maccatalyst was computed. net8.0-macos net8.0-macos was computed. net8.0-tvos net8.0-tvos was computed. net8.0-windows net8.0-windows was computed. net9.0 net9.0 is compatible. net9.0-android net9.0-android was computed. net9.0-browser net9.0-browser was computed. net9.0-ios net9.0-ios was computed. net9.0-maccatalyst net9.0-maccatalyst was computed. net9.0-macos net9.0-macos was computed. net9.0-tvos net9.0-tvos was computed. net9.0-windows net9.0-windows was computed. net10.0 net10.0 is compatible. net10.0-android net10.0-android was computed. net10.0-browser net10.0-browser was computed. net10.0-ios net10.0-ios was computed. net10.0-maccatalyst net10.0-maccatalyst was computed. net10.0-macos net10.0-macos was computed. net10.0-tvos net10.0-tvos was computed. net10.0-windows net10.0-windows was computed. |
This package is not used by any NuGet packages.
This package is not used by any popular GitHub repositories.