Skip to content

Python: Add Hosted-ToolboxMcpSkills sample#6359

Closed
SergeyMenshykh wants to merge 2 commits into
microsoft:mainfrom
SergeyMenshykh:sergeymenshykh/port-toolbox-mcp-skills-sample
Closed

Python: Add Hosted-ToolboxMcpSkills sample#6359
SergeyMenshykh wants to merge 2 commits into
microsoft:mainfrom
SergeyMenshykh:sergeymenshykh/port-toolbox-mcp-skills-sample

Conversation

@SergeyMenshykh
Copy link
Copy Markdown
Contributor

@SergeyMenshykh SergeyMenshykh commented Jun 5, 2026

Summary

Port of the .NET Hosted-ToolboxMcpSkills sample to Python.

Adds a new hosted agent sample at python/samples/04-hosting/foundry-hosted-agents/responses/12_foundry_toolbox_mcp_skills/ that demonstrates discovering MCP-based skills from a Foundry Toolbox and injecting them as a context provider using SkillsProvider(MCPSkillsSource(...)).

What this sample does

Connects to a Foundry Toolbox MCP endpoint and reads the well-known skill://index.json discovery resource to build a SkillsProvider. The provider implements the Agent Skills progressive-disclosure pattern:

  1. Advertise — skill names and descriptions are injected into the system prompt
  2. Load — full SKILL.md body is fetched on demand when the agent decides a skill is relevant
  3. Read resources — supplementary skill assets are read on demand

Also updates the parent foundry-hosted-agents/README.md table to include the new sample.

Partially solves: #6347

