The Unexpected AI Stack: C# + .NET (Part 1)

C# and .NET in the agentic era

C# and .NET as a stack are probably flying under the radar for many teams building new apps in the agentic era. For teams already using legacy C# and .NET, it may not be clear how to best leverage the modern .NET stack tooling to speed up agentic development.

Why C# for agentic development

In an age when agents can write code in any programming language, it is fair to ask: why have agents write code in C#? Last year, I was a technical lead on the effort at a $500m valuation series C post Y Combinator startup with approximately 40 engineers to move the entire backend off TypeScript plus Node and shift to a combination of C# and F# for all future backends. The reasons are numerous but it is worth summarizing this again in mid 2026 framed in the context of building in an agent-first reality.

Build-time and runtime type checks provide a foundational layer of safety and correctness to check agent outputs. Clean, easy to understand build-time error traces; compare this to typical TypeScript type error stacks. Roslyn static analyzers allow for custom static analysis for agent guardrails. Roslyn source generators for terse runtime code to remove boilerplate and reduce context for agents on the read path. Extensively and richly documented with broad first-party libraries means that LLMs have very good coverage in their training data. While Python and JavaScript may have more representation, the lack of a standard library and BCL means that there are many variants of how to accomplish the same task. EF Core, a mature powerful .NET ORM, provides build-time checks for database schemas and decreases the likelihood of runtime errors from schema changes. Mature, stable, consistent, easy to use tooling and tool chain. Just dotnet. Again, easy for agents to work with, minimizing the need for skills or explanatory text for the agents.

Aside from that, if developers are no longer writing code, why not choose a platform that has: built-in primitives for in-process asynchronous coordination such as System.Threading.Channels and the Orleans actor model; higher performance and throughput where it matters such as boundary serialization for both JSON and gRPC/Protobuf; better story around ecosystem security and dependency management such as NuGet versus NPM; a whole professional organization continuously patching and fixing the platform and libraries. Given the benefits of adding both build and runtime type safety as well as a mature, well-documented, and broad first-party platform, C# is a surprisingly strong choice for building real software with AI.

Encapsulating the runtime with Aspire

If you are using Docker Compose or Tilt for dev runtime orchestration, you may have occasionally wished that it was just a bit more programmable. That is exactly the gap that Aspire fills: a programmable orchestration layer that makes it easy to build an isolated runtime stack that agents can control while building software.

The best analogy is to consider the difference between Terraform and Pulumi or CDK. Terraform is a declarative tool for building infrastructure, while Pulumi and CDK are programmable tools that allow teams to build infrastructure with code. Aspire is the same thing for orchestrating your runtime stack. Early iterations of Aspire were focused more on building distributed, microservices systems. While Aspire is still great for that, it is even better as an agent-friendly runtime orchestration layer.

It has several key features that make it a foundational component of an agent-friendly software stack. It has a built-in internal network loop that isolates individual runtime stacks. This is important for agent development and worktrees because this allows agents to run the full stack in isolation. It has a built-in, queryable OpenTelemetry target that gives agents the richness to access not just logs but logical flows through traces and spans with rich metadata attributes on logs, traces/spans, and metrics. The aspire CLI supports searching and filtering console logs as well as OTEL structured logs and traces by resource. It is deeply programmable and supports C#, TypeScript, and Python for building runtime orchestration logic. And of course, it can run any code and has built-in extensions for handling most common runtime stack components such as Vite powered JS frontends, databases, messaging, caching, and so on.

Built-in telemetry and observability in Aspire

A key piece of the story is Aspire’s easy-to-use and built-in telemetry and observability backend. This screenshot of the Zeeq Aspire dashboard shows what a typical local runtime looks like. It does not look too different from Tilt or Docker Desktop, but peek the telemetry. Rich built-in telemetry lets agents get visibility into everyday types of issues such as the underlying database queries being issued. Most importantly, the telemetry sink also surfaces gen.ai attributes that give visibility into standard agent interactions: materialized prompts, tool calls, and so on. Aspire materializes gen.ai spans to make it easier to see final prompts, tool calls, and agent interactions. The agents can search these spans directly. You will certainly get much richer telemetry via specialized tools such as Langfuse (not mutually exclusive since it becomes just another OTEL sink), but Aspire’s built-in, searchable telemetry sink lets agents autonomously iterate with visibility into the runtime state.

aspire otel spans zeeq-server --search "BEGIN CHANGES" \
  --non-interactive -nologo \
  --dashboard-url http://localhost:15010

The CLI allows agents to search through the telemetry spans before doing a full read to trace through an instrumented execution flow (assuming your code has been well-instrumented!).

Agent interaction with runtime resources

