![]() |
|
[DevBlog MS] Build Your Own AI Agent Harness in C#, the MafClaw Live Series - Printable Version +- Sick Gaming (https://sickgaming.net) +-- Forum: Programming (https://sickgaming.net/forum-76.html) +--- Forum: C#, Visual Basic, & .Net Frameworks (https://sickgaming.net/forum-79.html) +--- Thread: [DevBlog MS] Build Your Own AI Agent Harness in C#, the MafClaw Live Series (/thread-113190.html) |
[DevBlog MS] Build Your Own AI Agent Harness in C#, the MafClaw Live Series - xSicKxBot - 09-19-2026 A few weeks ago I wrote about going from Code: dotnet run .But that post starts near the end of the journey. It assumes you already have an agent worth deploying. So the next question is: Quote:“What should I put inside the agent before I deploy it?” Tools. Planning. Safe file access. Human approval. Memory. Skills. Shell commands. Code execution. Background agents. Observability. Governance. Evaluations. That list gets big very quickly. The good news is that you do not need to build the runtime for all of it from scratch. Microsoft Agent Framework includes an agent harness, and I am building a complete C# agent with it live, one capability at a time, in a four-part series called From Model to Agent: The Agent Framework Harness, Live in C#. The series streams live simultaneously on the .NET YouTube channel and Microsoft Reactor, four consecutive Thursdays in September, and every session stays available afterward on demand on both platforms. Two sessions are already available, and two more are coming. Let me show you what we are building and why the harness makes this much easier. Register for the live Agent Framework series What we build across four sessions We start with this: Code: [code]AIAgent agent = chatClient.AsHarnessAgent(new HarnessAgentOptions
{
ChatOptions = new ChatOptions
{
Instructions = instructions,
Tools = tools
}
});[/code]Then we grow the same agent through four stages:
That is the complete journey: from one call around an Code: IChatClientFirst: what is an agent harness? A language model can generate text. An agent needs more. It needs a loop that can call tools, inspect the results, update a plan, remember useful information, request approval for risky actions, manage a growing context window, and keep working until the task is complete. That surrounding runtime is the harness. Where the term comes from The Microsoft Agent Framework team introduced the concept in the excellent Build your own claw and agent harness with Microsoft Agent Framework series. Their explanation is simple: a “claw” is a CLI-style agent built on top of a harness. You bring the model, instructions, and domain tools. The harness supplies the agentic machinery around them. In .NET, the key line is this one: Code: [code]AIAgent agent = chatClient.AsHarnessAgent(new HarnessAgentOptions
{
ChatOptions = new ChatOptions
{
Instructions = "You are a personal finance education assistant.",
Tools = [StockTools.GetStockPrice]
}
});[/code]That call gives the agent a complete pipeline with capabilities such as:
Each capability is configurable. You can replace it, disable it, or add your own provider. That is the advantage of starting with the harness: you spend your time on what makes the agent useful, instead of rebuilding the same orchestration loop for every project. The agent we are building Across the four sessions, we build one personal finance education assistant. Why finance? Because it gives us realistic boundaries to discuss:
This is a learning scenario All prices and transactions in the samples are mock and illustrative. This is not financial advice. It is a useful scenario for learning how agent systems behave when tools have different levels of risk. The complete code is in the MafClaw sample repository. Session 1: turn a model into an agent In , we started with the smallest useful agent. First, create an Code: IChatClientCode: [code]IChatClient chatClient =
new AIProjectClient(new Uri(endpoint), new AzureCliCredential())
.GetProjectOpenAIClient()
.GetResponsesClient()
.AsIChatClient(model);[/code]Then wrap it with the harness and give it one custom tool: Code: [code]AIAgent agent = chatClient.AsHarnessAgent(new HarnessAgentOptions
{
ChatOptions = new ChatOptions
{
Instructions = """
You are a personal finance education assistant.
Use get_stock_price for stock prices.
Use hosted web search for recent market news and cite sources.
Use the todo list to track multi-step work.
""",
Tools = [StockTools.GetStockPrice]
}
});[/code]The custom tool is ordinary C#. Agent Framework generates its tool schema from the function signature and descriptions: Code: [code][Description("Gets the illustrative stock price for a ticker symbol.")]
public static string GetStockPriceBySymbol(
[Description("Stock ticker symbol, e.g. MSFT")] string symbol)
{
var upper = symbol.Trim().ToUpperInvariant();
return upper switch
{
"MSFT" => "MSFT: 512.34 USD (mock)",
"NVDA" => "NVDA: 184.72 USD (mock)",
"AMZN" => "AMZN: 241.18 USD (mock)",
_ => $"{upper}: not available"
};
}
public static AIFunction GetStockPrice { get; } =
AIFunctionFactory.Create(
GetStockPriceBySymbol,
"get_stock_price");[/code]Now the difference between a chat application and an agent becomes visible. Ask: Code: [code]What is the price of MSFT?[/code]The model chooses the tool, the harness invokes it, the result returns to the model, and the agent produces the final answer. Ask something larger: Code: [code]Review my watchlist and suggest what I should research next.[/code]The harness can create a plan and maintain a todo list while it works. We did not write a custom planning engine for the demo. We configured the behavior that makes this finance agent ours, and the harness supplied the planning runtime. This first session is available now: Session 2: work with user data safely An agent becomes much more useful when it can work with your data. It also becomes much more dangerous. In , we gave the finance assistant access to a portfolio CSV, but only inside an approved working directory: Code: [code]var workingDirectory =
Path.Combine(AppContext.BaseDirectory, "working");
AIAgent agent = chatClient.AsHarnessAgent(new HarnessAgentOptions
{
FileAccessStore =
new FileSystemAgentFileStore(workingDirectory),
ChatOptions = new ChatOptions
{
Instructions = """
The user's portfolio is in portfolio.csv.
Read it before answering portfolio questions.
Write generated reports under the approved working folder.
""",
}
});[/code]The model does not receive arbitrary filesystem access. The application supplies a file store rooted at one folder, and the harness exposes file tools against that boundary. This means the happy path works: Code: [code]What is in my portfolio?[/code]And the unsafe path is blocked: Code: [code]Read C:\some-other-folder\outside-portfolio.csv[/code]The second boundary is human approval. A simulated trade is wrapped in Code: ApprovalRequiredAIFunctionCode: [code]public static AIFunction RequestSimulatedTrade { get; } =
new ApprovalRequiredAIFunction(
AIFunctionFactory.Create(
RequestSimulatedTradeOrder,
"request_simulated_trade"));[/code]The model can request the action, but it cannot execute it directly. Harness emits an approval request first. The host application can show exactly which tool and arguments need approval, then return the human decision to the same agent session. We also configured a low-friction safe path: Code: [code]ToolApprovalAgentOptions = new ToolApprovalAgentOptions
{
AutoApprovalRules =
[
FileAccessProvider.ReadOnlyToolsAutoApprovalRule
],
},[/code]Read-only file operations can proceed automatically. Writes, destructive operations, and the simulated trade still cross an approval boundary. That distinction matters. If every harmless read interrupts the user, approval becomes noise. The goal is not to show more confirmation dialogs. The goal is to make consequential actions visible. A question from the audience became a new sample During the live Q&A, someone asked: Quote:“What if the user does not answer the approval request?” Great question. Silence is not consent An approval flow that waits forever is not complete. So, after the session, I built a new sample with a bounded approval policy: a five-second deadline per attempt, a maximum of five attempts, retries for missing or invalid input, immediate approval for Code: yCode: nThe policy starts with a small configuration: Code: [code]const int maxApprovalAttempts = 5;
var approvalTimeout = TimeSpan.FromSeconds(5);
var approvalPolicy = new TimedApprovalPolicy(
maxApprovalAttempts,
approvalTimeout);[/code]You can find the complete implementation in Sample 22: approval retries and timeouts. The final part of Session 2 was memory. We compared local, application-owned JSON memory with managed Foundry Memory, and discussed why the model saying “I saved that” is not proof that anything was persisted. The application needs a real storage result, a scope, and a way to surface failures. Session 2 is also available now: Session 3: skills, shell, CodeAct, and background agents The first two sessions make the agent useful and safe. The third makes it more capable. In , we cover four different ways to expand an agent without turning its system prompt into a 400-page instruction manual:
Confinement, not just approval Shell and code execution are powerful capabilities. Confinement, policy, and approval improve the experience, but they are not a substitute for isolation. That boundary still matters. We build all four live, with the finance assistant as the running example. Session 4: make the agent production-ready At this point the claw can plan, use tools, work with files, request approvals, remember facts, load skills, execute code, and delegate research. That is the moment when somebody asks: Quote:“OK, the agent is done… now, how do I deploy this thing?” Yes, we are back to the question from my previous post .In , we close the loop with:
A production decision, not a framework limitation A shared hosted container should not inherit arbitrary local filesystem or shell access just because those capabilities were useful during development. Every capability the harness gives you locally has a production-appropriate equivalent, and choosing between them is a deliberate decision, not something the framework decides for you. The exact deployment approach follows the container-hosting setup from the Agent Framework sample. My earlier three-lines-of-C# post remains a useful introduction to the hosting model, but this claw has extra capabilities and therefore extra production decisions. We build the observability, governance, evaluation, and deployment story live in this final session. Why start with the harness? You can build every one of these pieces yourself. You can write a tool loop, serialize history after every service call, maintain a plan, compact context, build a memory layer, design an approval protocol, load skills, manage background workers, and instrument the whole pipeline. Sometimes you need that level of control. But most teams want to spend their time on the domain behavior that makes the agent valuable:
The harness gives those decisions a composable home. You still own the boundaries. You still choose the tools. You still decide what gets approved, remembered, executed, traced, and deployed. You just do not have to rebuild the agent runtime before answering any of those questions. Join the series The Microsoft Agent Framework blog has the complete written, .NET-and-Python version of this journey:
And in the series, we build the .NET version live, one capability at a time, streaming live simultaneously on the .NET YouTube channel and Microsoft Reactor, four consecutive Thursdays in September, then staying available on demand on both platforms: Register for the live Agent Framework series Get the complete C# samples Bring your questions. The approval timeout sample exists because someone did exactly that. Learn more Happy coding! Bruno The post Build Your Own AI Agent Harness in C#, the MafClaw Live Series appeared first on .NET Blog. |