Agent Harness and Claw Microsoft released the Agent Framework, enabling developers to build custom agent harnesses with minimal code. The framework bundles function invocation, history persistence, planning, and web search into a single call, allowing developers to focus on custom tools and instructions. A personal finance assistant example demonstrates creating a chat client, wrapping it in a harness, and running it interactively. Part 1 of Build your own claw and agent harness with Microsoft Agent Framework. In the overview https://devblogs.microsoft.com/agent-framework/build-your-own-claw-and-agent-harness-with-microsoft-agent-framework/ we said a “claw” is really just an agent harness : a loop around a model, wired up with tools, planning, memory, and more. In this first post we stand up that loop and give our personal finance assistant its first three abilities: - a custom tool get stock price , web search for market news, and planning – so a vague request like “Review my watchlist and recommend some stocks to add” becomes a tracked, step-by-step plan. The remarkable part: we get almost all of this for free. Agent Framework’s harness bundles function invocation, history persistence, planning, and web search into a single call. We only supply what makes our agent ours – its instructions and its custom tool. Let’s build it in three steps: construct a chat client, turn it into a harness, then run it through an interactive console. Step 1 – Construct a chat client Everything starts with a chat client – the thing that actually talks to a model. We point it at an endpoint, give it a credential for auth, and tell it which model deployment to use. In this example we are using Microsoft Foundry https://ai.azure.com/home with the Responses API https://ai.azure.com/api-reference/responses/create-response/ . .NET js // Read configuration from environment variables. var endpoint = Environment.GetEnvironmentVariable "FOUNDRY PROJECT ENDPOINT" ?? throw new InvalidOperationException "FOUNDRY PROJECT ENDPOINT is not set." ; var deploymentName = Environment.GetEnvironmentVariable "FOUNDRY MODEL" ?? "gpt-5.4"; // Build an IChatClient backed by a Microsoft Foundry project. IChatClient chatClient = new AIProjectClient new Uri endpoint , new DefaultAzureCredential .GetProjectOpenAIClient .GetResponsesClient .AsIChatClient deploymentName ; – your Microsoft Foundry project endpoint. FOUNDRY PROJECT ENDPOINT – the model deployment to call e.g. FOUNDRY MODEL gpt-5.4 .– handles auth from your environment e.g. run DefaultAzureCredential az login locally to use its session . In production, prefer a specific credential such as ManagedIdentityCredential . Python FoundryChatClient reads FOUNDRY PROJECT ENDPOINT and FOUNDRY MODEL from the environment. client = FoundryChatClient credential=AzureCliCredential – your Microsoft Foundry project endpoint. FOUNDRY PROJECT ENDPOINT – the model deployment to call. FOUNDRY MODEL – uses your AzureCliCredential az login session; swap in any other credential you prefer. Many clients, one harness.We used a Microsoft Foundry client here, but the harness works withanychat client – Azure OpenAI, OpenAI, Anthropic, Google Gemini, Ollama, and more.See the provider samples for how to construct each one: Also see the documentation for all providers . Step 2 – Turn the chat client into a harness Now we wrap that client in the harness. In .NET you call AsHarnessAgent ; in Python you call create harness agent . For now, we supply just two things: instructions what the agent is for and a custom tool . Instructions The harness handles how to operate; our instructions describe what the agent is for. .NET js var instructions = """ Personal Finance Assistant Instructions You are a personal finance and investing assistant. When asked about a stock, look up its current price with the get stock price tool, and use web search for recent news, earnings, or analyst commentary. Working style - Always verify numbers with a tool rather than relying on memory. Stock prices change. - Cite web sources inline when you use them. - Keep the user's watchlist in a memory file called watchlist.md : read it when reviewing the watchlist, and update it whenever the user adds or removes a ticker. """; Python FINANCE INSTRUCTIONS = """\ Personal Finance Assistant Instructions You are a personal finance and investing assistant. When asked about a stock, look up its current price with the get stock price tool, and use web search for recent news, earnings, or analyst commentary. Working style - Always verify numbers with a tool rather than relying on memory. Stock prices change. - Cite web sources inline when you use them. - Keep the user's watchlist in a memory file called watchlist.md : read it when reviewing the watchlist, and update it whenever the user adds or removes a ticker. """ A custom tool A tool is just a function the model can call. We expose a get stock price function; the framework generates the JSON schema from its signature and parameter descriptions. .NET Description "Gets the latest delayed, illustrative stock price for a ticker symbol." public static StockQuote GetStockPrice Description "The stock ticker symbol, e.g. MSFT or AAPL." string symbol { // ... look up the price ... return new StockQuote symbol.ToUpperInvariant , price, "USD", DateTimeOffset.UtcNow ; } public static AIFunction CreateGetStockPriceTool = AIFunctionFactory.Create GetStockPrice, "get stock price" ; Python python def get stock price symbol: Annotated str, "The stock ticker symbol, e.g. MSFT or AAPL." , - dict str, object : """Get the latest delayed, illustrative stock price for a ticker symbol.""" ... look up the price ... return {"symbol": ticker, "price": round price, 2 , "currency": "USD", "as of": ...} The samples return mock prices from an in-memory dictionary so they run with no external dependencies. In a real assistant you’d call a market-data API here. Wire it together With the instructions and tool in hand, one call builds the agent. .NET AIAgent agent = chatClient.AsHarnessAgent new HarnessAgentOptions { ChatOptions = new ChatOptions { Instructions = instructions, Tools = StockTools.CreateGetStockPriceTool , }, } ; Python agent = create harness agent client=client, agent instructions=FINANCE INSTRUCTIONS, tools=get stock price, That single call gives us function invocation, per-service-call history persistence, a TodoProvider and AgentModeProvider for planning, and web search – all on by default and each configurable. We only supplied what makes our agent ours : its instructions and a custom tool. This is why web search and planning “just work”. We never wrote web-search code – the harness adds a hosted web-search tool by default turn it off with DisableWebSearch / disable web search , so “Any recent news on NVDA?” works out of the box. And because the harness includes a TodoProvider and an AgentModeProvider , asking it to “Review my watchlist and recommend some stocks to add” while in plan mode makes it produce a plan, write a todo list, then move into execute mode. Note that hosted web search needs to be supported by your service to work out of the box. We are using Microsoft Foundry with Responses, which fully supports web search. Step 3 – Run it through the harness console Finally, we hand the agent to a shared harness console – a streaming terminal UI with /todos , /mode , and /exit commands, and output colored by mode cyan for planning, green for execution . The console is is provided as a sample. Both languages ship the full source, designed to be copied and adapted as a starting point for your own UX web app, chat surface, IDE extension, … : - .NET: Harness Shared Console - Python: console .NET await HarnessConsole.RunAgentAsync agent, userPrompt: "Ask about a stock or say 'review my watchlist' to get started.", new HarnessConsoleOptions { / observers + command handlers / } ; Python await run agent async agent, session=agent.create session , observers=build observers with planning agent , initial mode="plan", title="💹 Finance Assistant", Run the sample: .NET cd dotnet dotnet run --project samples/02-agents/Harness/BuildYourOwnClaw/Claw Step01 MeetYourClaw Python uv run python/samples/02-agents/harness/build your own claw/claw step01 meet your claw.py Then try these in order: /mode execute – switch out of the default plan mode; quick lookups don’t need a plan. What's the price of MSFT? – watch the agent call your get stock price tool. Any recent news on NVDA? – watch it use web search. Add MSFT, NVDA and SPY to my watch list /mode plan – switch back to plan mode for a bigger, multi-step task. Review my watchlist and recommend some stocks to add – watch it plan, ask clarifying questions, then execute. Type /todos to see the list of todos and /mode to inspect the current mode. The default location for memories is the agent-file-memory directory, but can be customized via options. Under this directory, you have a subdirectory for each session. Thanks to the agent instructions, the watchlist is saved to watchlist.md in the current session folder. Save and resume a session The console can also persist the whole session to disk. Under the hood /session-export simply serializes the AgentSession object – conversation history and context-provider state such as the directory containing your file memory – to JSON via the agent’s SerializeSessionAsync , then writes it to a file. /session-import reads that file back and deserializes it into a live session. Continue in order: /session-export my-session.json – saves the current session including the watchlist memory to a file on disk. /exit , then relaunch the app – you’re back to a fresh, empty session. /session-import my-session.json – restores the saved session from disk. /mode execute – switch out of the default plan mode; quick lookups don’t need a plan. What's on my watchlist? – the agent answers from the restored memory; nothing was re-typed. How plan mode works Why does plan mode ask questions and request approval , while execute mode just gets on with it? The trick is structured output . The harness ships with two modes out of the box – plan the default and execute – and the console’s planning observer treats them differently. In execute mode the model replies with ordinary prose and gets to work. In plan mode, the console asks the model for a structured response instead of free-form text, by setting a response format with a JSON schema: .NET // In the console's planning observer, only while in plan mode: options.ResponseFormat = ChatResponseFormat.ForJsonSchema