TaoChenOSU and others added 2 commits June 5, 2026 15:18
Port of the .NET Hosted-ToolboxMcpSkills sample (PR microsoft#6175) to Python.

Demonstrates hosting an agent that discovers MCP-based skills from a
Foundry Toolbox MCP endpoint using SkillsProvider(MCPSkillsSource(...))
as a context provider, implementing the Agent Skills progressive-
disclosure pattern (advertise, load, read resources).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings June 5, 2026 14:27
@moonbox3 moonbox3 added documentation Improvements or additions to documentation python labels Jun 5, 2026
@SergeyMenshykh SergeyMenshykh marked this pull request as draft June 5, 2026 14:29
@SergeyMenshykh SergeyMenshykh self-assigned this Jun 5, 2026
@SergeyMenshykh SergeyMenshykh moved this to In Review in Agent Framework Jun 5, 2026
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 5, 2026

Python Test Coverage

Python Test Coverage Report •
FileStmtsMissCoverMissing
packages/foundry_hosting/agent_framework_foundry_hosting
   _responses.py77010386%186–189, 254, 314–315, 329, 332–333, 381–382, 392, 429, 484, 498, 549, 552–556, 575, 578, 584, 586, 640, 645, 676, 679, 684–690, 693–694, 696–697, 707–710, 1011, 1024, 1493–1495, 1497, 1544–1545, 1547–1548, 1550–1551, 1553–1554, 1559, 1568, 1571–1573, 1575, 1589, 1602, 1647–1648, 1650, 1655–1659, 1661, 1668–1669, 1671–1672, 1678, 1680–1684, 1691, 1697, 1719, 1725, 1731, 1733, 1735–1738, 1746, 1748, 1799–1801, 1811–1812
TOTAL38307445588% 

Python Unit Test Overview

Tests Skipped Failures Errors Time
7639 34 💤 0 ❌ 0 🔥 2m 2s ⏱️

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new Python hosted-agent sample that mirrors the .NET “Hosted-ToolboxMcpSkills” scenario: discovering MCP-served Agent Skills from a Foundry Toolbox and injecting them via SkillsProvider(MCPSkillsSource(...)) in a Responses-protocol host. Also improves the existing Foundry Toolbox sample manifest/docs and updates hosting to surface multiple consent links when the Foundry MCP gateway returns a structured consent error payload.

Changes:

  • New sample 12_foundry_toolbox_mcp_skills (code + container + deployment descriptors + docs) showing toolbox skill discovery and progressive disclosure.
  • Updates 04_foundry_toolbox sample to provide a concrete, default PAT-based GitHub MCP connection (with OAuth2 alternative) and clearer README guidance.
  • Enhances Foundry hosting consent interception to parse structured gateway consent errors and emit one OAuthConsentRequestOutputItem per tool source; updates unit tests accordingly.

Reviewed changes

Copilot reviewed 14 out of 14 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
python/samples/04-hosting/foundry-hosted-agents/responses/12_foundry_toolbox_mcp_skills/requirements.txt Sample dependencies for toolbox MCP skills discovery.
python/samples/04-hosting/foundry-hosted-agents/responses/12_foundry_toolbox_mcp_skills/README.md End-to-end documentation for the new hosted sample and skill setup.
python/samples/04-hosting/foundry-hosted-agents/responses/12_foundry_toolbox_mcp_skills/main.py Implements MCP connection + MCPSkillsSourceSkillsProvider injection + Responses host.
python/samples/04-hosting/foundry-hosted-agents/responses/12_foundry_toolbox_mcp_skills/Dockerfile Containerizes the new sample for hosted deployment.
python/samples/04-hosting/foundry-hosted-agents/responses/12_foundry_toolbox_mcp_skills/agent.yaml Container agent deployment descriptor for Foundry hosting.
python/samples/04-hosting/foundry-hosted-agents/responses/12_foundry_toolbox_mcp_skills/agent.manifest.yaml azd ai agent init manifest template for the new sample.
python/samples/04-hosting/foundry-hosted-agents/responses/12_foundry_toolbox_mcp_skills/.env.example Environment variable template for local runs.
python/samples/04-hosting/foundry-hosted-agents/responses/12_foundry_toolbox_mcp_skills/.dockerignore Excludes local artifacts/secrets from Docker build context.
python/samples/04-hosting/foundry-hosted-agents/responses/04_foundry_toolbox/requirements.txt Normalizes sample requirements (uncomments framework deps).
python/samples/04-hosting/foundry-hosted-agents/responses/04_foundry_toolbox/README.md Clarifies default GitHub MCP auth (PAT) and how to switch to OAuth2.
python/samples/04-hosting/foundry-hosted-agents/responses/04_foundry_toolbox/agent.manifest.yaml Makes toolbox/connection resources concrete and prompts for needed parameters.
python/samples/04-hosting/foundry-hosted-agents/README.md Adds the new sample to the hosting samples index table.
python/packages/foundry_hosting/tests/test_responses.py Updates consent parsing tests for structured gateway messages and multi-consent results.
python/packages/foundry_hosting/agent_framework_foundry_hosting/_responses.py Parses structured consent payloads and emits multiple consent request output items.

Comment on lines +328 to +329
else:
logger.warning("Consent URL in error message is not a valid URL: %s", consent_url) # type: ignore
Copy link
Copy Markdown

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated Code Review

Reviewers: 4 | Confidence: 79%

✓ Correctness

The PR refactors consent error handling from a single URL string to a structured JSON payload containing multiple consent errors. The implementation is correct: the JSON parsing is defensive (handles missing '{', malformed JSON, missing keys), the function guarantees a non-empty list when returning non-None (so the handler loop is safe), and the ConsentError dataclass has auto-generated eq making test assertions work correctly. The error code change from -32007 to -32006 is intentional per the protocol update. The sample code properly scopes the MCP session within the async-with block that encloses server.run_async(). No correctness issues found.

✓ Security Reliability

The PR refactors consent error handling from a single URL string to a structured JSON payload with multiple consent errors, adds a new MCP skills sample, and un-comments toolbox configuration in an existing sample. The JSON parsing in consent_url_from_error is well-guarded with try/except and type checks, and degrades gracefully on malformed input. No critical security or reliability issues found. One defense-in-depth suggestion regarding URL scheme validation on extracted consent URLs.

✓ Test Coverage

The test changes correctly update existing tests to match the new consent_url_from_error return type (list[ConsentError] | None) and add a test for the no-JSON-in-message case. However, the new multi-consent-error iteration path in _handle_inner_agent (the for-loop emitting multiple OAuth items) has no integration test exercising it with >1 error. Additionally, the json.JSONDecodeError catch branch in the parser is unreached by any test.

✓ Design Approach

I found one design-level gap in the new hosted MCP-skills sample: unlike the existing toolbox-backed hosted samples, it only supports constructing the MCP URL from project endpoint + toolbox name, so it drops the explicit-endpoint input path that this repo already uses and documents for local development. I did not find a verified blocking issue in the consent-error changes from the files inspected.


Automated review by SergeyMenshykh's agents

):
consent_url = error["error"]["message"] # type: ignore
if isinstance(consent_url, str):
consent_errors.append(ConsentError(name=error.get("name", "Unknown"), consent_url=consent_url)) # type: ignore
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Defense-in-depth: consent_url is extracted from a JSON payload inside an error message and ultimately presented to the user as a clickable link. Consider validating it starts with https:// before accepting it, to guard against non-HTTP schemes reaching the client if the gateway format changes or an intermediary is compromised.

Suggested change
consent_errors.append(ConsentError(name=error.get("name", "Unknown"), consent_url=consent_url)) # type: ignore
if isinstance(consent_url, str) and consent_url.startswith("https://"):
consent_errors.append(ConsentError(name=error.get("name", "Unknown"), consent_url=consent_url)) # type: ignore

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code is not part of this PR — it came in from main during rebase. The comment should be addressed in a separate PR by the team that owns _responses.py.

async def main() -> None:
project_endpoint = os.environ["FOUNDRY_PROJECT_ENDPOINT"]
deployment = os.environ.get("AZURE_AI_MODEL_DEPLOYMENT_NAME", "gpt-5")
toolbox_name = os.environ["FOUNDRY_TOOLBOX_NAME"]
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This hard-codes toolbox resolution to {FOUNDRY_PROJECT_ENDPOINT}/toolboxes/{FOUNDRY_TOOLBOX_NAME}/mcp?api-version=v1. The existing toolbox samples (04_foundry_toolbox, 06_files) prefer an explicit FOUNDRY_TOOLBOX_ENDPOINT env var and only fall back to project/name derivation, and the README documents that direct-endpoint path as the recommended local-dev flow. Consider adopting the same pattern so users with an existing MCP endpoint can point the sample at it directly.

@github-project-automation github-project-automation Bot moved this from In Review to Done in Agent Framework Jun 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation python

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

5 participants