{"slug": "from-single-call-to-agents-five-new-claude-capabilities-available-in-ms-foundry", "title": "From single call to agents: five new Claude capabilities available in MS Foundry", "summary": "Anthropic has made five Claude capabilities—Structured outputs, Web search, Web fetch, MCP connector, and Tool search—available on deployments hosted on Azure in Microsoft Foundry, eliminating the trade-off between agentic functionality and keeping prompts and completions within Azure. The features, previously limited to Hosted on Anthropic deployments, now run on Azure infrastructure, with prompts and completions remaining within Azure and only usage metadata and safety-flagged content egressing to Anthropic. This enables regulated industries to run web-search-backed agents and MCP-connected tools while maintaining data residency commitments.", "body_md": "# From single call to agents: five new Claude capabilities now available in Microsoft Foundry\n\n**Structured outputs, Web search, Web fetch, MCP connector, and Tool search are now available for Claude models in Microsoft Foundry hosted on Azure, the building blocks that turn a model endpoint into a production agent platform.**\n\nWhen Claude models became generally available in Microsoft Foundry in June 2026, the headline was new hosted on Azure access: frontier Claude models, Azure-native endpoints, Entra ID authentication, and Azure Marketplace billing. That solved the *procurement and governance* problem. Teams could finally run Claude inside the same subscription, network perimeter, and cost-management surface as the rest of their Azure estate.\n\nBut access to a model is not the same as a platform for agents. In the weeks since the generally available launch, we’ve now added more capabilities to the Hosted on Azure options. The pattern we have seen across Foundry customers is the same: a team ships a strong Claude-powered feature, then spends the next quarter rebuilding the same four pieces of scaffolding.\n\n- A retry loop that re-prompts the model because it returned JSON with a trailing comma.\n- A bespoke search-and-scrape service, with its own crawler, cache, robots.txt handling, and citation plumbing.\n- A hand-rolled MCP client so the model can reach Jira, ServiceNow, Confluence, and three internal APIs.\n- A tool router, because once you wire up 300 tools the model starts picking the wrong one.\n\nEvery one of those is undifferentiated engineering. None of it is your product. This release moves all four into the platform and, critically, moves them onto deployments hosted on Azure, so you no longer choose between agentic capability and keeping prompts and completions within Azure.\n\nThis post walks through each capability: what it does, the API shape on Foundry, a realistic enterprise use case, and the constraints that will bite you in production. Code examples are given in Python and TypeScript using the Anthropic Foundry SDKs.\n\n## The part that changes the architecture: these now run hosted on Azure\n\nClaude models in Microsoft Foundry come in two hosting options, chosen when you create the deployment. Previously, the agentic feature set was available only on Hosted on Anthropic deployments, which forced trade-off: teams with a data-handling commitment that prompts and completions stay within Azure had to either give up that commitment or rebuild search, fetch, MCP, and tool routing client-side.\n\n**That trade-off is now resolved. Structured outputs, Web search, Web fetch, MCP connector, and Tool search are available on deployments hosted on Azure.**\n\nHosted on Azure |\nHosted on Anthropic |\n|\n|---|---|---|\n| Where inference runs | Anthropic-operated service on Azure infrastructure | Anthropic-operated service on Anthropic infrastructure |\n| Model availability | Latest Opus, Sonnet, and Haiku models | The full Claude catalogue on Foundry |\n| Deployment types | Global Standard, US Data Zone Standard | Global Standard |\n| The five features in this post | ✓ | ✓ |\n| Recommended for | Most workloads | Access to models not yet hosted on Azure |\n\nFor deployments hosted on Azure, prompts and completions remain within Azure; only usage metadata and content flagged by Anthropic’s safety systems egress to Anthropic. Anthropic acts as an independent processor for Microsoft, and customers using Claude through Foundry are subject to Anthropic’s data use terms.\n\nThe practical consequence for regulated industries is significant. A US Data Zone Standard deployment keeps inference within the United States, equivalent to setting `inference_geo: \"us\"`\n\non the Claude API and that deployment can now run a web-search-backed research agent, connect to your internal MCP servers, and return grammar-constrained JSON. Twelve months ago that combination required choosing between capability and residency posture. It no longer does.\n\nSee these capabilities in action on this upcoming webinar. [LINK: https://www.anthropic.com/webinars/claude-in-microsoft-foundry-tool-integrations-in-practice?utm_source=partner-msft&utm_medium=webinar&utm_campaign=msft-promotion]\n\n## 1. Structured Outputs: the end of JSON.parse() roulette\n\n### The problem\n\nEvery team that has put an LLM in a data pipeline has written this code:\n\n```\nfor attempt in range(3):\n    raw = call_model(prompt)\n    try:\n        data = json.loads(raw)\n        validate(data)\n        break\n    except (json.JSONDecodeError, ValidationError):\n        prompt += \"\\n\\nYour last response was invalid JSON. Try again.\"\n```\n\nIt works most of the time. “Most of the time” is a terrible property for a batch job that processes 400,000 documents overnight, because 0.3% failure is 1,200 rows in a dead-letter queue that somebody has to triage on Monday.\n\n### What it does\n\nStructured outputs constrain generation itself. The model’s decoding is restricted by a grammar compiled from your JSON Schema, so the output *cannot* be malformed. Two complementary features, usable independently or together:\n\n**JSON outputs**(`output_config.format`\n\n) — controls the shape of Claude’s response text.**Strict tool use**(*strict: true*on a tool) — guarantees schema-valid tool*inputs*.\n\nThe first governs what Claude says. The second governs how Claude calls your functions.\n\n### Use case: claims intake at a specialty insurer\n\nA commercial insurer receives first-notice-of-loss submissions as free-text email, broker PDFs, and adjuster voice-note transcripts. The downstream system is an Azure SQL table with a rigid schema and a Logic Apps workflow that routes by severity. Historically, extraction ran through a regex-and-heuristics pipeline that covered about 60% of formats and dumped the rest into a manual queue.\n\nWith structured outputs, the extraction contract is the schema:\n\n``` python\nfrom pydantic import BaseModel\nfrom typing import Literal\nfrom anthropic import AnthropicFoundry\n\nclass ClaimIntake(BaseModel):\n    policy_number: str\n    claimant_name: str\n    loss_date: str                       # ISO 8601\n    loss_type: Literal[\n        \"property_damage\", \"bodily_injury\", \"business_interruption\",\n        \"auto_liability\", \"other\",\n    ]\n    estimated_severity_usd: float\n    third_party_involved: bool\n    injuries_reported: bool\n    summary: str\n    escalate_to_adjuster: bool\n\nclient = AnthropicFoundry(resource=\"contoso-ai\")\n\nresponse = client.messages.parse(\n    model=\"claude-opus-5\",\n    max_tokens=2048,\n    system=(\n        \"You are a claims intake analyst. Extract only what is stated or \"\n        \"clearly implied in the submission. If severity is not stated, \"\n        \"estimate conservatively from comparable losses.\"\n    ),\n    messages=[{\"role\": \"user\", \"content\": submission_text}],\n    output_format=ClaimIntake,\n)\n\nclaim = response.parsed_output      # a ClaimIntake instance, already validated\nif claim.escalate_to_adjuster:\n    enqueue_for_adjuster(claim)\n```\n\nThe TypeScript equivalent, using Zod:\n\n``` python\nimport { z } from \"zod\";\nimport AnthropicFoundry from \"@anthropic-ai/foundry-sdk\";\nimport { zodOutputFormat } from \"@anthropic-ai/sdk/helpers/zod\";\n\nconst ClaimIntake = z.object({\n  policy_number: z.string(),\n  claimant_name: z.string(),\n  loss_date: z.string(),\n  loss_type: z.enum([\n    \"property_damage\", \"bodily_injury\", \"business_interruption\",\n    \"auto_liability\", \"other\",\n  ]),\n  estimated_severity_usd: z.number(),\n  third_party_involved: z.boolean(),\n  injuries_reported: z.boolean(),\n  summary: z.string(),\n  escalate_to_adjuster: z.boolean(),\n});\n\nconst client = new AnthropicFoundry({ resource: \"contoso-ai\" });\n\nconst response = await client.messages.parse({\n  model: \"claude-opus-5\",\n  max_tokens: 2048,\n  messages: [{ role: \"user\", content: submissionText }],\n  output_config: { format: zodOutputFormat(ClaimIntake) },\n});\n```\n\n## 2. Web Search: current information, with citations, without a crawler\n\n### What it does\n\nAdd one tool to the request and Claude decides when to search, runs as many searches as it needs within your limit, and returns an answer with citations attached to the specific spans it drew from. You do not run a crawler, manage an index, or write a re-ranker.\n\nVersion available is `web_search_20250305`\n\n— basic search\n\n```\nresponse = client.messages.create(\n    model=\"claude-opus-5\",\n    max_tokens=4096,\n    messages=[{\"role\": \"user\", \"content\": \"What's the current state of the EU AI Act's GPAI obligations?\"}],\n    tools=[{\"type\": \"web_search_20250305\", \"name\": \"web_search\", \"max_uses\": 5}],\n)\n```\n\n### Dynamic filtering: the token economics change\n\nWith basic search, every result loads into the context window in full — including the boilerplate, the nav chrome, and the four paragraphs that had nothing to do with your question. On a research-heavy request that is tens of thousands of wasted input tokens per turn.\n\nWith `web_search_20260209`\n\nand later, Claude instead writes and runs code that filters results *before* they reach context, keeping only relevant content. Mechanically, search runs from inside the code execution tool: on these versions `allowed_callers`\n\ndefaults to `[\"code_execution_20260120\"]`\n\n, and Foundry provisions the code execution the request needs automatically. You do not add code execution to your *tools* array, and there is no extra charge for those calls beyond standard token costs.\n\nTo force direct calls without dynamic filtering, set `allowed_callers: [\"direct\"]`\n\n. Models that do not support programmatic tool calling require this; without it you get a 400 telling you so.\n\n### Use case: regulatory change monitoring at a global bank\n\nA Tier 1 bank’s regulatory affairs team tracks rule changes across a dozen jurisdictions. The old process was a team of analysts with RSS feeds and a shared inbox; the median time from publication to an internal impact note was six days.\n\nThe rebuild is a nightly Azure Container Apps job. The critical design choice is not the prompt — it is `allowed_domains`\n\n. Regulatory monitoring is exactly the case where you cannot afford a secondary source paraphrasing a rule incorrectly:\n\n```\nREGULATOR_DOMAINS = [\n    \"eba.europa.eu\", \"esma.europa.eu\", \"eur-lex.europa.eu\",\n    \"federalreserve.gov\", \"sec.gov\", \"occ.gov\",\n    \"bankofengland.co.uk\", \"fca.org.uk\",\n    \"mas.gov.sg\", \"apra.gov.au\",\n]\n\nresponse = client.messages.create(\n    model=\"claude-opus-5\",\n    max_tokens=8192,\n    system=(\n        \"You monitor prudential and conduct regulation for a global bank. \"\n        \"Report only changes published in the last 7 days. For each change, \"\n        \"state the regulator, the instrument, the effective date, and the \"\n        \"business lines affected. Do not speculate beyond the source text.\"\n    ),\n    messages=[{\"role\": \"user\", \"content\": \"What changed this week in capital and liquidity rules?\"}],\n    tools=[{\n        \"type\": \"web_search_20260318\",\n        \"name\": \"web_search\",\n        \"max_uses\": 12,\n        \"allowed_domains\": REGULATOR_DOMAINS,\n        \"user_location\": {\n            \"type\": \"approximate\",\n            \"city\": \"London\",\n            \"region\": \"England\",\n            \"country\": \"GB\",\n            \"timezone\": \"Europe/London\",\n        },\n    }],\n)\njs\nconst response = await client.messages.create({\n  model: \"claude-opus-5\",\n  max_tokens: 8192,\n  system: \"You monitor prudential and conduct regulation for a global bank. ...\",\n  messages: [{ role: \"user\", content: \"What changed this week in capital and liquidity rules?\" }],\n  tools: [{\n    type: \"web_search_20260318\",\n    name: \"web_search\",\n    max_uses: 12,\n    allowed_domains: REGULATOR_DOMAINS,\n    user_location: {\n      type: \"approximate\", city: \"London\", region: \"England\",\n      country: \"GB\", timezone: \"Europe/London\",\n    },\n  }],\n});\n```\n\n`allowed_domains`\n\nand `blocked_domains`\n\nare mutually exclusive — send both and you get a 400. Entries are bare domains with an optional path (`example.com`\n\n, `example.com/blog`\n\n), no scheme.\n\n## 3. Web Fetch: read the document you have given\n\n### What it does\n\nWhere web search discovers, web fetch reads. Point it at a URL and it returns full page text or, for PDFs, base64 document content that is processed exactly like a directly attached PDF.\n\nVersions, again meaningful:\n\n`web_fetch_20250910`\n\n— basic fetch\n\nUse case: third-party risk assessment at a healthcare system\n\nA hospital network onboards roughly 40 SaaS vendors a quarter. Each triggers a security review: read the vendor’s trust centre, their subprocessor list, their most recent SOC 2 scope summary, their DPA, and their status page history. An analyst spends two to three hours per vendor reading PDFs.\n\n```\nVENDOR_DOCS = [\n    \"https://vendor.example.com/trust\",\n    \"https://vendor.example.com/legal/subprocessors\",\n    \"https://vendor.example.com/security/soc2-scope.pdf\",\n    \"https://vendor.example.com/legal/dpa.pdf\",\n]\n\nresponse = client.messages.create(\n    model=\"claude-opus-5\",\n    max_tokens=8192,\n    system=(\n        \"You are a third-party risk analyst for a healthcare system subject to \"\n        \"HIPAA. Assess each vendor against: data residency, subprocessor \"\n        \"disclosure, breach notification SLA, encryption at rest and in transit, \"\n        \"BAA availability, and SOC 2 scope coverage. Cite the source for every \"\n        \"finding. If a control is not addressed in the documents, say so \"\n        \"explicitly rather than inferring.\"\n    ),\n    messages=[{\n        \"role\": \"user\",\n        \"content\": \"Assess this vendor:\\n\" + \"\\n\".join(VENDOR_DOCS),\n    }],\n    tools=[{\n        \"type\": \"web_fetch_20260318\",\n        \"name\": \"web_fetch\",\n        \"max_uses\": 8,\n        \"allowed_domains\": [\"vendor.example.com\"],\n        \"citations\": {\"enabled\": True},\n        \"max_content_tokens\": 60000,\n    }],\n)\njs\nconst response = await client.messages.create({\n  model: \"claude-opus-5\",\n  max_tokens: 8192,\n  system: \"You are a third-party risk analyst for a healthcare system ...\",\n  messages: [{ role: \"user\", content: `Assess this vendor:\\n${VENDOR_DOCS.join(\"\\n\")}` }],\n  tools: [{\n    type: \"web_fetch_20260318\",\n    name: \"web_fetch\",\n    max_uses: 8,\n    allowed_domains: [\"vendor.example.com\"],\n    citations: { enabled: true },\n    max_content_tokens: 60000,\n  }],\n});\n```\n\nThree parameters are doing real work here. `citations: { enabled: true }`\n\n— unlike web search, citations are **off by default** for fetch, and for a risk assessment that is exactly backwards, so turn them on. `allowed_domains`\n\nprevents the model from wandering off to a marketing blog. `max_content_tokens`\n\ntruncates oversized text content before it enters context — with one important caveat covered below.\n\nBudget accordingly: an average 10 kB web page is roughly 2,500 tokens, a 100 kB documentation page roughly 25,000, and a 500 kB research-paper PDF roughly 125,000. Four documents of that size will consume a serious fraction of your context window in a single turn.\n\n### Combining search and fetch\n\nThe highest-leverage pattern in this release is enabling both tools together. When a user names a specific document without giving a URL — “read the README from the anthropics/anthropic-sdk-python repo,” “pull up the vendor’s latest DPA” — Claude uses search to locate it, then fetch to read it in full:\n\n```\nresponse = client.messages.create(\n    model=\"claude-opus-5\",\n    max_tokens=8192,\n    messages=[{\n        \"role\": \"user\",\n        \"content\": (\n            \"Find the three most recent independent analyses of hospital \"\n            \"ransomware incidents in 2026 and give me a detailed comparison \"\n            \"of the attack vectors described.\"\n        ),\n    }],\n    tools=[\n        {\"type\": \"web_search_20260318\", \"name\": \"web_search\", \"max_uses\": 5},\n        {\n            \"type\": \"web_fetch_20260318\",\n            \"name\": \"web_fetch\",\n            \"max_uses\": 5,\n            \"citations\": {\"enabled\": True},\n            \"max_content_tokens\": 50000,\n        },\n    ],\n)\n```\n\nClaude searches, picks the most promising results, fetches them in full, and analyses with citations. Search gives you breadth cheaply; fetch gives you depth on the handful of sources that matter.\n\n## 4. MCP Connector: your systems of record, without an MCP client\n\n### What it does\n\nModel Context Protocol has become the de facto standard for exposing enterprise systems to models. MCP connector lets you point the Messages API at remote MCP servers directly — no client implementation, no session management, no tool-schema translation layer. The service performs the connection and the tool calls on your behalf.\n\nThe API has two halves: `mcp_servers`\n\ndefines connections, and an `mcp_toolset`\n\nentry in *tools* defines which of that server’s tools are enabled and how.\n\n```\nresponse = client.beta.messages.create(\n    model=\"claude-opus-5\",\n    max_tokens=4096,\n    messages=[{\"role\": \"user\", \"content\": \"What's blocking the payments release?\"}],\n    mcp_servers=[{\n        \"type\": \"url\",\n        \"url\": \"https://mcp.contoso.com/jira/sse\",\n        \"name\": \"jira\",\n        \"authorization_token\": jira_oauth_token,\n    }],\n    tools=[{\"type\": \"mcp_toolset\", \"mcp_server_name\": \"jira\"}],\n    betas=[\"mcp-client-2025-11-20\"],\n)\n```\n\nNote `client.beta.messages.create`\n\nand the *betas* header — the MCP connector is in beta on Foundry, as it is on the Claude API.\n\n### Use case: an internal IT support agent\n\nA manufacturer’s IT service desk handles 12,000 tickets a month. Roughly 40% are resolvable through a fixed sequence: look up the user in the directory, check their device compliance state, search the knowledge base, and either apply a known fix or escalate with context attached. The team already runs MCP servers for ServiceNow, Intune, and their Confluence knowledge base — built for their internal Claude Code deployment.\n\nMCP connector lets the same servers back a customer-facing agent with no new integration work. The interesting part is the tool governance:\n\n```\nresponse = client.beta.messages.create(\n    model=\"claude-opus-5\",\n    max_tokens=4096,\n    system=(\n        \"You are an IT support agent. Diagnose using read-only tools first. \"\n        \"Never make a change without stating what you are about to do.\"\n    ),\n    messages=[{\"role\": \"user\", \"content\": user_ticket}],\n    mcp_servers=[\n        {\"type\": \"url\", \"url\": \"https://mcp.contoso.com/servicenow/sse\",\n         \"name\": \"servicenow\", \"authorization_token\": snow_token},\n        {\"type\": \"url\", \"url\": \"https://mcp.contoso.com/intune/sse\",\n         \"name\": \"intune\", \"authorization_token\": intune_token},\n        {\"type\": \"url\", \"url\": \"https://mcp.contoso.com/confluence/sse\",\n         \"name\": \"kb\", \"authorization_token\": kb_token},\n    ],\n    tools=[\n        # ServiceNow: everything except the destructive operations\n        {\n            \"type\": \"mcp_toolset\",\n            \"mcp_server_name\": \"servicenow\",\n            \"configs\": {\n                \"delete_incident\": {\"enabled\": False},\n                \"bulk_close_incidents\": {\"enabled\": False},\n                \"modify_sla\": {\"enabled\": False},\n            },\n        },\n        # Intune: strict allowlist, read-only\n        {\n            \"type\": \"mcp_toolset\",\n            \"mcp_server_name\": \"intune\",\n            \"default_config\": {\"enabled\": False},\n            \"configs\": {\n                \"get_device_compliance\": {\"enabled\": True},\n                \"list_user_devices\": {\"enabled\": True},\n            },\n        },\n        # Knowledge base: large, rarely all needed at once\n        {\n            \"type\": \"mcp_toolset\",\n            \"mcp_server_name\": \"kb\",\n            \"default_config\": {\"defer_loading\": True},\n        },\n    ],\n    betas=[\"mcp-client-2025-11-20\"],\n)\njs\nconst response = await client.beta.messages.create({\n  model: \"claude-opus-5\",\n  max_tokens: 4096,\n  system: \"You are an IT support agent. Diagnose using read-only tools first. ...\",\n  messages: [{ role: \"user\", content: userTicket }],\n  mcp_servers: [\n    { type: \"url\", url: \"https://mcp.contoso.com/intune/sse\", name: \"intune\",\n      authorization_token: intuneToken },\n  ],\n  tools: [{\n    type: \"mcp_toolset\",\n    mcp_server_name: \"intune\",\n    default_config: { enabled: false },\n    configs: {\n      get_device_compliance: { enabled: true },\n      list_user_devices: { enabled: true },\n    },\n  }],\n  betas: [\"mcp-client-2025-11-20\"],\n});\n```\n\nThree patterns are worth naming because they map directly onto how enterprises actually govern agents:\n\n**Denylist**— enable everything, disable the destructive operations. Good default when the server is trusted and broad capability is useful.**Allowlist**—`default_config: {enabled: false}`\n\n, then enable named tools. The right posture for anything touching identity, endpoints, or money. This is how you build a genuinely read-only agent.**Deferred**—`default_config: {defer_loading: true}`\n\n, which hands the server’s tools to tool search rather than loading them into context. Covered in the next section.\n\nConfiguration merges with precedence: per-tool *configs* beats set-level `default_config`\n\nbeats system defaults.\n\n### Response blocks\n\nMCP tool calls appear as `mcp_tool_use`\n\nand `mcp_tool_result`\n\nblocks, with `server_name`\n\nidentifying the source — useful for per-server audit logging:\n\n```\nfor block in response.content:\n    if block.type == \"mcp_tool_use\":\n        audit_log.record(server=block.server_name, tool=block.name, args=block.input)\n```\n\n## 5. Tool Search: scaling past the point where agents get confused\n\n### The problem\n\nTwo failure modes appear at the same threshold, and both are unintuitive to teams whose agent works fine with eight tools.\n\n**Context bloat.** A modest multi-server setup — GitHub, Slack, Sentry, Grafana, Splunk — consumes roughly **55,000 tokens in tool definitions before the model does any work**. That is context you paid for and cannot use, on every single turn.\n\n**Selection accuracy collapse.** Claude’s ability to pick the right tool degrades once you exceed roughly **30–50 available tools**. Not gracefully. The agent starts calling `search_issues`\n\nwhen it wanted `search_pull_requests`\n\n, and your evals get noisy in a way that looks like a prompting problem but is not.\n\n### What it does\n\nTool search inverts the loading model. Instead of every definition entering context up front, Claude searches your catalogue and loads only what it needs — typically 3–5 tools per request, cutting definition tokens by **over 85%**. Because the working set stays small, selection accuracy stays high across thousands of tools.\n\nTwo variants:\n\n`tool_search_tool_regex_20251119`\n\n— Claude writes Python`re.search()`\n\npatterns (max 200 characters, case-insensitive)`tool_search_tool_bm25_20251119`\n\n— Claude writes natural-language queries (max 500 characters)\n\nBoth search tool names, descriptions, argument names, *and* argument descriptions.\n\n```\nresponse = client.messages.create(\n    model=\"claude-opus-5\",\n    max_tokens=4096,\n    messages=[{\"role\": \"user\", \"content\": \"Open a Sev-2 for the checkout latency spike and page the on-call.\"}],\n    tools=[\n        {\"type\": \"tool_search_tool_regex_20251119\", \"name\": \"tool_search_tool_regex\"},\n        # ~3-5 hot tools stay loaded\n        {\"name\": \"search_incidents\", \"description\": \"...\", \"input_schema\": {...}},\n        # everything else is deferred\n        {\"name\": \"create_incident\", \"description\": \"...\", \"input_schema\": {...},\n         \"defer_loading\": True},\n        {\"name\": \"page_oncall\", \"description\": \"...\", \"input_schema\": {...},\n         \"defer_loading\": True},\n        # ... 400 more\n    ],\n)\n```\n\nThe mental model that trips people up: `defer_loading`\n\n**controls what enters the context window, not what you send.** You still transmit every tool definition in the *tools* array on every request — the API needs them server-side to run the search and expand `tool_reference`\n\nblocks. At least one tool must remain non-deferred; normally that is the tool search tool itself. Never set `defer_loading: true`\n\non the tool search tool, and note that deferring *every* tool returns a 400: `At least one tool must have defer_loading=false.`\n\n### Use case: a field-service agent over 600 tools\n\nAn industrial equipment manufacturer runs a field-service agent for 3,000 technicians. It spans nine MCP servers — parts inventory, warranty, CRM, scheduling, telematics, shipping, billing, a document store, and a diagnostics service — for a combined 600-plus tools. Loaded eagerly, tool definitions alone consumed most of a 200k context window and the agent’s tool selection was unreliable enough that the pilot nearly died.\n\nWith MCP servers, you do not set `defer_loading`\n\non individual tool definitions. You set it once on the toolset:\n\n```\nresponse = client.beta.messages.create(\n    model=\"claude-opus-5\",\n    max_tokens=8192,\n    system=(\n        \"You support field technicians. You can search for tools covering parts \"\n        \"inventory, warranty claims, customer records, scheduling, telematics, \"\n        \"shipping, billing, service documentation, and diagnostics.\"\n    ),\n    messages=[{\"role\": \"user\", \"content\": (\n        \"Unit SN-44812 is throwing a hydraulic pressure fault. Check whether \"\n        \"it's under warranty, find the replacement seal kit, and see if we can \"\n        \"get it on site by Thursday.\"\n    )}],\n    mcp_servers=[\n        {\"type\": \"url\", \"url\": \"https://mcp.contoso.com/parts/sse\",\n         \"name\": \"parts\", \"authorization_token\": parts_token},\n        {\"type\": \"url\", \"url\": \"https://mcp.contoso.com/warranty/sse\",\n         \"name\": \"warranty\", \"authorization_token\": warranty_token},\n        {\"type\": \"url\", \"url\": \"https://mcp.contoso.com/logistics/sse\",\n         \"name\": \"logistics\", \"authorization_token\": logistics_token},\n        # ... six more\n    ],\n    tools=[\n        {\"type\": \"tool_search_tool_bm25_20251119\", \"name\": \"tool_search_tool_bm25\"},\n        {\"type\": \"mcp_toolset\", \"mcp_server_name\": \"parts\",\n         \"default_config\": {\"defer_loading\": True},\n         \"configs\": {\"search_parts\": {\"defer_loading\": False}}},\n        {\"type\": \"mcp_toolset\", \"mcp_server_name\": \"warranty\",\n         \"default_config\": {\"defer_loading\": True}},\n        {\"type\": \"mcp_toolset\", \"mcp_server_name\": \"logistics\",\n         \"default_config\": {\"defer_loading\": True}},\n    ],\n    betas=[\"mcp-client-2025-11-20\"],\n)\n```\n\n`search_parts`\n\nis the single most-used tool in the system, so it stays hot with `defer_loading: false`\n\nwhile the rest of the parts server defers. That is the pattern: keep your 3–5 highest-frequency tools loaded, defer the long tail.\n\nNote the system prompt. Telling the model what *categories* of tools exist measurably improves search quality — it cannot search for capabilities it does not know to look for.\n\n### The response flow\n\n```\n{\n  \"type\": \"server_tool_use\",\n  \"id\": \"srvtoolu_01ABC123\",\n  \"name\": \"tool_search_tool_bm25\",\n  \"input\": { \"query\": \"warranty coverage lookup by serial number\", \"limit\": 10 }\n}\n```\n\nfollowed by\n\n```\n{\n  \"type\": \"tool_search_tool_result\",\n  \"tool_use_id\": \"srvtoolu_01ABC123\",\n  \"content\": {\n    \"type\": \"tool_search_tool_search_result\",\n    \"tool_references\": [{ \"type\": \"tool_reference\", \"tool_name\": \"get_warranty_status\" }]\n  }\n}\n```\n\nThe API expands `tool_reference`\n\nblocks into full definitions before Claude sees them. You never expand them yourself. Never return a `tool_result`\n\nfor the `srvtoolu_...`\n\nID — the API rejects it. Pass the assistant’s content back unchanged on the next turn, along with the same full *tools* array, and Claude can reuse discovered tools in later turns without searching again.\n\n## Operational checklist\n\nBefore you ship any of this to production on Foundry:\n\n**Deployment.** Everything in this post works on both hosting options. Choose Hosted on Azure if your workload needs prompts and completions to remain within Azure, or US Data Zone Standard to keep inference within the United States. Choose Hosted on Anthropic if you need a model that is not yet hosted on Azure.**Auth.** Use Entra ID with Azure RBAC rather than API keys. Tokens expire after about an hour — refresh them.**Cost controls.**`max_uses`\n\non search and fetch.`max_content_tokens`\n\non fetch.`defer_loading`\n\non large toolsets. Budget web search at $10 per 1,000 searches; fetch and tool search add no per-call charge. All of it bills as Claude Consumption Units through Azure Marketplace, metered hourly and invoiced monthly in arrears.**Data handling.** Structured outputs are ZDR-processed but schemas are cached 24 hours — no PHI in schemas. MCP connector’s server exchange is not covered by ZDR. Get both reviewed.**Security.** Treat`allowed_domains`\n\non web fetch as a security control against prompt injection. Use allowlist-style`mcp_toolset`\n\nconfigs for anything touching identity, endpoints, or funds. Verify denylists in CI, because unknown tool names warn rather than error.**Resilience.** Handle`pause_turn`\n\non search. Echo`encrypted_content`\n\nbyte-for-byte. Check`stop_reason`\n\nbefore parsing structured output. Implement exponential backoff — Foundry does not surface Anthropic’s rate-limit headers.**Observability.** Log`request-id`\n\nand*apim-request-id*. Route to Azure Monitor and Log Analytics; Anthropic recommends at least a 30-day rolling retention. Track which tools tool search discovers and iterate on descriptions.**Not available on Foundry.** Message Batches API, Admin API, Models API, Compliance API, Claude Managed Agents, server-side fallback, and the Advisor tool. Plan around them.\n\n## Where to start\n\nIf you are picking one thing to try this week, pick the one that matches the pain you already have.\n\nData pipeline with a retry loop and a dead-letter queue? **Structured outputs.** It is the smallest change with the most immediate reliability win — a schema and one parameter.\n\nAnalysts manually reading source documents? **Web search plus web fetch**, domain-restricted, citations on.\n\nAn MCP server already running for your internal Claude Code deployment? **MCP connector.** The integration work is done; you are pointing a new consumer at it.\n\nAn agent that works in demos and gets confused in production? Count your tools. Past 30, it is **tool search**, not your prompt.\n\nThe through-line is that the platform now owns the scaffolding. What is left for you to build is the part that is actually your business.", "url": "https://wpnews.pro/news/from-single-call-to-agents-five-new-claude-capabilities-available-in-ms-foundry", "canonical_source": "https://devblogs.microsoft.com/foundry/five-new-claude-capabilities-now-available-in-foundry/", "published_at": "2026-08-29 18:42:27+00:00", "updated_at": "2026-08-29 19:18:41.206064+00:00", "lang": "en", "topics": ["artificial-intelligence", "ai-products", "ai-infrastructure", "ai-agents"], "entities": ["Anthropic", "Microsoft Foundry", "Azure", "Claude"], "alternates": {"html": "https://wpnews.pro/news/from-single-call-to-agents-five-new-claude-capabilities-available-in-ms-foundry", "markdown": "https://wpnews.pro/news/from-single-call-to-agents-five-new-claude-capabilities-available-in-ms-foundry.md", "text": "https://wpnews.pro/news/from-single-call-to-agents-five-new-claude-capabilities-available-in-ms-foundry.txt", "jsonld": "https://wpnews.pro/news/from-single-call-to-agents-five-new-claude-capabilities-available-in-ms-foundry.jsonld"}}