<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: norequest</title>
    <description>The latest articles on DEV Community by norequest (@norequest).</description>
    <link>https://dev.to/norequest</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3973053%2F4c945246-7fd8-4697-965a-32f449c184ce.jpeg</url>
      <title>DEV Community: norequest</title>
      <link>https://dev.to/norequest</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/norequest"/>
    <language>en</language>
    <item>
      <title>Turn your existing ASP.NET Core API into MCP tools at build time</title>
      <dc:creator>norequest</dc:creator>
      <pubDate>Sun, 07 Jun 2026 21:31:21 +0000</pubDate>
      <link>https://dev.to/norequest/turn-your-existing-aspnet-core-api-into-mcp-tools-at-build-time-16d4</link>
      <guid>https://dev.to/norequest/turn-your-existing-aspnet-core-api-into-mcp-tools-at-build-time-16d4</guid>
      <description>&lt;p&gt;You already shipped a Web API. Now an AI agent wants to use it. With the official MCP C# SDK, the path from "I have endpoints" to "an agent can call them" runs straight through a pile of hand-written boilerplate. McpIt removes that pile. You add one attribute, and the tools are generated for you at compile time.&lt;/p&gt;

&lt;h2&gt;
  
  
  The boilerplate problem
&lt;/h2&gt;

&lt;p&gt;The official &lt;a href="https://www.nuget.org/packages/ModelContextProtocol" rel="noopener noreferrer"&gt;MCP C# SDK&lt;/a&gt; is excellent, Microsoft-backed, and the right foundation. But it expects you to hand-write an &lt;code&gt;[McpServerTool]&lt;/code&gt; class for every operation you want an agent to reach. You already described that operation once: the route, the HTTP verb, the parameters, the validation, the business logic all live in your controller action. Writing a second parallel class that mirrors all of it, then keeping the two in sync forever, is exactly the kind of duplication that rots.&lt;/p&gt;

&lt;p&gt;If you have ten endpoints you want to expose, you write and maintain ten extra classes. Rename a parameter in the controller, and the tool drifts until you remember to update it by hand. That is the tax McpIt is built to remove.&lt;/p&gt;

&lt;h2&gt;
  
  
  Before and after
&lt;/h2&gt;

&lt;p&gt;Here is a normal ASP.NET Core controller action:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ApiController&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"orders"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OrdersController&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ControllerBase&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;HttpGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{id}"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;GetOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;$"order-&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is the same action exposed to AI agents. Add one attribute and a &lt;code&gt;&amp;lt;summary&amp;gt;&lt;/code&gt; for the description:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;McpIt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.AspNetCore.Mvc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ApiController&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"orders"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OrdersController&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ControllerBase&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;Gets an order by its id.&amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;HttpGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{id}"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;McpTool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"getOrder"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;GetOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;$"order-&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is the whole change. At compile time McpIt generates the MCP tool class for &lt;code&gt;getOrder&lt;/code&gt;. It reads the action's HTTP verb, route template, parameters, and XML summary, then builds the tool's input schema and safety hints from them. The &lt;code&gt;id&lt;/code&gt; route parameter becomes a typed tool argument. The &lt;code&gt;&amp;lt;summary&amp;gt;&lt;/code&gt; becomes the tool description.&lt;/p&gt;

&lt;p&gt;To an MCP client, a &lt;code&gt;tools/list&lt;/code&gt; call now returns:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"tools"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"getOrder"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Gets an order by its id."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"inputSchema"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"object"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"integer"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"required"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"annotations"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"readOnlyHint"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"idempotentHint"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice the &lt;code&gt;annotations&lt;/code&gt;. Because &lt;code&gt;GetOrder&lt;/code&gt; is a GET, McpIt marks the tool read-only and idempotent automatically. No hand-written class, no duplicated schema.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;p&gt;Three things make McpIt different from the alternatives.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It is a source generator, not a runtime layer.&lt;/strong&gt; The tool classes exist at build time. There is no reflection scanning your assembly at startup and no proxy object built on the fly. That means it is AOT-safe: it works under Native AOT and trimming, where reflection-heavy approaches break.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It invokes your action in-process.&lt;/strong&gt; When an agent calls &lt;code&gt;getOrder&lt;/code&gt;, McpIt calls your real &lt;code&gt;GetOrder&lt;/code&gt; method directly. Your routing, model binding, validation, and business logic all run exactly as they do for an HTTP caller. There is no second HTTP request looping back into your own server. The only comparable library, &lt;code&gt;Api.ToMcp&lt;/code&gt;, makes an internal HTTP self-call for every tool invocation. McpIt skips the network entirely.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It covers controllers and minimal APIs.&lt;/strong&gt; Mark a controller action or a minimal-API endpoint with &lt;code&gt;[McpTool]&lt;/code&gt;. Both styles work. &lt;code&gt;Api.ToMcp&lt;/code&gt; is controllers-only.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this over writing servers by hand
&lt;/h2&gt;

&lt;p&gt;The official SDK gives you the transport, the protocol, and the serving infrastructure. That is real value, and McpIt sits on top of it: it layers on &lt;code&gt;ModelContextProtocol.AspNetCore&lt;/code&gt; and lets the official SDK serve the tools. What McpIt adds is the generation step, so you never hand-write the tool classes that mirror endpoints you already wrote.&lt;/p&gt;

