Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions sdk/ai/azure-ai-projects/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@

* Placeholder

### Sample updates

* Added new Hosted Agent sample `sample_agent_user_identity_isolation.py` under `samples/hosted_agents/`, demonstrating per-user response-chain isolation with delegated end-user identities sent in the `x-ms-user-identity` header.
* Updated Hosted Agent toolbox asset `samples/hosted_agents/assets/toolbox-agent/main.py` to use `FoundryToolbox` and `as_skills_provider()` for toolbox MCP skill discovery and wiring, replacing the earlier manual MCP session, auth, and HTTP client setup.
* Updated Hosted Agent asset requirements under `samples/hosted_agents/assets/toolbox-agent/requirements.txt` to use new versions of `agent-framework-foundry` and `agent-framework-foundry-hosting`

## 2.3.0 (2026-07-01)

Expand Down
2 changes: 1 addition & 1 deletion sdk/ai/azure-ai-projects/assets.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
"AssetsRepo": "Azure/azure-sdk-assets",
"AssetsRepoPrefixPath": "python",
"TagPrefix": "python/ai/azure-ai-projects",
"Tag": "python/ai/azure-ai-projects_762155b7e7"
"Tag": "python/ai/azure-ai-projects_82895b86e0"
}
5 changes: 5 additions & 0 deletions sdk/ai/azure-ai-projects/cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
"dargilco",
"dedup",
"deser",
"devtools",
"dotenv",
"evals",
"FineTuning",
"fspath",
Expand All @@ -35,10 +37,13 @@
"reraises",
"simpleqna",
"skoid",
"testutils",
"Tadmaq",
"Udbk",
"UPIA",
"unsanitized",
"Vnext",
"westus",
"xhigh",
"likert"
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@

with (
DefaultAzureCredential() as credential,
AIProjectClient(endpoint=endpoint, credential=credential, allow_preview=True) as project_client,
AIProjectClient(endpoint=endpoint, credential=credential) as project_client,
):

# ------------------------------------------------------------------
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@
async def main() -> None:
async with (
DefaultAzureCredential() as credential,
AIProjectClient(endpoint=endpoint, credential=credential, allow_preview=True) as project_client,
AIProjectClient(endpoint=endpoint, credential=credential) as project_client,
):

# ------------------------------------------------------------------
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@

with (
DefaultAzureCredential() as credential,
AIProjectClient(endpoint=endpoint, credential=credential, allow_preview=True) as project_client,
AIProjectClient(endpoint=endpoint, credential=credential) as project_client,
):

# ------------------------------------------------------------------
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@

with (
DefaultAzureCredential() as credential,
AIProjectClient(endpoint=endpoint, credential=credential, allow_preview=True) as project_client,
AIProjectClient(endpoint=endpoint, credential=credential) as project_client,
):

# ------------------------------------------------------------------
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@

with (
DefaultAzureCredential() as credential,
AIProjectClient(endpoint=endpoint, credential=credential, allow_preview=True) as project_client,
AIProjectClient(endpoint=endpoint, credential=credential) as project_client,
project_client.get_openai_client() as openai_client,
):

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Copyright (c) Microsoft. All rights reserved.

import asyncio
import os

from agent_framework import Agent
from agent_framework.foundry import FoundryChatClient
from agent_framework_foundry_hosting import ResponsesHostServer # type: ignore[import-untyped]
from azure.identity import DefaultAzureCredential
from dotenv import load_dotenv

load_dotenv()


async def main() -> None:
project_endpoint = os.environ["FOUNDRY_PROJECT_ENDPOINT"]
deployment = os.environ["FOUNDRY_MODEL_NAME"]
store_responses = os.environ.get("AGENT_STORE_RESPONSES", "false").lower() == "true"

with DefaultAzureCredential() as credential:
agent = Agent(
client=FoundryChatClient(
project_endpoint=project_endpoint,
model=deployment,
credential=credential,
allow_preview=True,
),
name=os.environ.get("AGENT_NAME", "BASIC_AGENT"),
instructions=os.environ.get(
"AGENT_INSTRUCTIONS",
),
default_options={"store": store_responses},
)

server = ResponsesHostServer(agent)
await server.run_async()


if __name__ == "__main__":
asyncio.run(main())
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
agent-framework-foundry==1.10.0
agent-framework-foundry-hosting>=1.0.0a260630
python-dotenv
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -2,78 +2,53 @@

import asyncio
import os
from collections.abc import Callable, Generator

