---
title: "Dependency injection for tools in .NET"
url: https://recursiveai.org/onemodelcontext/cookbook/dependency-injection-for-tools/
description: "Inject singleton dependencies into tool classes and resolve scoped services per request with the OneModelContext hosting adapter."
version: "1.0.1"
---

# Dependency injection for tools in .NET

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

[NuGet](https://www.nuget.org/packages/RecursiveAI.OneModelContext.Hosting) · [GitHub](https://github.com/recursive-ai-oss/onemodelcontext-csharp) · [Docs](https://recursiveai.org/onemodelcontext/docs/) · [Download example (.zip)](https://recursiveai.org/onemodelcontext/cookbook/dependency-injection-for-tools/dependency-injection-for-tools.zip)

Supply Model Context Protocol® (MCP) tool dependencies from your .NET service container.

```shell
dotnet add package RecursiveAI.OneModelContext.Hosting
dotnet add package Microsoft.Extensions.Hosting
```

Requires [.NET 10](https://dotnet.microsoft.com/download/dotnet/10.0) (net10.0); .NET 9 and earlier aren’t supported.

```csharp
using Microsoft.Extensions.DependencyInjection;
using RecursiveAI.OneModelContext;
using RecursiveAI.OneModelContext.Hosting;

var services = new ServiceCollection();
services.AddSingleton<GreetingBook>();
services.AddContextServer<Greeter>();
using var provider = services.BuildServiceProvider();
var server = provider
    .GetRequiredService<ContextServer>();
Console.WriteLine(server.Tools.Count);

public sealed class GreetingBook
{
    public string Text => "Hello";
}
public partial class Greeter(GreetingBook book)
{
    [Tool]
    public string Greet() => book.Text;
}
```

This snippet only builds the server; the full program below connects a client and calls the tool.

The snippet creates the tool class through the container and prints the registered tool count:

```text
1
```


Unzip [the example](https://recursiveai.org/onemodelcontext/cookbook/dependency-injection-for-tools/dependency-injection-for-tools.zip), then `dotnet run`.

## The full program

The download uses the generic host, starts a child server, and calls two tools. `GreetingBook` is a singleton passed to the tool’s constructor. `RequestLabel` is scoped and resolved inside the handler. The core package’s source generator supplies `Greeter`’s registration code from the partial class and its `[Tool]` methods.

The host clears console logging because standard output carries protocol messages. Missing service registrations fail when the container or handler tries to resolve them.

```csharp
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using RecursiveAI.OneModelContext;
using RecursiveAI.OneModelContext.Hosting;
using System.Reflection;

if (args.Contains("--stdio"))
{
    var builder = Host.CreateApplicationBuilder();
    builder.Logging.ClearProviders();
    builder.Services.AddSingleton<GreetingBook>();
    builder.Services.AddScoped<RequestLabel>();
    builder.Services.AddContextServer<Greeter>().UseStdio();
    await builder.Build().RunAsync();
    return;
}
var assembly = Assembly.GetExecutingAssembly().Location;
await using var client = await ModelContext.ConnectAsync($"dotnet \"{assembly}\" --stdio");
Console.WriteLine((await client.CallToolAsync("greet", new { name = "Ada" })).Text);
Console.WriteLine((await client.CallToolAsync("label", new { })).Text);

public sealed class GreetingBook
{
    public string Greet(string name) => $"Hello, {name}!";
}
public sealed class RequestLabel
{
    public string Text => "Scoped service resolved";
}
public partial class Greeter(GreetingBook book)
{
    /// <summary>Greet a person.</summary>
    [Tool]
    public string Greet(string name) => book.Greet(name);

    /// <summary>Read a request-scoped label.</summary>
    [Tool]
    public string Label(RequestContext context) =>
        context.GetRequiredService<RequestLabel>().Text;
}
```

`dotnet run` prints:

```text
Hello, Ada!
Scoped service resolved
```

## Variations

Use a factory instead of constructor discovery. Replace the `AddContextServer<Greeter>()` line in the full program with:

```csharp
builder.Services.AddContextServer()
    .AddTools(services => new Greeter(
        services.GetRequiredService<GreetingBook>()))
    .UseStdio();
```
The output stays:

```text
Hello, Ada!
Scoped service resolved
```

## Related recipes

- [Build a Model Context Protocol® server in C#](https://recursiveai.org/onemodelcontext/cookbook/build-a-server-in-csharp/)
- [Connect to a server in C#](https://recursiveai.org/onemodelcontext/cookbook/connect-to-a-server/)

---

## The short answer

For MCP server dependency injection in .NET (Model Context Protocol®), install `RecursiveAI.OneModelContext.Hosting` and register tools with `services.AddContextServer<Tools>()`. The tool class is constructed from the container. Its constructor should take singleton dependencies; request-scoped dependencies belong inside the handler and are resolved through `RequestContext`.

The full example includes both patterns. It uses `Microsoft.Extensions.Hosting` to run a stdio server whose lifetime follows the generic host. The hosting adapter itself references the hosting abstractions; your application chooses the generic host package. You can also use the container to construct a server without starting a transport, as the snippet at the top does.

## How can I implement dependency injection in C# in .NET Core?

Register application services before registering the tool class. `AddSingleton<GreetingBook>()` says one book is shared for the container’s lifetime. `AddContextServer<Greeter>()` asks the adapter to register the generated tool class. The container supplies its constructor arguments using `ActivatorUtilities`, unless the class already has its own registration.

`Greeter` must be partial because the core package’s source generator adds its registration implementation. `[Tool]` identifies the methods to publish. A plain class with unannotated public methods is not automatically exposed as tools. Keep the package reference in the example project so the generator participates in compilation.

For explicit construction, the factory variation uses `AddTools(services => new Greeter(...))`. The factory builds the tool class without reflection. It is useful when construction needs application-specific choices that don’t map neatly to automatic constructor selection. The factory is not a per-call factory: registration happens when the server is first resolved.

## What is .NET dependency injection?

For this server, dependency injection separates tool behavior from service construction. The container owns the services, while the tool receives what it needs. The practical issue is lifetime: the adapter keeps one tool-class instance for the life of the server, so constructor dependencies live with that instance.

A request scope is different. Every tool, resource and prompt invocation gets its own scope. `context.GetRequiredService<RequestLabel>()` resolves the label from that scope, and the scope is disposed when the call ends. Don’t capture a scoped service in the long-lived tool constructor. The full program’s `Label` method shows exactly where to resolve it.

When a tool using protocol revision `2026-07-28` asks for client input, it may be run again from the start on retry. Each attempt gets a new scope. Don’t assume a scoped object survives a confirmation round trip. These lifetime rules come from the pinned [hosting adapter documentation](https://github.com/recursive-ai-oss/onemodelcontext-csharp/blob/d72d78c39c934697857be92dd8514efd4c415a88/docs/adapters.md).

A stdio transport also gives the process’s output stream a special role: it carries protocol data. Clear console logging as this example does, or direct the console logger to standard error. `UseStdio()` serves one connection and asks the host to stop when that connection closes, so the full program can dispose its client and let the child server end.
