Skip to Content
InstallationYour API

Adding Ithil to Your API

Ithil.Hosting is a NuGet package you add to your existing ASP.NET Web API. It registers the tool schema endpoint the gateway reads, and gives you two ways to mark endpoints as tools:

  • Controllers — the [AgentTool] attribute on action methods, discovered at compile time
  • Minimal APIs — .WithAgentTool() on mapped routes, discovered at startup (0.3.0+)

Use either or both; they end up in a single schema.

Your existing API code, routes, and business logic are unchanged. Ithil is additive only.

Install the package

dotnet add package Ithil.Hosting

Wire up in Program.cs (controllers)

var builder = WebApplication.CreateBuilder(args); builder.Services.AddControllers(); builder.Services.AddIthilHosting(); // registers Ithil services var app = builder.Build(); app.MapControllers(); app.MapIthilSchema(SchemaRegistry.Tools); // exposes GET /ithil/schema app.Run();

SchemaRegistry is a static class emitted by the Ithil source generator at compile time. It contains a Tools collection of all [AgentTool]-decorated methods found in the project.

Wire up in Program.cs (minimal APIs)

Chain .WithAgentTool() onto each route you want agents to call, then pass includeMappedEndpoints: true to MapIthilSchema:

using Ithil.Generated; using Ithil.Hosting; var builder = WebApplication.CreateBuilder(args); builder.Services.AddIthilHosting(); var app = builder.Build(); app.MapGet("/api/inventory/{sku}", (string sku, bool? includeReserved) => ...) .WithAgentTool("GetInventory", "Returns current stock on hand for a SKU."); app.MapPost("/api/inventory/adjust", (StockAdjustment adjustment) => ...) .WithAgentTool("AdjustInventory", "Adjusts stock for a SKU.", allowWrite: true); // Unmarked, so agents never see it. app.MapGet("/healthz", () => Results.Ok("healthy")); app.MapIthilSchema(SchemaRegistry.Tools, includeMappedEndpoints: true); app.Run();

Unlike [AgentTool], WithAgentTool() takes the tool name as its first argument — lambdas have no meaningful method name to borrow. Marking is opt-in, so adding a route never exposes it by accident.

Apps that mix controllers and minimal APIs use the same call: keep app.MapControllers() and switch to MapIthilSchema(SchemaRegistry.Tools, includeMappedEndpoints: true). If a controller action and a mapped endpoint share the same verb and route, the [AgentTool] entry wins.

See the WithAgentTool() Reference for every parameter, how handler parameters are classified, and how to pick a verb for Map / MapMethods endpoints.

How the source generator works

At compile time, the Ithil Roslyn source generator:

  1. Scans every class in the project for [AgentTool] attributes
  2. For each decorated method, reads the HTTP verb (from [HttpGet], [HttpPost], etc.) and the route template
  3. Emits a SchemaRegistry class with a statically-typed Tools collection containing the full MCP tool schema for each method

No reflection occurs at runtime. The schema is fully resolved at build time, which means startup is fast and there are no surprises if an attribute is misconfigured — the build fails instead.

What MapIthilSchema does

Every form of MapIthilSchema registers a single endpoint:

GET /ithil/schema

MapIthilSchema(SchemaRegistry.Tools) serves your [AgentTool] tools. MapIthilSchema(SchemaRegistry.Tools, includeMappedEndpoints: true) serves those plus every WithAgentTool() endpoint, reading the routing table on the first request and caching the result.

The gateway polls this endpoint at startup to discover which tools are available. The response is a JSON array of tool definitions:

[ { "name": "GetOrderStatus", "description": "Returns the current status of an order given its order ID", "httpMethod": "GET", "routePattern": "orders/{id}/status", "inputSchema": { "type": "object", "properties": { "id": { "type": "string" } }, "required": ["id"] }, "allowWrite": false, "maxResponseTokens": 2000 } ]

The gateway must be able to reach GET /ithil/schema on your API. Ensure there are no network or firewall rules blocking this path. The endpoint does not require authentication — it is an internal service-to-service call between the gateway and your API.

Verify it worked

After starting your API, confirm the schema endpoint is responding:

curl http://localhost:5200/ithil/schema

You should see a JSON array containing one entry for every [AgentTool]-decorated method and every WithAgentTool() endpoint. If a tool is missing, check that:

  1. The source generator ran — rebuild the project with dotnet build
  2. MapIthilSchema(...) is called in Program.cs
  3. At least one method has [AgentTool] applied, or one route has .WithAgentTool()
  4. For minimal-API tools, you passed includeMappedEndpoints: true

If the endpoint returns 500, a mapped endpoint has an ambiguous HTTP verb — the error message names the tool and the fix. See Choosing a verb.

Targeting specific endpoints

[AgentTool] is applied to individual controller action methods:

[AgentTool("Returns inventory count for a SKU")] [HttpGet("inventory/{sku}")] public IActionResult GetInventory(string sku) { ... } [AgentTool("Creates a purchase order", AllowWrite = true)] [HttpPost("orders")] public IActionResult CreateOrder([FromBody] OrderRequest req) { ... }

Minimal-API routes are targeted the same way, one call per route:

app.MapGet("/inventory/{sku}", (string sku) => ...) .WithAgentTool("GetInventory", "Returns inventory count for a SKU"); app.MapPost("/orders", (OrderRequest req) => ...) .WithAgentTool("CreateOrder", "Creates a purchase order", allowWrite: true);

See the [AgentTool] Reference and the WithAgentTool() Reference for all parameters.

Last updated on