{"slug": "agent-harness-and-claw", "title": "Agent Harness and Claw", "summary": "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.", "body_md": "*Part 1 of Build your own claw and agent harness with Microsoft Agent Framework.*\n\nIn 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:\n\n- a\n**custom tool**(`get_stock_price`\n\n), **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\n\nplan.\n\nThe 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.\n\nLet’s build it in three steps: construct a chat client, turn it into a harness, then run it through an interactive console.\n\n## Step 1 – Construct a chat client\n\nEverything 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.\n\nIn 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/).\n\n**.NET**\n\n``` js\n// Read configuration from environment variables.\nvar endpoint = Environment.GetEnvironmentVariable(\"FOUNDRY_PROJECT_ENDPOINT\")\n    ?? throw new InvalidOperationException(\"FOUNDRY_PROJECT_ENDPOINT is not set.\");\nvar deploymentName = Environment.GetEnvironmentVariable(\"FOUNDRY_MODEL\") ?? \"gpt-5.4\";\n\n// Build an IChatClient backed by a Microsoft Foundry project.\nIChatClient chatClient =\n    new AIProjectClient(new Uri(endpoint), new DefaultAzureCredential())\n        .GetProjectOpenAIClient()\n        .GetResponsesClient()\n        .AsIChatClient(deploymentName);\n```\n\n– your Microsoft Foundry project endpoint.`FOUNDRY_PROJECT_ENDPOINT`\n\n– the model deployment to call (e.g.`FOUNDRY_MODEL`\n\n`gpt-5.4`\n\n).– handles auth from your environment (e.g. run`DefaultAzureCredential`\n\n`az login`\n\nlocally to use its session). In\n\nproduction, prefer a specific credential such as `ManagedIdentityCredential`\n\n.\n\n**Python**\n\n```\n# FoundryChatClient reads FOUNDRY_PROJECT_ENDPOINT and FOUNDRY_MODEL from the environment.\nclient = FoundryChatClient(credential=AzureCliCredential())\n```\n\n– your Microsoft Foundry project endpoint.`FOUNDRY_PROJECT_ENDPOINT`\n\n– the model deployment to call.`FOUNDRY_MODEL`\n\n– uses your`AzureCliCredential`\n\n`az login`\n\nsession; swap in any other credential you prefer.\n\nMany 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:\n\nAlso see the\n\n[documentation for all providers].\n\n## Step 2 – Turn the chat client into a harness\n\nNow we wrap that client in the harness. In .NET you call `AsHarnessAgent`\n\n; in Python you call `create_harness_agent`\n\n. For now, we supply just two things: **instructions** (what the agent is for) and a **custom tool**.\n\n### Instructions\n\nThe harness handles *how* to operate; our instructions describe *what* the agent is for.\n\n**.NET**\n\n``` js\nvar instructions =\n    \"\"\"\n    ## Personal Finance Assistant Instructions\n\n    You are a personal finance and investing assistant. When asked about a stock, look up its\n    current price with the get_stock_price tool, and use web search for recent news, earnings,\n    or analyst commentary.\n\n    ### Working style\n    - Always verify numbers with a tool rather than relying on memory. Stock prices change.\n    - Cite web sources inline when you use them.\n    - Keep the user's watchlist in a memory file called `watchlist.md`: read it when reviewing\n      the watchlist, and update it whenever the user adds or removes a ticker.\n    \"\"\";\n```\n\n**Python**\n\n```\nFINANCE_INSTRUCTIONS = \"\"\"\\\n## Personal Finance Assistant Instructions\n\nYou are a personal finance and investing assistant. When asked about a stock, look up its current\nprice with the get_stock_price tool, and use web search for recent news, earnings, or analyst\ncommentary.\n\n### Working style\n- Always verify numbers with a tool rather than relying on memory. Stock prices change.\n- Cite web sources inline when you use them.\n- Keep the user's watchlist in a memory file called `watchlist.md`: read it when reviewing the\n  watchlist, and update it whenever the user adds or removes a ticker.\n\"\"\"\n```\n\n### A custom tool\n\nA tool is just a function the model can call. We expose a `get_stock_price`\n\nfunction; the framework generates the JSON schema from its signature and parameter descriptions.\n\n**.NET**\n\n```\n[Description(\"Gets the latest (delayed, illustrative) stock price for a ticker symbol.\")]\npublic static StockQuote GetStockPrice(\n    [Description(\"The stock ticker symbol, e.g. MSFT or AAPL.\")] string symbol)\n{\n    // ... look up the price ...\n    return new StockQuote(symbol.ToUpperInvariant(), price, \"USD\", DateTimeOffset.UtcNow);\n}\n\npublic static AIFunction CreateGetStockPriceTool() => AIFunctionFactory.Create(GetStockPrice, \"get_stock_price\");\n```\n\n**Python**\n\n``` python\ndef get_stock_price(\n    symbol: Annotated[str, \"The stock ticker symbol, e.g. MSFT or AAPL.\"],\n) -> dict[str, object]:\n    \"\"\"Get the latest (delayed, illustrative) stock price for a ticker symbol.\"\"\"\n    # ... look up the price ...\n    return {\"symbol\": ticker, \"price\": round(price, 2), \"currency\": \"USD\", \"as_of\": ...}\n```\n\nThe 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.\n\n### Wire it together\n\nWith the instructions and tool in hand, one call builds the agent.\n\n**.NET**\n\n```\nAIAgent agent = chatClient.AsHarnessAgent(new HarnessAgentOptions\n{\n    ChatOptions = new ChatOptions\n    {\n        Instructions = instructions,\n        Tools = [StockTools.CreateGetStockPriceTool()],\n    },\n});\n```\n\n**Python**\n\n```\nagent = create_harness_agent(\n    client=client,\n    agent_instructions=FINANCE_INSTRUCTIONS,\n    tools=get_stock_price,\n)\n```\n\nThat single call gives us function invocation, per-service-call history persistence, a `TodoProvider`\n\nand `AgentModeProvider`\n\nfor 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.\n\nThis 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`\n\n/ `disable_web_search`\n\n), so *“Any recent news on NVDA?”* works out of the box. And because the harness includes a `TodoProvider`\n\nand an `AgentModeProvider`\n\n, 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.\n\nNote 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.\n\n## Step 3 – Run it through the harness console\n\nFinally, we hand the agent to a shared harness console – a streaming terminal UI with `/todos`\n\n, `/mode`\n\n, and `/exit`\n\ncommands, and output colored by mode (cyan for planning, green for execution).\n\nThe 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, …):\n\n- .NET:\n`Harness_Shared_Console`\n\n- Python:\n`console`\n\n**.NET**\n\n```\nawait HarnessConsole.RunAgentAsync(\n    agent,\n    userPrompt: \"Ask about a stock or say 'review my watchlist' to get started.\",\n    new HarnessConsoleOptions { /* observers + command handlers */ });\n```\n\n**Python**\n\n```\nawait run_agent_async(\n    agent,\n    session=agent.create_session(),\n    observers=build_observers_with_planning(agent),\n    initial_mode=\"plan\",\n    title=\"💹 Finance Assistant\",\n)\n```\n\nRun the sample:\n\n**.NET**\n\n```\ncd dotnet\ndotnet run --project samples/02-agents/Harness/BuildYourOwnClaw/Claw_Step01_MeetYourClaw\n```\n\n**Python**\n\n```\nuv run python/samples/02-agents/harness/build_your_own_claw/claw_step01_meet_your_claw.py\n```\n\nThen try these in order:\n\n`/mode execute`\n\n– switch out of the default plan mode; quick lookups don’t need a plan.`What's the price of MSFT?`\n\n– watch the agent call your`get_stock_price`\n\ntool.`Any recent news on NVDA?`\n\n– watch it use web search.`Add MSFT, NVDA and SPY to my watch list`\n\n`/mode plan`\n\n– switch back to plan mode for a bigger, multi-step task.`Review my watchlist and recommend some stocks to add`\n\n– watch it plan, ask clarifying questions, then execute. Type`/todos`\n\nto see the list of todos and`/mode`\n\nto inspect the current mode.\n\nThe default location for memories is the `agent-file-memory`\n\ndirectory, 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`\n\nin the current session folder.\n\n### Save and resume a session\n\nThe console can also persist the whole session to disk. Under the hood `/session-export`\n\nsimply serializes the `AgentSession`\n\nobject – conversation history *and* context-provider state such as the directory containing your file memory – to JSON via the agent’s `SerializeSessionAsync`\n\n, then writes it to a file. `/session-import`\n\nreads that file back and deserializes it into a live session. Continue in order:\n\n`/session-export my-session.json`\n\n– saves the current session (including the watchlist memory) to a file on disk.`/exit`\n\n, then relaunch the app – you’re back to a fresh, empty session.`/session-import my-session.json`\n\n– restores the saved session from disk.`/mode execute`\n\n– switch out of the default plan mode; quick lookups don’t need a plan.`What's on my watchlist?`\n\n– the agent answers from the restored memory; nothing was re-typed.\n\n## How plan mode works\n\nWhy does plan mode *ask questions* and *request approval*, while execute mode just gets on with it? The trick is **structured output**.\n\nThe harness ships with two modes out of the box – `plan`\n\n(the default) and `execute`\n\n– 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:\n\n**.NET**\n\n```\n// In the console's planning observer, only while in plan mode:\noptions.ResponseFormat = ChatResponseFormat.ForJsonSchema<PlanningResponse>();\n```\n\n**Python**\n\n```\n# In the console's planning observer, only while in plan mode:\noptions[\"response_format\"] = PlanningResponse\n```\n\nThat schema forces the model into one of exactly two shapes:\n\n**.NET**\n\n```\nclass PlanningResponse\n{\n    PlanningResponseType Type;        // Clarification or Approval\n    List<PlanningQuestion> Questions; // one or more items\n}\n\nclass PlanningQuestion\n{\n    string Message;          // the question, or the plan summary\n    List<string>? Choices;   // suggested options (clarification only)\n}\n```\n\n**Python**\n\n```\nclass PlanningResponse(BaseModel):\n    type: PlanningResponseType         # \"clarification\" or \"approval\"\n    questions: list[PlanningQuestion]  # one or more items\n\nclass PlanningQuestion(BaseModel):\n    message: str                       # the question, or the plan summary\n    choices: list[str] | None = None   # suggested options (clarification only)\n```\n\n**Clarification**– the model isn’t sure what you want yet, so it returns one or more questions. Each can carry a list of`Choices`\n\n, which the console renders as pickable options (you can always type a free-form answer too).*“Which MSFT – Microsoft or another ticker?”***Approval**– the model has a plan and returns a single item whose`Message`\n\nis the plan summary. The console surfaces it as an*“Approve and switch to execute mode”*prompt, so nothing executes until you say so.\n\nThis is what makes planning feel deliberate: the agent gathers what it needs, shows you the plan, and only then flips to execute mode and works through the todo list. The `PlanningResponse`\n\ntype lives in the console **sample** in both languages, so you can copy and tailor the schema – different question types, richer approvals – to fit your own UX.\n\n## Turning features off\n\nEverything the harness gives us – todos, agent modes, web search, file memory, file access, tool approval – is **on by default and individually toggleable**. If a feature doesn’t fit your scenario, switch it off with a single option. For example, to drop the todo list and web search:\n\n**.NET**\n\n```\nAIAgent agent = chatClient.AsHarnessAgent(new HarnessAgentOptions\n{\n    DisableTodoProvider = true,\n    DisableWebSearch = true,\n    ChatOptions = new ChatOptions { Instructions = instructions, Tools = [/* ... */] },\n});\n```\n\n**Python**\n\n```\nagent = create_harness_agent(\n    client=client,\n    agent_instructions=FINANCE_INSTRUCTIONS,\n    tools=get_stock_price,\n    disable_todo=True,\n    disable_web_search=True,\n)\n```\n\nThe common .NET switches are `DisableTodoProvider`\n\n, `DisableAgentModeProvider`\n\n, `DisableWebSearch`\n\n, `DisableFileMemory`\n\n, `DisableFileAccess`\n\n, and `DisableToolApproval`\n\n; Python exposes the equivalent flags it supports (`disable_todo`\n\n, `disable_mode`\n\n, `disable_memory`\n\n, `disable_web_search`\n\n). Our Part 1 sample leaves everything on – we just don’t lean on file access or approvals yet; those get their own spotlight in Part 2. Start with everything on, then trim to taste.\n\n## The runnable samples\n\n**.NET:**`dotnet/samples/02-agents/Harness/BuildYourOwnClaw/Claw_Step01_MeetYourClaw`\n\n**Python:**`python/samples/02-agents/harness/build_your_own_claw`\n\n## Use these building blocks in your own agent\n\nThe harness wires all of this up for you, but none of it is locked inside the harness. Web search is just a **tool**, and modes and todos are each a plain **context provider** — you can pick up exactly the pieces you want and add them to *any* agent, even without adopting the full harness. Here’s where to find them:\n\n| Feature | .NET (type — namespace) | Python (import) |\n|---|---|---|\nWeb search |\n`HostedWebSearchTool` – `Microsoft.Extensions.AI` (add to `ChatOptions.Tools` ) |\n`chat_client.get_web_search_tool()` |\nPlanning modes |\n`AgentModeProvider` – `Microsoft.Agents.AI` |\n`from agent_framework import AgentModeProvider` |\nTodo lists |\n`TodoProvider` – `Microsoft.Agents.AI` |\n`from agent_framework import TodoProvider` |\n\nIn **.NET**, the mode and todo providers ship in the `Microsoft.Agents.AI`\n\npackage, while the hosted web-search tool comes from `Microsoft.Extensions.AI`\n\n. In **Python**, all three live in the `agent-framework`\n\npackage. The providers plug in through an agent’s context providers and web search through its tools — the same wiring the harness does on your behalf.\n\n## What’s next\n\nOur claw can look things up, search the web, and plan. But it can’t yet touch *your* data, and there’s nothing stopping it from taking a sensitive action. In Part 2 – Working with your data, safely we will give it **file access**, gate risky actions behind **approvals**, and add durable **memory** so it remembers your preferences.\n\n## 📚 The series\n\nPart of **Build your own claw with Microsoft Agent Framework**:\n\n[Overview: Build your own claw and agent harness with Microsoft Agent Framework](https://devblogs.microsoft.com/agent-framework/build-your-own-claw-and-agent-harness-with-microsoft-agent-framework)**Part 1 – Meet your agent harness and claw***(you are here)*- Part 2 – Working with your data, safely\n*(coming soon)* - Part 3 – Scaling its capabilities\n*(coming soon)* - Part 4 – Production-ready\n*(coming soon)*", "url": "https://wpnews.pro/news/agent-harness-and-claw", "canonical_source": "https://devblogs.microsoft.com/agent-framework/meet-your-agent-harness-and-claw/", "published_at": "2026-06-24 09:49:40+00:00", "updated_at": "2026-06-24 10:14:38.457192+00:00", "lang": "en", "topics": ["ai-agents", "developer-tools", "ai-tools"], "entities": ["Microsoft", "Microsoft Agent Framework", "Microsoft Foundry", "Azure OpenAI", "OpenAI", "Anthropic", "Google Gemini", "Ollama"], "alternates": {"html": "https://wpnews.pro/news/agent-harness-and-claw", "markdown": "https://wpnews.pro/news/agent-harness-and-claw.md", "text": "https://wpnews.pro/news/agent-harness-and-claw.txt", "jsonld": "https://wpnews.pro/news/agent-harness-and-claw.jsonld"}}