&lt;p&gt;Exposure is opt-in. Only endpoints you annotate with &lt;code&gt;[McpTool]&lt;/code&gt; become tools, so you are never accidentally handing an agent your entire API surface. And safety hints come from the verb: GET and HEAD are read-only and idempotent, while POST, PUT, PATCH, and DELETE are flagged destructive. Exposing a destructive operation raises a build warning until you acknowledge it with &lt;code&gt;[McpTool(AllowDestructive = true)]&lt;/code&gt;. Build-time diagnostics (&lt;code&gt;MCPGEN001&lt;/code&gt; for a missing description, &lt;code&gt;MCPGEN002&lt;/code&gt; for an unacknowledged destructive op) keep the tool surface honest before it ships.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install and quickstart
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet add package McpIt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;McpIt&lt;/code&gt; brings in the official MCP SDK transitively, so you do not add &lt;code&gt;ModelContextProtocol.AspNetCore&lt;/code&gt; yourself. The &lt;code&gt;[McpTool]&lt;/code&gt; and &lt;code&gt;[McpToolOutput]&lt;/code&gt; attributes ship in the small &lt;code&gt;McpIt.Abstractions&lt;/code&gt; package, which also comes in transitively.&lt;/p&gt;

&lt;p&gt;Wire it up in &lt;code&gt;Program.cs&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;McpIt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WebApplication&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddControllers&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddMcpServer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithHttpTransport&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stateless&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithToolsFromAssembly&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;   &lt;span class="c1"&gt;// discovers the tools McpIt generated&lt;/span&gt;

&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddMcpEndpoints&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;   &lt;span class="c1"&gt;// in-process invoker for the generated tools&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapControllers&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapMcp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/mcp"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;   &lt;span class="c1"&gt;// MCP server at /mcp; your API stays where it is&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your REST API runs unchanged, and an MCP server is now served at &lt;code&gt;/mcp&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep the tool surface lean
&lt;/h2&gt;

&lt;p&gt;Two extras help responses stay cheap. &lt;code&gt;[McpToolOutput]&lt;/code&gt; shapes what comes back: &lt;code&gt;Fields&lt;/code&gt; projects a response down to the top-level JSON properties you list, and &lt;code&gt;MaxLength&lt;/code&gt; truncates the result.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;HttpGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{id}"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;McpTool&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;McpToolOutput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Fields&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"status"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;MaxLength&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;500&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Order&lt;/span&gt; &lt;span class="nf"&gt;GetOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The second extra is the token-report tool. Agents load every tool's name, description, and input schema into context before the user asks anything, so a fat tool surface is a real, recurring context cost. &lt;code&gt;mcp-token-report&lt;/code&gt; measures it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet tool &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; McpIt.TokenReport.Tool

mcp-token-report http://localhost:5199/mcp             &lt;span class="c"&gt;# live server or a saved tools-list.json&lt;/span&gt;
mcp-token-report http://localhost:5199/mcp &lt;span class="nt"&gt;--markdown&lt;/span&gt;  &lt;span class="c"&gt;# Markdown table for CI artifacts&lt;/span&gt;
mcp-token-report http://localhost:5199/mcp &lt;span class="nt"&gt;--budget&lt;/span&gt; 2000   &lt;span class="c"&gt;# exit 1 if over budget&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is fully offline and deterministic, so it is safe to gate a CI build with &lt;code&gt;--budget&lt;/code&gt;. The counts come from an offline heuristic tokenizer: estimates for comparing tools and catching bloat, not exact billing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Honest current limitations
&lt;/h2&gt;

&lt;p&gt;McpIt is v1.0.0, and there are edges worth knowing up front:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It targets &lt;strong&gt;.NET 10&lt;/strong&gt; and builds on &lt;code&gt;ModelContextProtocol.AspNetCore&lt;/code&gt; 1.4.0.&lt;/li&gt;
&lt;li&gt;It is &lt;strong&gt;controllers-focused&lt;/strong&gt;, with minimal-API support alongside. The richest behavior lives in the controller path.&lt;/li&gt;
&lt;li&gt;Output shaping is &lt;strong&gt;best-effort&lt;/strong&gt;: malformed JSON passes through untouched rather than being reshaped.&lt;/li&gt;
&lt;li&gt;Token counts are &lt;strong&gt;heuristic estimates&lt;/strong&gt;, not provider-exact billing numbers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of these block the core use case, but you should know them before you adopt.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;MCP in .NET is having a moment: Visual Studio 2026 ships with built-in MCP support, Aspire is leaning in, and the SDK hit 1.0. If you already have an ASP.NET Core API, this is the shortest path from "I have endpoints" to "an agent can use them."&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Star the repo: &lt;a href="https://github.com/norequest/McpIt" rel="noopener noreferrer"&gt;github.com/norequest/McpIt&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Install from NuGet: &lt;a href="https://www.nuget.org/packages/McpIt" rel="noopener noreferrer"&gt;nuget.org/packages/McpIt&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One &lt;code&gt;[McpTool]&lt;/code&gt; attribute, zero reflection, zero proxy, zero hand-written server. That is the pitch.&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>ai</category>
      <category>aspnetcore</category>
      <category>mcp</category>
    </item>
  </channel>
</rss>
