Python: Make foundry toolbox MCP skills sample self-contained#7099
Python: Make foundry toolbox MCP skills sample self-contained#7099giles17 wants to merge 2 commits into
Conversation
Rework sample 12 (foundry_toolbox_mcp_skills) so users can build it from zero with azd, mirroring samples 04 and 09: - Bundle two single-file SKILL.md skills (support-style, escalation-policy) and a skills-only toolbox.yaml (with one connectionless code_interpreter tool, required by `azd ai toolbox create`). - Rewrite the README as an azd-native, from-zero guide (create skills -> create toolbox -> set TOOLBOX_ENDPOINT -> run) and fix the stale MCPSkillsSource API description to match main.py. - Switch config from TOOLBOX_NAME to the versioned TOOLBOX_ENDPOINT (.env.example, agent.yaml, agent.manifest.yaml); add .azdignore. Also enable the sample to run unattended behind ResponsesHostServer: - Forward disable_load_skill_approval / disable_read_skill_resource_approval / disable_run_skill_script_approval from FoundryToolbox.as_skills_provider() to the underlying SkillsProvider, so load_skill needs no approval round-trip (the Responses host runs without an AgentSession, which the default approval flow requires). main.py now uses as_skills_provider(disable_load_skill_approval=True). - Add unit tests covering the default and overridden approval behaviour. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 4f14f83d-1868-45c1-be1a-12f49a58ac36
There was a problem hiding this comment.
Pull request overview
Updates the Python 12_foundry_toolbox_mcp_skills Responses-hosted sample so it can be run end-to-end starting from an empty Foundry project (bundled skill sources + toolbox definition), and extends FoundryToolbox.as_skills_provider() to optionally disable skill-tool approval requirements for unattended hosting scenarios.
Changes:
- Bundles two single-file
SKILL.mdskills and atoolbox.yaml, and rewrites the sample README into an azd-native “from zero” walkthrough usingTOOLBOX_ENDPOINT. - Updates the sample’s config assets (
agent.yaml,agent.manifest.yaml,.env.example) to use a versioned toolbox endpoint instead ofTOOLBOX_NAME, and excludes authoring inputs from deployment via.azdignore/.dockerignore. - Extends
FoundryToolbox.as_skills_provider()to forwarddisable_*_skill_*_approvaloptions toSkillsProvider, and adds unit tests validating default/override behavior.
Reviewed changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| python/samples/04-hosting/foundry-hosted-agents/responses/12_foundry_toolbox_mcp_skills/toolbox.yaml | Adds a toolbox definition referencing the bundled skills (plus a placeholder tool to satisfy CLI constraints). |
| python/samples/04-hosting/foundry-hosted-agents/responses/12_foundry_toolbox_mcp_skills/skills/support-style/SKILL.md | Adds bundled “support-style” skill source. |
| python/samples/04-hosting/foundry-hosted-agents/responses/12_foundry_toolbox_mcp_skills/skills/escalation-policy/SKILL.md | Adds bundled “escalation-policy” skill source. |
| python/samples/04-hosting/foundry-hosted-agents/responses/12_foundry_toolbox_mcp_skills/README.md | Rewrites docs to a from-zero azd walkthrough and explains progressive disclosure stages/constraints. |
| python/samples/04-hosting/foundry-hosted-agents/responses/12_foundry_toolbox_mcp_skills/main.py | Uses as_skills_provider(disable_load_skill_approval=True) for unattended hosting. |
| python/samples/04-hosting/foundry-hosted-agents/responses/12_foundry_toolbox_mcp_skills/agent.yaml | Switches configuration from TOOLBOX_NAME to TOOLBOX_ENDPOINT. |
| python/samples/04-hosting/foundry-hosted-agents/responses/12_foundry_toolbox_mcp_skills/agent.manifest.yaml | Switches manifest parameterization from TOOLBOX_NAME to TOOLBOX_ENDPOINT. |
| python/samples/04-hosting/foundry-hosted-agents/responses/12_foundry_toolbox_mcp_skills/.env.example | Documents and switches to TOOLBOX_ENDPOINT. |
| python/samples/04-hosting/foundry-hosted-agents/responses/12_foundry_toolbox_mcp_skills/.dockerignore | Excludes bundled skill/toolbox authoring inputs from container builds. |
| python/samples/04-hosting/foundry-hosted-agents/responses/12_foundry_toolbox_mcp_skills/.azdignore | Excludes authoring inputs and local env files from azd packaging/deploy context. |
| python/packages/foundry_hosting/tests/test_toolbox.py | Adds tests for approval override forwarding behavior. |
| python/packages/foundry_hosting/agent_framework_foundry_hosting/_toolbox.py | Adds approval-disabling kwargs to as_skills_provider() and forwards them into SkillsProvider. |
Python Test Coverage Report •
Python Unit Test Overview
|
||||||||||||||||||||||||||||||
| azd ai skill create support-style --file ./skills/support-style/SKILL.md --no-prompt | ||
| azd ai skill create escalation-policy --file ./skills/escalation-policy/SKILL.md --no-prompt |
There was a problem hiding this comment.
Are these verified? Shouldn't we provide the entire skill folder instead of just the md file?
There was a problem hiding this comment.
Yes, I ran both azd ai skill create … --file …/SKILL.md commands against a live Foundry project and confirmed the skills upload and load end-to-end.
Single-file is intentional because providing the folder requires zipping it and foundry serves it as type: archive and this is not handled yet in our framework. Issue tracked here: #6088
| ``` | ||
|
|
||
| When the agent connects to the toolbox MCP endpoint, skills are advertised through a well-known `skill://index.json` discovery resource. The `MCPSkillsSource` in this sample reads `skill://index.json` the first time the agent runs to discover all attached skills, then fetches each `SKILL.md` body on demand via `resources/read`. | ||
| > **Why single files (not ZIPs)?** Uploading a skill as a `.zip` (to bundle supplementary resource files) makes Foundry serve it as `type: archive` in the toolbox's `skill://index.json`. The Python `MCPSkillsSource` only discovers `type: skill-md` entries, so archive skills are silently dropped. Keeping each skill as a single `SKILL.md` ensures both are discovered. |
There was a problem hiding this comment.
We are not using the MCPSkillsSource, do we? The _FoundryToolboxSkillsSource derives only from SkillsSource
Motivation & Context
The
12_foundry_toolbox_mcp_skillssample was not self-contained and did not work end-to-end:04_foundry_toolbox(bundles atoolbox.yaml) and sample09_foundry_skills(bundlesSKILL.mdsources). A user starting from zero was stuck.SkillsProvider(MCPSkillsSource(...))) that did not matchmain.py(FoundryToolbox(...).as_skills_provider()), and used the fragileTOOLBOX_NAME(unversioned) env var instead of a versioned endpoint.SkillsProviderregistersload_skillwith approval required by default, butResponsesHostServerruns the agent without anAgentSession, so the default approval flow (andToolApprovalMiddleware) cannot complete — every skill-loading request returned an unansweredmcp_approval_request.This makes the sample a complete, from-zero, azd-native walkthrough and lets it run unattended behind the Responses host.
Description & Review Guide
What are the major changes?
SKILL.mdskills (support-style,escalation-policy, reused from sample 09) and atoolbox.yamlthat references them. The toolbox includes one connectionlesscode_interpretertool becauseazd ai toolbox createrejects a skills-only toolbox;load_tools=Falsekeeps it hidden from the model, so it stays effectively skills-only. Add.azdignoreand update.dockerignoreso authoring inputs are excluded from the deployed container.TOOLBOX_ENDPOINT→ run), fixing the staleMCPSkillsSourcedescription and documenting the two progressive-disclosure stages this path supports (advertise + load). Config switched fromTOOLBOX_NAMEto the versionedTOOLBOX_ENDPOINTacross.env.example,agent.yaml, andagent.manifest.yaml.agent-framework-foundry-hosting):FoundryToolbox.as_skills_provider()now forwardsdisable_load_skill_approval/disable_read_skill_resource_approval/disable_run_skill_script_approvalto the underlyingSkillsProvider.main.pyusesas_skills_provider(disable_load_skill_approval=True)so the hosted agent loads skills without an approval round-trip. Adds unit tests for the default and overridden behaviour.What is the impact of these changes?
support-style(canarySTYLE-CANARY-3318); an escalation prompt loads both skills (canaryESC-CANARY-7742). The package change is purely additive (new keyword-only args defaulting toFalse), so existing callers are unaffected.type: archive, which the PythonMCPSkillsSourcedoes not currently discover (it only handlestype: skill-md). The README explains this.What do you want reviewers to focus on?
as_skills_provider()is the preferred fix for theResponsesHostServer(noAgentSession) scenario, vs. an alternative.Related Issue
Contribution Checklist
breaking changelabel (or add "[BREAKING]" to the title prefix, before or after any language prefix) — a workflow keeps the label and title prefix in sync automatically.