Skip to content

Commit daab7b6

Browse files
lindazqliCopilot
andcommitted
Scope Toolboxes preview header to skills + management REST only
The Foundry-Features: Toolboxes=V1Preview header is not required for regular tool consumption over the toolbox MCP endpoint. Remove it from the verify, Microsoft Agent Framework, Copilot SDK, and hosted-agent samples, and correct the prose that claimed it was mandatory on every request. Keep it only where it is actually needed: consuming skills as MCP resources (10b) and the preview management REST API (5). Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
1 parent c17ffbb commit daab7b6

1 file changed

Lines changed: 16 additions & 16 deletions

File tree

notebooks/mastering-foundry-toolbox.ipynb

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -185,8 +185,9 @@
185185
" starts with `if skip(...): ...`.\n",
186186
"- `mcp_token()` - a bearer token scoped to `https://ai.azure.com/.default`, the audience the\n",
187187
" toolbox MCP endpoint expects.\n",
188-
"- `TOOLBOX_HEADERS` - **every** call to a toolbox MCP endpoint must carry\n",
189-
" `Foundry-Features: Toolboxes=V1Preview`. Forgetting it is the #1 cause of 404s.\n",
188+
"- `TOOLBOX_HEADERS` - the `Foundry-Features: Toolboxes=V1Preview` header. Needed **only** for consuming\n",
189+
" **skills** as MCP resources (section 10b) and the preview management REST API (section 5) - regular\n",
190+
" tool calls don't need it.\n",
190191
"- `created_resources` - a tracker the cleanup section walks in reverse."
191192
]
192193
},
@@ -242,7 +243,7 @@
242243
"# The MCP endpoint audience is ai.azure.com (NOT management.azure.com).\n",
243244
"TOOLBOX_SCOPE = \"https://ai.azure.com/.default\"\n",
244245
"\n",
245-
"# Mandatory on every toolbox MCP request while the feature is in preview.\n",
246+
"# Only needed for skills MCP resources (10b) and the preview management REST API (5); not tool calls.\n",
246247
"TOOLBOX_HEADERS = {\"Foundry-Features\": \"Toolboxes=V1Preview\"}\n",
247248
"\n",
248249
"credential = DefaultAzureCredential()\n",
@@ -1765,8 +1766,8 @@
17651766
"| **Developer** | `{project}/toolboxes/{name}/versions/{version}/mcp?api-version=v1` | one pinned version |\n",
17661767
"| **Consumer** | `{project}/toolboxes/{name}/mcp?api-version=v1` | the **default** version |\n",
17671768
"\n",
1768-
"Both require the bearer token (scope `https://ai.azure.com/.default`) **and** the\n",
1769-
"`Foundry-Features: Toolboxes=V1Preview` header on every request."
1769+
"Both require the bearer token (scope `https://ai.azure.com/.default`). The\n",
1770+
"`Foundry-Features: Toolboxes=V1Preview` header is only needed for skills-resource calls (section 10b), not for regular tool calls."
17701771
]
17711772
},
17721773
{
@@ -1818,8 +1819,8 @@
18181819
"claim (tool count *and* input-schema payload size). Then we run a `tool_search` -> `call_tool`\n",
18191820
"round-trip and read each tool's `_meta.tool_configuration` (which carries `require_approval`).\n",
18201821
"\n",
1821-
"We use the `mcp` SDK's streamable-HTTP client, passing the bearer token and the mandatory\n",
1822-
"preview header. If a tool's connection uses `oauth2`, the first call returns `CONSENT_REQUIRED`\n",
1822+
"We use the `mcp` SDK's streamable-HTTP client, passing the bearer token (no preview\n",
1823+
"header is needed for tool calls). If a tool's connection uses `oauth2`, the first call returns `CONSENT_REQUIRED`\n",
18231824
"(`-32006`) with a consent URL - we surface it so you can consent and retry."
18241825
]
18251826
},
@@ -1844,7 +1845,7 @@
18441845
"\n",
18451846
"async def _list_tools(url: str):\n",
18461847
" \"\"\"tools/list against a specific toolbox MCP endpoint.\"\"\"\n",
1847-
" headers = {**TOOLBOX_HEADERS, \"Authorization\": f\"Bearer {mcp_token()}\"}\n",
1848+
" headers = {\"Authorization\": f\"Bearer {mcp_token()}\"}\n",
18481849
" async with streamablehttp_client(url, headers=headers) as (read, write, _):\n",
18491850
" async with ClientSession(read, write) as session:\n",
18501851
" await session.initialize()\n",
@@ -1869,7 +1870,7 @@
18691870
" assert \"tool_search\" in after_names, \"Tool Search not active - is the default version search-first?\"\n",
18701871
"\n",
18711872
" # Drive the meta-tool, then read approval config off a listed tool.\n",
1872-
" headers = {**TOOLBOX_HEADERS, \"Authorization\": f\"Bearer {mcp_token()}\"}\n",
1873+
" headers = {\"Authorization\": f\"Bearer {mcp_token()}\"}\n",
18731874
" async with streamablehttp_client(CONSUMER_URL, headers=headers) as (read, write, _):\n",
18741875
" async with ClientSession(read, write) as session:\n",
18751876
" await session.initialize()\n",
@@ -1958,7 +1959,7 @@
19581959
"\n",
19591960
"The whole point of one governed endpoint is that *any* MCP client can use it unchanged. Here are\n",
19601961
"three: **Microsoft Agent Framework**, **LangGraph**, and the **Copilot SDK**. Each just needs the\n",
1961-
"consumer URL, a bearer token, and the preview header."
1962+
"consumer URL and a bearer token."
19621963
]
19631964
},
19641965
{
@@ -1985,8 +1986,8 @@
19851986
"source": [
19861987
"# --- Microsoft Agent Framework -------------------------------------------------\n",
19871988
"# MAF speaks MCP natively via MCPStreamableHTTPTool. Point it at the consumer URL.\n",
1988-
"# Auth note: the toolbox MCP endpoint needs a bearer token + the preview header on\n",
1989-
"# EVERY request, including the initialize handshake. MAF's header_provider only injects\n",
1989+
"# Auth note: the toolbox MCP endpoint needs a bearer token on every request, including the\n",
1990+
"# initialize handshake. MAF's header_provider only injects\n",
19901991
"# on tool *calls*, so we hand it a pre-authenticated http_client whose default headers\n",
19911992
"# cover connect + initialize. load_prompts=False skips a prompts/list the endpoint\n",
19921993
"# doesn't serve, and we build FoundryChatClient from the endpoint + credential (the\n",
@@ -1997,7 +1998,7 @@
19971998
"\n",
19981999
"async def run_maf():\n",
19992000
" http = httpx.AsyncClient(\n",
2000-
" headers={**TOOLBOX_HEADERS, \"Authorization\": f\"Bearer {mcp_token()}\"},\n",
2001+
" headers={\"Authorization\": f\"Bearer {mcp_token()}\"},\n",
20012002
" follow_redirects=True,\n",
20022003
" timeout=httpx.Timeout(30.0, read=300.0),\n",
20032004
" )\n",
@@ -2105,7 +2106,7 @@
21052106
"source": [
21062107
"# --- Copilot SDK ---------------------------------------------------------------\n",
21072108
"# The GitHub Copilot SDK (pip install github-copilot-sdk; Python 3.11+) consumes the toolbox as a\n",
2108-
"# remote MCP server - same consumer URL, same bearer token + preview header. Remote MCP servers are\n",
2109+
"# remote MCP server - same consumer URL, same bearer token. Remote MCP servers are\n",
21092110
"# configured through create_session(..., mcp_servers={...}) with type \"http\".\n",
21102111
"# Docs: https://docs.github.com/copilot/how-tos/copilot-sdk/features/mcp\n",
21112112
"if not os.getenv(\"PROJECT_ENDPOINT\"):\n",
@@ -2127,7 +2128,7 @@
21272128
" \"foundry_toolbox\": {\n",
21282129
" \"type\": \"http\",\n",
21292130
" \"url\": CONSUMER_URL,\n",
2130-
" \"headers\": {**TOOLBOX_HEADERS, \"Authorization\": f\"Bearer {mcp_token()}\"},\n",
2131+
" \"headers\": {\"Authorization\": f\"Bearer {mcp_token()}\"},\n",
21312132
" \"tools\": [\"*\"], # expose every toolbox tool (tool_search included)\n",
21322133
" },\n",
21332134
" },\n",
@@ -2188,7 +2189,6 @@
21882189
"# toolbox_tool = MCPStreamableHTTPTool(\n",
21892190
"# name=\"foundry_toolbox\",\n",
21902191
"# url=CONSUMER_URL, # same consumer URL as local\n",
2191-
"# headers=TOOLBOX_HEADERS, # token injected by the runtime\n",
21922192
"# )\n",
21932193
"# return ChatAgent(\n",
21942194
"# chat_client=AzureAIAgentClient(project_client=project, model=MODEL_DEPLOYMENT),\n",

0 commit comments

Comments
 (0)