Skip to main content
The C# SDK follows the TypeScript SDK: the same events, the same injected parameters, and the same text for agents. This page lists the versions it supports, where it differs, what it sends besides your events, how its exporters behave, and the limits that you can hit. Logging, diagnostics, tests and shutdown covers how it runs.

Supported versions

  • The MCP SDK. The official C# MCP SDK, ModelContextProtocol 1.0.0 or later: the 1.x and 2.x lines from one package. The 0.x previews and other .NET MCP libraries are not supported.
  • .NET. The package targets .NET 8 (for .NET 8 and 9), .NET 10, and .NET Standard 2.0 (for .NET Framework 4.7.2 or later). It depends on ModelContextProtocol and AgentCat.Api (and Microsoft.Bcl.Memory on .NET Standard 2.0), and finds ASP.NET Core at run time, so a stdio server needs no ASP.NET Core.
  • ModelContextProtocol.Core alone. A project that references only ModelContextProtocol.Core 2.x gets ModelContextProtocol 1.0.0 through the AgentCat package, and AddMcpServer() fails in that mix. Such a project uses AgentCatSdk.Track(), or references ModelContextProtocol 2.x directly.
  • The generic host. The flush after the host stops needs Microsoft.Extensions.Hosting 8 or later. Older hosts and the legacy WebHost rely on the process-exit handler (see Shutdown).
  • Native AOT and trimming. On .NET 8 and .NET 10 the package is trim and Native AOT compatible: a stdio server and an ASP.NET Core server published with Native AOT get no warnings from AgentCat, and request capture works. On .NET 8 an ASP.NET Core app still gets two warnings from ASP.NET Core’s own JsonOptions. Under Native AOT, error events have no file names or line numbers, and on .NET 8 they carry the stack text but no frames.

Differences from the TypeScript SDK

Hooks get a snapshot and a token

Hooks receive one AgentCatToolCall and a CancellationToken, not the TypeScript (request, extra), because the C# request context is shared and changes during the call. They return a ValueTask. AgentCat cancels the token when it stops waiting for the hook: after 30 seconds, once it has used the result, or when it drops the event (a full queue or a shutdown). It is not the request’s cancellation token. TypeScript hooks get no token.

JSON data is JsonNode

Free-form data is JsonNode or JsonObject, not arbitrary objects: UserIdentity.UserData, the result of EventProperties, and the Parameters, Response, Error and Properties of a custom event. AgentCat serializes them without reflection.

Injected parameters never reach your tool

With EnableTracing = false, and when AgentCat fails while it prepares a call, your tool still gets its arguments without the parameters that AgentCat added to its schema. The TypeScript SDK forwards them unchanged in those cases.

Unknown tools

A call to a tool that does not exist gets a JSON-RPC error without AgentCat’s session text, because the C# MCP SDK throws for it. TypeScript servers on version 2 of the TypeScript MCP SDK behave the same.

Errors from tools

When a tool throws, the C# MCP SDK turns the exception into an error result. AgentCat adds its session text to that result, as the TypeScript SDK does, and the event records the exception with its full .NET type name and its stack. The stack ends at AgentCat’s filter.

Tools that ask for more input

On ModelContextProtocol 2.x, the event of a tool call that asks the client for more input (input_required) has no response and no output tokens. The TypeScript SDK records the input_required result.

The project ID

null reads the project ID from AGENTCAT_PROJECT_ID. An empty string sends no events to AgentCat: the events go to your exporters only. In the TypeScript SDK, null means no project.

The request in parameters.extra

parameters.extra holds the sessionId of the transport, the requestId, the _meta of the request and, on ASP.NET Core, the requestInfo with the headers and the URL. There is no authInfo: hooks read the authenticated user from call.User. The host of the URL keeps its case, which the TypeScript SDK lower-cases. On the legacy SSE transport, the requestInfo is the session’s GET /sse request, where the TypeScript SDK records the POST of the call.

Delivery

Each delivery attempt has a 10-second timeout. The TypeScript SDK sets none. As in the TypeScript SDK, the queue holds 10,000 events and drops the oldest when it is full, and AgentCat retries a failed event up to 3 times, after 1, 2 and 4 seconds.

Additions of the C# SDK

  • AGENTCAT_ENABLED and test runs. The environment variable turns AgentCat on or off with no code, and AgentCat is off while your tests run. AgentCatSdk.Fake() records the events of your tests. The TypeScript SDK tracks in tests. See Turn AgentCat on or off.
  • LogToHost sends AgentCat’s log lines to the logger of the host (see The log file).
  • DisableShutdownHandlers turns off the process-exit handler and, for Track(), the SIGTERM and SIGINT handlers (see Shutdown).

What the C# SDK sends

Request headers and URL

On an ASP.NET Core server, the event of a tool call carries every request header of that call, verbatim, Authorization and Cookie included, and the full URL with its query string, under parameters.extra.requestInfo. The TypeScript SDK sends every header too. Request headers and URL lists the cases without a request and has a RedactEvent hook that removes credentials.

Diagnostics

To find broken installs, AgentCat sends its own log lines to AgentCat, with the session IDs, actor IDs and error messages that they hold. Diagnostics says what they contain and how to turn them off.

Exporters

