Recursive AI

Host a server on ASP.NET Core

OneModelContext is a free, open-source .NET SDK for the Model Context Protocol® from Recursive AI. MIT or Apache-2.0, no native dependencies.

Add a Model Context Protocol® endpoint to your web app.

Terminal
dotnet add package RecursiveAI.OneModelContext.AspNetCore

Use a Web SDK project (Microsoft.NET.Sdk.Web), as in the download.

Requires .NET 10 (net10.0); .NET 9 and earlier aren’t supported.

C#
using RecursiveAI.OneModelContext;
using Microsoft.AspNetCore.Builder;
using RecursiveAI.OneModelContext.AspNetCore;
 
var builder =
    WebApplication.CreateSlimBuilder(args);
var app = builder.Build();
var server =
    new ContextServer("calculator", "1.0.0");
new Calculator().AddTo(server);
app.MapModelContext("/tools", server);
await app.RunAsync();
 
public partial class Calculator
{
    [Tool] public int Add(int a,int b)=>a+b;
}

Download this example (.zip): unzip, then dotnet run.

The snippet at the top keeps serving until stopped. Its endpoint is the listening URL ASP.NET Core prints at startup, followed by /tools.

The full program

The download starts ASP.NET Core on a free loopback port, maps /tools, makes a real HTTP tool call and stops. To keep hosting, replace the demonstration client and StopAsync with await app.WaitForShutdownAsync();.

C#
using RecursiveAI.OneModelContext;
using RecursiveAI.OneModelContext.AspNetCore;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Logging;
using System.Net;
 
var builder = WebApplication.CreateSlimBuilder(args);
builder.Logging.ClearProviders();
await using var app = builder.Build();
app.Urls.Add($"http://{IPAddress.Loopback}:0");
var server = new ContextServer("calculator", "1.0.0");
new Calculator().AddTo(server);
app.MapModelContext("/tools", server);
await app.StartAsync();
var url = new Uri(app.Urls.Single() + "/tools");
await using (var client = await ModelContext.ConnectAsync(ContextTransport.Http(url)))
{
    Console.WriteLine("Host: ASP.NET Core");
    Console.WriteLine((await client.CallToolAsync("add", new { a = 2, b = 40 })).Text);
}
await app.StopAsync();
 
public partial class Calculator
{
    /// <summary>Add two integers.</summary>
    [Tool]
    public int Add(int a, int b) => a + b;
}

dotnet run prints:

Text
Host: ASP.NET Core
42

A route that was not mapped returns HTTP 404; make sure the client uses the same path as MapModelContext.

The core package includes the source generator for [Tool] methods. Keep the class partial; the generator supplies its registration code. The download pins the package version in Example.csproj.

Variations

Change the arguments. Replace the tool call inside the full program with:

C#
Console.WriteLine((await client.CallToolAsync(
    "add", new { a = 10, b = 32 })).Text);
Text
42

The short answer

To host an MCP server (Model Context Protocol®) on ASP.NET Core, install RecursiveAI.OneModelContext.AspNetCore, create a ContextServer, register your generated tool class with AddTo and map it with app.MapModelContext. This creates a Streamable HTTP endpoint within your web app, not a second web server beside it.

The example maps /tools. That path is arbitrary, but the client must use the same path. The full program starts the web application, calls it over HTTP and stops it so dotnet run produces a useful result immediately.

How do I run my own server on ASP.NET Core?

Use the Web SDK and target .NET 10. The package reference brings the ASP.NET Core adapter and core library; you don’t need a separate hosting package merely to map an endpoint. The project in the download shows the SDK and pinned reference.

Build the web application, create the protocol server and add tool registrations before mapping. The Calculator class is partial because the source generator supplies AddTo. Its [Tool] method becomes the discoverable add operation.

The snippet at the top uses RunAsync to keep hosting until shutdown. The downloadable program deliberately differs at the end: it uses StartAsync, connects a client to the bound address, calls the tool and then stops the app. The free port avoids conflicts with other local examples. Replacing that demonstration lifecycle with the normal web-host lifetime leaves the endpoint available to other clients.

How do I connect a C# client to the HTTP endpoint?

Connect with ContextTransport.Http and the complete URL, including /tools. The full program gets the actual bound address from the app and appends that route. It then uses the ordinary client API to call add, just as a stdio client would.

If the client receives HTTP 404, check the path before debugging tool registration. A host can be reachable while the route is wrong. Conversely, reaching the route does not mean a tool named after your C# method exists: the generated name for Add is add.

One endpoint handles the library’s supported modern and legacy protocol eras. Do not create separate web routes just to split the calculator by era. The getting-started ASP.NET Core example demonstrates both on the same mapping, and the HTTP limits guide explains how requests differ.

How do I add authentication to a protocol server?

Choose authentication explicitly; mapping the route alone does not require sign-in. Middleware added before MapModelContext, such as authentication or rate limiting, runs before the protocol handler. The library also supports .RequireAuthorization() when the host has an authentication scheme configured. These are web-host concerns, not attributes to add to the calculator’s arithmetic method.

For OAuth-aware resource protection, configure a ResourceServer with the endpoint’s resource URI and a token validator, then provide it through StreamableHttpOptions. This supplies the protocol’s protected-resource metadata and challenges as documented in the resource-server guide. A resource URI is configuration, not a value to derive from an untrusted Host header.

For deployment, keep the boundary clear: ASP.NET Core owns network binding, middleware and application lifetime; OneModelContext owns the protocol endpoint. The local self-call above demonstrates routing and tool invocation, not a complete public deployment or sign-in system. The OAuth server recipe includes a runnable local authorization exchange.

All onemodelcontext recipes