import httpx
from typing import Any, cast
from agent_framework import Agent, SkillsProvider
from agent_framework import Agent
from agent_framework.foundry import FoundryChatClient
from agent_framework_foundry_hosting import ResponsesHostServer # type: ignore[import-untyped]
from azure.identity import DefaultAzureCredential, get_bearer_token_provider
from agent_framework_foundry_hosting import FoundryToolbox, ResponsesHostServer
from azure.identity import DefaultAzureCredential
from dotenv import load_dotenv
from mcp.client.session import ClientSession
from mcp.client.streamable_http import streamable_http_client

MCPSkillsSource = cast(Any, __import__("agent_framework", fromlist=["MCPSkillsSource"]).MCPSkillsSource)

# Load environment variables from .env file
load_dotenv()


class ToolboxAuth(httpx.Auth):
"""Attach a fresh Foundry bearer token to every request."""

requires_response_body = True

def __init__(self, token_provider: Callable[[], str]) -> None:
self._token_provider = token_provider

def auth_flow(self, request: httpx.Request) -> Generator[httpx.Request, httpx.Response, None]:
request.headers["Authorization"] = f"Bearer {self._token_provider()}"
yield request


async def main() -> None:
project_endpoint = os.environ["FOUNDRY_PROJECT_ENDPOINT"]
deployment = os.environ["FOUNDRY_MODEL_NAME"]
toolbox_mcp_url = os.environ["MCP_SERVER_URL"]
store_responses = os.environ.get("AGENT_STORE_RESPONSES", "false").lower() == "true"

credential = DefaultAzureCredential()
token_provider = get_bearer_token_provider(credential, "https://ai.azure.com/.default")

async with (
httpx.AsyncClient(
auth=ToolboxAuth(token_provider),
headers={"Foundry-Features": "Routines=V1Preview"},
timeout=httpx.Timeout(30.0, read=300.0),
follow_redirects=True,
) as http_client,
streamable_http_client(
url=toolbox_mcp_url,
http_client=http_client,
) as (read, write, _),
ClientSession(read, write) as session,
):
await session.initialize()

skills_provider = SkillsProvider(MCPSkillsSource(client=session))

agent = Agent(
client=FoundryChatClient(
project_endpoint=project_endpoint,
model=deployment,
credential=credential,
),
name=os.environ.get("AGENT_NAME", "TOOLBOX_AGENT"),
instructions=os.environ.get(
"AGENT_INSTRUCTIONS",
),
context_providers=[skills_provider],
default_options={"store": store_responses},
)

server = ResponsesHostServer(agent)
await server.run_async()
# FoundryToolbox resolves the toolbox endpoint from the environment
# (TOOLBOX_ENDPOINT, or FOUNDRY_PROJECT_ENDPOINT + TOOLBOX_NAME), authenticates
# every request with the credential, and forwards the platform per-request
# call-id. ``load_tools=False`` keeps the toolbox's tools hidden so only its
# Agent Skills (SEP-2640) are surfaced; passing it via ``tools=`` connects the
# MCP session that ``as_skills_provider()`` reads from.
toolbox = FoundryToolbox(url=os.environ["MCP_SERVER_URL"], credential=credential, load_tools=False)

# as_skills_provider() discovers skills from skill://index.json on the toolbox
# MCP session and exposes them as an agent context provider; SKILL.md bodies are
# fetched on demand via resources/read.
skills_provider = toolbox.as_skills_provider()

client = FoundryChatClient(
project_endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"],
model=os.environ["FOUNDRY_MODEL_NAME"],
credential=credential,
)

agent = Agent(
client=client,
name=os.environ.get("AGENT_NAME", "hosted-toolbox-mcp-skills"),
instructions="You are a helpful assistant.",
tools=toolbox,
context_providers=[skills_provider],
# History will be managed by the hosting infrastructure, thus there
# is no need to store history by the service. Learn more at:
# https://developers.openai.com/api/reference/resources/responses/methods/create
default_options={"store": False},
)

server = ResponsesHostServer(agent)
await server.run_async()


if __name__ == "__main__":
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,3 @@
agent-framework-foundry==1.8.2
agent-framework-foundry-hosting==1.0.0a260618
azure-ai-agentserver-core
azure-ai-agentserver-invocations
azure-ai-agentserver-responses
azure-ai-projects
azure-identity
httpx
mcp
agent-framework-foundry==1.10.0
agent-framework-foundry-hosting>=1.0.0a260630
python-dotenv
Loading
Loading