---
title: "Connect to a server in C#"
url: https://recursiveai.org/onemodelcontext/cookbook/connect-to-a-server/
description: "A client discovers a server’s tools and calls them. You don’t need a language model or an API key to make a tool call."
version: "1.0.1"
---

# Connect to a server in C#

**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) · [GitHub](https://github.com/recursive-ai-oss/onemodelcontext-csharp) · [Docs](https://recursiveai.org/onemodelcontext/docs/)

Discover tools and call them without a model or API key.

```shell
dotnet add package RecursiveAI.OneModelContext
```

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

```csharp
using RecursiveAI.OneModelContext;
// Pass a server command as the first argument.
await using var client =
    await ModelContext.ConnectAsync(args[0]);
Console.WriteLine((await client.CallToolAsync(
    "add", new { a = 2, b = 40 })).Text);
```

**[Download this example (.zip)](https://recursiveai.org/onemodelcontext/cookbook/connect-to-a-server/connect-to-a-server.zip)**: unzip, then `dotnet run`.

For the snippet at the top, unzip the [calculator server example](https://recursiveai.org/onemodelcontext/cookbook/build-a-server-in-csharp/) into a sibling folder named `Server` (containing `Example.csproj`). From your client project’s folder, run:

```shell
dotnet run -- "dotnet run --project ../Server/Example.csproj -- --stdio"
```

The entire quoted server command becomes `args[0]`. The client snippet prints:

```text
42
```

## The full program

The download includes its own calculator server in the same executable. It starts that child process, lists its tools, calls `add` and closes the connection. Replace the connection command with your own server to use another process.

```csharp
using RecursiveAI.OneModelContext;
using System.Reflection;

if (args.Contains("--stdio"))
{
    await ModelContext.ServeAsync(new Calculator());
    return;
}
// Start this same assembly in server mode, then call it over stdio.
var assembly = Assembly.GetExecutingAssembly().Location;
await using var client = await ModelContext.ConnectAsync(
    $"dotnet \"{assembly}\" --stdio");
Console.WriteLine($"Server: {client.ServerInfo.Name}");
foreach (var tool in await client.ListToolsAsync())
    Console.WriteLine($"Tool: {tool.Name}");
Console.WriteLine((await client.CallToolAsync("add", new { a = 2, b = 40 })).Text);

public partial class Calculator
{
    /// <summary>Add two integers.</summary>
    [Tool]
    public int Add(int a, int b) => a + b;
}
```

`dotnet run` prints:

```text
Server: Example
Tool: add
42
```

A server that exits or cannot be started prevents connection; tool argument failures are returned through the tool result.

## Variations

List the available tool names. Add this after connecting in the full program:

```csharp
foreach (var tool in await client.ListToolsAsync())
    Console.WriteLine(tool.Name);
```

```text
add
```

## Related recipes

- [Build a Model Context Protocol® server in C#](https://recursiveai.org/onemodelcontext/cookbook/build-a-server-in-csharp/)
- [Host a Model Context Protocol® server on ASP.NET Core](https://recursiveai.org/onemodelcontext/cookbook/host-on-aspnet-core/)
- [Serve the Model Context Protocol® over HTTP without ASP.NET](https://recursiveai.org/onemodelcontext/cookbook/serve-over-http-without-aspnet/)
- [Require OAuth sign-in on a Model Context Protocol® server](https://recursiveai.org/onemodelcontext/cookbook/require-sign-in-on-a-server/)
- [Sign in to a protected Model Context Protocol® server in .NET](https://recursiveai.org/onemodelcontext/cookbook/sign-in-to-a-protected-server/)

---

## The short answer

A C# MCP client (Model Context Protocol®) can connect with `ModelContext.ConnectAsync`, discover tools with `ListToolsAsync` and invoke one with `CallToolAsync`. OneModelContext can launch a server command over stdio or connect to an HTTP endpoint. A model is optional: the code here directly asks a calculator to add two integers.

The downloadable program contains the server as well as the client. That makes the result reproducible without a running service. To use your own server, replace the child-process command with its command line and call one of the tools it advertises.

## How do I create a Model Context Protocol® client?

Create a .NET 10 console app, install the core package and await `ModelContext.ConnectAsync`. For stdio, the string argument is the server command, not its display name. The client starts the process and communicates through its standard streams. Use `await using` so disposal closes the connection when the work is finished.

The top snippet takes that command from the first application argument. For the downloaded executable, the server mode is `--stdio`. The full program builds the command from its own assembly path, including quotes around the path so a space in a folder name does not split it into multiple arguments.

Inspect the server before assuming a tool exists. `ListToolsAsync` returns tool definitions, including names and input schemas. The variation prints those names. For this calculator, the correct name is `add`; the C# method name `Add` is not the protocol name.

## What is a protocol client for?

It is the calling side of a tool connection. It can discover what a server offers and send structured requests. In this example there is no conversation or probabilistic step: the application picks the tool and arguments itself, then prints the returned text.

This is useful when you want to test an integration before connecting a model, or when application logic already knows which operation to run. A successful connection establishes protocol communication; it does not promise that a particular tool exists or that any arguments you invent are accepted.

The anonymous object in the example becomes the argument object. Its property names match the tool schema. Check `IsError` when application behavior depends on whether a tool succeeded; tool errors are not the same thing as failing to start the server process. The [getting-started guide](https://recursiveai.org/onemodelcontext/docs/getting-started/) demonstrates an invalid argument and its returned error.

## How do I connect to a remote server from C#?

Use `ContextTransport.Http` with the endpoint URI instead of a child-process command. The HTTP host must already be running, and the URI must include the mapped path. The client negotiates the protocol rather than requiring you to construct initialization JSON by hand. The [HTTP client guide](https://recursiveai.org/onemodelcontext/docs/http-client/) covers the transport’s options and limits.

For a protected endpoint, supply an HTTP client created by `OAuthClient.CreateHttpClient`. That handler can answer a challenge, obtain a token and replay the request. Merely adding an authorization decorator does not perform interactive sign-in.

For NativeAOT, change the argument serialization too. The object-argument overload shown here uses reflection; the [pinned README](https://recursiveai.org/onemodelcontext/docs/overview/) says it is not trim- or NativeAOT-safe. These examples are ordinary .NET console builds, not AOT projects. An AOT application must use `CallToolAsync(name, arguments, typeInfo)` with its own source-generated `JsonTypeInfo<T>` instead of assuming an anonymous object will survive trimming.