The aspire CLI becomes a primary interaction point with agents for runtime resources including: reading logs via aspire logs, querying the state of running resources via aspire describe resource, and controlling resource lifecycle via aspire resource resource start|stop|rebuild.

# Search logs for a given resource
aspire logs zeeq-server --search "principal" \
  --non-interactive --nologo

# Allow agent to further filter via jq
aspire logs zeeq-server --search "principal" \
  --non-interactive --nologo --format json

# Check application status with all environment variables
aspire describe zeeq-server --format json

# Check application status and query for specific properties
aspire describe zeeq-server --apphost ./host --format json | \
  jq -c '.resources[] { name, displayName, state, healthStatus, commands: (.commands // {} | keys) }'

# Rebuild and restart a compiled resource like a .NET or Rust backend
aspire resource zeeq-server rebuild

# Restart a Node frontend resource
aspire resource zeeq-web restart

Port isolation for worktrees

The inner network loop becomes a key feature if you are using worktrees and you want to run the full application stack in isolation to allow agents to work in parallel.

# Run with randomized ports and isolated user secrets
aspire run --isolated

Wiring CSharpRepl into the running application

CSharpRepl, abbreviated CSR, is a command line REPL that allows interactive C# scripting. In the past, this has been generally useful for quickly trying out C# code without having to create a project or compile and run. However, the real unlock with CSR is that it can be wired into a running instance of the application.

Imagine a typical dependency graph for a web API handler endpoint. If the agent can only test the web API handler entrypoint via curl, then it cannot easily diagnose issues with Service α since it must always pass through three layers of dependencies before it can test an input scenario with Service α. While some of this can be covered with unit and integration tests, in some cases — especially with AI-enabled apps — there is no substitute for being able to test the runtime application directly like a human might with a debugger.

This is precisely what CSR allows. Will Fuqua, the author of CSharpRepl, has a writeup that shows the gist of this:

# Get the installation location of the CSharpRepl tool hooks
csharprepl connect init

# This yields two environment variables (example) that can be wired up via Aspire
export DOTNET_STARTUP_HOOKS=".../tools/csharprepl/.../connector/CSharpRepl.InjectedHook.dll"
export ASPNETCORE_HOSTINGSTARTUPASSEMBLIES="CSharpRepl.InjectedHook"

# Then list the candidate processes for CSR to connect to
csharprepl connect list

# And connect to the PID
csharprepl connect 6580

Once connected, the agent can then directly manipulate Service α and any other runtime dependencies without having to go through the API entrypoint. This allows agents to diagnose issues and iterate on fixes much more quickly since it can wrap the running functions, test different input scenarios, replace the current implementation and run a live test reaching directly into the DI container, and bypass the authentication and authorization layers at the boundary to simulate different users without having complicated agent authorization paths or weakened surface areas. Think of it like a blood test versus directly examining an organ or tumor. The blood test is still useful, but being able to directly probe the organ or tumor will give a more complete picture of underlying operational runtime behavior and make it easier for diagnosing issues.

Wrap and replace for runtime diagnosis

But it goes beyond that. The #replace command allows agents to intercept and replace a running function with another implementation. The #wrap command allows agents to intercept and wrap a running function with additional logic such as logging, telemetry, and so on. How wrap and replace can be used by agents to diagnose runtime issues, insert additional telemetry, and test new implementations rapidly.

Together, this toolset gives agents a huge unlock because the agents can reach deep into the runtime configuration of the application to test different scenarios. Three aspects that this boosts: agents can better diagnose production issues by directly interacting with a local running instance and simulate the failure conditions or attempt to recreate the failure conditions from a production trace. Agents can build more stable runtime systems and regression test backends without the need to use slower frontend browser/client manipulation. Agents can experiment and iterate faster while building more stable runtime/production code.

Closing thoughts

While it is true that a lot of the innovation in the AI space originates from the Python ecosystem, for most teams building business applications using AI, the C# and .NET stack is a highly underrated and perhaps surprising option, especially for folks that haven’t dabbled in C# or .NET since the .NET Framework era. Modern .NET and C# are well-suited for teams that want to build real products and real software on a stable base in a programming language and stack that offers agents strong build-time guardrails as well as throughput and performance at scale where it matters — consider the API boundary serialization of JSON and gRPC/Protobuf, for example, both areas where C# and .NET excel. In Part 2, we will build a practical template for a C# + .NET agent-enabled application that you can use as a starting point for your own projects. We will also go into real-world examples of how to let agents operate the stack effectively when building autonomously including a skill that guides agents on getting the most out of CSharpRepl. If you are curious to see a real-world setup, check out the Zeeq.ai repo.