The exporters send the same data as in the TypeScript SDK, with these rules:
  • One set for each process. The first WithAgentCat() or Track() of the process that adds exporters sets them up for every server of the process. AgentCat ignores the exporters of later installs, without a log line. While a fake is current, AgentCat does not set up exporters.
  • An empty Exporters sets nothing up. A later install can still add exporters, and an install without a project ID still logs its warning that the events go nowhere. The TypeScript SDK counts an empty exporters object as configured.
  • Read once. AgentCat reads the dictionary and the exporter configurations, OtlpExporter.Headers included, when it installs. Later changes have no effect. A key is a name of your choice: it shows only in log lines, and keys are case-sensitive. The exporters run in the order you added them.
  • Retries. When the AgentCat API fails and AgentCat retries, it sends the event to your exporters again on each attempt, so an exporter can get up to 4 copies.
  • Never in the way. An exporter never delays or stops the delivery to AgentCat, and its failures only write lines to the AgentCat log. A wrong endpoint, site, host or API key shows up as a failure line for each export. FlushAsync and the stop budget wait for the exporter requests too.
  • Limits. Each exporter request has a 10-second timeout. At most 10 requests run at once, and 1,000 wait. When more wait, AgentCat drops the oldest and logs WARN: AgentCat telemetry export queue full, dropped the oldest <n> request(s). The TypeScript SDK sends every request at once, with no timeout.
  • Sentry. AgentCat replaces the key part of the DSN, between :// and @, with *** in every log line. An invalid DSN skips only that exporter (see Sentry).
  • PostHog gets the parameters and the response of each call, request headers included (see PostHog).

Known limits

  • The official C# stdio client kills the server. When an McpClient over StdioClientTransport is disposed, it kills the server process instead of closing its input: at once on ModelContextProtocol 1.0 to 1.2, and after the client’s ShutdownTimeout on 1.3 and later. The server’s shutdown code never runs, so the events that AgentCat has not sent by then are lost.
  • Concurrent tool creation before Microsoft.Extensions.AI.Abstractions 10.9.0. In earlier versions (ModelContextProtocol 1.0.0 brings 10.3.0, 2.2.0 brings 10.8.3), two threads that create a tool from the same method for the first time can put the method’s RequestContext parameter into the tool’s input schema (dotnet/extensions#7677). When that parameter is named context, AgentCat takes context for the tool’s own argument: it neither adds its own nor removes it. If your server creates tools concurrently, for example on every stateless request, reference Microsoft.Extensions.AI.Abstractions 10.9.0 or later.
  • When AgentCat cannot track a server, it logs Warning: Failed to track server - <error>, and the server runs untracked.
  • Handlers.CallToolWithAlternateHandler (ModelContextProtocol 2.x). The MCP SDK rejects tools/call filters together with this handler, so AgentCat leaves a server that sets it untracked and logs Warning: AgentCat did not instrument this server: Handlers.CallToolWithAlternateHandler is set, and McpServer.Create rejects tools/call filters with it. Set the handler in a Configure step, not in ConfigureSessionOptions: ConfigureSessionOptions runs after AgentCat has added its filters, and the MCP SDK then rejects the session.
  • Filters that you add later. AgentCat adds its tools/call filters at both ends of the filter list, after your Configure steps. A filter that you insert at index 0 later, in ConfigureSessionOptions or in a PostConfigure step that you register after WithAgentCat(), runs outside AgentCat. When such a filter turns a tool exception into its own result, that result still gets the session text, but the event records the error result that the MCP SDK builds from the exception, which the client does not see.
  • A result that a filter outside AgentCat changes in place (ModelContextProtocol 1.x). When AgentCat adds nothing to a result, it hands the tool’s own result object outward. A filter outside AgentCat that changes that object in place therefore races the event’s later read: the event can carry the change or lose its response field. The response to the client is not affected. A filter that needs to change a result returns a new result object.
  • Session text on ModelContextProtocol 2.x. An error result built from a tool exception gets no session text in two cases: a tool that asks the client for input on a stateful session, and a tool call that runs as a background task of the MCP Tasks extension.
  • Hooks and the request. The part of a hook before its first await runs on the request thread, before your tool, so a slow synchronous part delays the tool call. A hook can run after the request has ended, when request-scoped services (call.Request.Services) are disposed: read what you need before the first await. A hook that still runs, for up to 30 seconds, keeps its request context alive, as in the TypeScript SDK.
  • Hooks that block. AgentCat delivers events with five workers. RedactEvent and RedactSensitiveInformation run on these workers with no time limit, as in the TypeScript and PHP SDKs. A redaction hook that blocks, or a tool-call hook that blocks synchronously after AgentCat cancels its token, holds one of the five workers.
  • Listing on a fresh process. When AgentCat lists the tools itself for a tools/call (see Stateless HTTP), the 5-second limit does not stop a list handler that blocks before it returns. When the limit ends a listing, AgentCat cancels the token that it passed to your list handler, and the callbacks that your handler registered on that token run on the request thread of the tool call.
  • Very deep JSON. AgentCat measures and writes the JSON of an event recursively. A JsonObject that your code builds hundreds of levels deep, for example in EventProperties, can drop its event on a thread with a small stack. The TypeScript SDK drops cyclic properties the same way.
  • Tool names are kept. AgentCat keeps what it learns about each tool name for the life of the process. A server with many per-tenant tool names keeps all of them.
  • Exporter hosts that do not answer. One exporter host that never answers holds all 10 request slots, for 10 seconds each, and the requests of your other exporters wait behind it.
  • Exporter memory. AgentCat counts the 1,000 waiting exporter requests, not their size. With EnableAITracing, a PostHog request can be up to about 200 KB, so 1,000 waiting requests can hold about 200 MB.
  • Exporter log lines. With all four exporters, each event writes about 12 more lines to the AgentCat log, and AgentCat writes each line to the file synchronously.
  • .NET Framework hosts. On .NET Framework, AgentCat sends through HttpClientHandler, with at most five connections to a server. An open connection does not see a DNS change, and the OpenTelemetry HttpClient instrumentation for .NET Framework can record AgentCat’s requests.