main
9 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
25ec4c3b5c |
Python: Support archive-type MCP skills (source, toolbox, sample) (#7121)
* Python: Support archive-type MCP skills in MCPSkillsSource Add `archive`-type skill support to `MCPSkillsSource` so an MCP server can advertise packaged skills (ZIP / TAR / gzip-compressed TAR) that are downloaded, safely unpacked to a local directory, and served like file-based skills, while keeping the guarantee that MCP-delivered scripts are never executed. - Dispatch `skill://index.json` entries by `type`: `skill-md` (existing, fetched on demand) and `archive` (new). Unknown types are skipped. - `_ArchiveEntryLoader` downloads, extracts, and prunes archive skills and delegates discovery to an internal `FileSkillsSource` created with no script extensions and no runner, so bundled scripts surface as read-only resources only. - Hardened stdlib extraction: path-traversal (zip-slip) guard, non-regular TAR member skipping, and file-count / uncompressed-size / download-size limits. - Configure via `archive_*` constructor kwargs (no options object, per Python conventions); use `CachingSkillsSource` for refresh rather than a source level refresh interval. - Fix `FileSkillsSource` to treat `None` extensions as "use defaults" and an empty tuple as "discover none" (an empty tuple previously fell back to defaults). Port of .NET PR #6631. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 9e358a4e-538f-46be-8c58-128b6182352d * Propagate non-not-found archive download errors in MCPSkillsSource Only swallow "resource not found" MCP errors when downloading an archive resource; re-raise every other error (auth failure, INTERNAL_ERROR, connection drop, timeout) so a transient transport failure is not silently turned into a missing skill. This matches the existing failure model used by `_try_read_index` and `MCPSkill.get_resource`, and avoids a failed `CachingSkillsSource` refresh overwriting a previously cached list with a partial result. Add tests asserting archive-download INTERNAL_ERROR and ConnectionError propagate out of `get_skills`. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 9e358a4e-538f-46be-8c58-128b6182352d * Python: Expose archive skill options on FoundryToolbox and demo in sample - FoundryToolbox.as_skills_provider() now forwards the MCPSkillsSource archive options (archive_skills_directory, archive_resource_extensions, archive_resource_search_depth, archive_max_file_count, archive_max_size_bytes, archive_max_uncompressed_size_bytes). Only explicitly-set options are forwarded so unset ones keep the MCPSkillsSource defaults. This lets a hosted toolbox agent redirect archive extraction to a writable directory (the default is under the cwd, which may be read-only in a container). - Add unit tests covering default (no options forwarded) and override forwarding. - Update the 12_foundry_toolbox_mcp_skills sample to demonstrate all three progressive-disclosure stages with an archive skill: escalation-policy now ships a references/refund-matrix.md resource and is uploaded as a ZIP archive; main.py disables load_skill and read_skill_resource approval and points archive extraction at a temp directory. README, toolbox.yaml, and ignore files updated accordingly. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 4f14f83d-1868-45c1-be1a-12f49a58ac36 * Python: Fix ty type error in toolbox archive-option test Cast provider._source to _FoundryToolboxSkillsSource before accessing the private _archive_options, so the ty checker (which runs over tests) resolves the concrete type instead of the SkillsSource base. Replaces the mypy-style type: ignore that ty did not honor. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 4f14f83d-1868-45c1-be1a-12f49a58ac36 * Rework archive-type skill support in MCPSkillsSource to unpack archives entirely in memory instead of extracting them to a local directory, and apply reviewer feedback. * Python: Raise on archive member path-traversal (zip-slip) Treat a `..` path-traversal member in an archive skill as a hostile archive and reject the whole skill, matching how the file-count and uncompressed-size limits reject a malformed archive (previously the member was silently skipped while the rest of the skill still loaded). - `_normalize_archive_member_name` now raises `ValueError` on a `..` escape; benign degenerate entries (empty, `.`, `/`) still return None (skipped) and absolute paths are still neutralized to relative. The raise propagates to `_ArchiveEntryLoader._build_skill`, which already skips the skill on error. - Update tests: traversal cases now assert a raise, and add an end-to-end test that a zip-slip archive drops the whole skill. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 9e358a4e-538f-46be-8c58-128b6182352d * Python: Revert archive skill demo in toolbox MCP skills sample Restore the 12_foundry_toolbox_mcp_skills sample to its pre-PR, skill-md-only form (matching the .NET Agent_Step26_FoundryToolboxMcpSkills sample, which uses skill-md and no ZIP archive): - Revert main.py, toolbox.yaml, README.md, .azdignore, .dockerignore, and escalation-policy/SKILL.md to the single-file SKILL.md version. - Remove the archive demo files added by this PR (.gitignore and escalation-policy/references/refund-matrix.md). - Soften two README notes so they no longer claim archive skills are unsupported/silently dropped (this PR adds archive support); instead frame single-file SKILL.md as a focus choice and point to the archive_* options. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 9e358a4e-538f-46be-8c58-128b6182352d * Python: Clarify archive framing in mcp_based_skill sample README The mcp_based_skill sample is a generic MCP consumer that discovers whatever the server advertises; it does not itself demonstrate archive skills. Reword the archive note so it reads as an MCPSkillsSource capability rather than a sample feature, and fix the stale "unpacked to a local directory" claim to "unpacked in memory" (matching the in-memory extraction implementation). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 9e358a4e-538f-46be-8c58-128b6182352d --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 9e358a4e-538f-46be-8c58-128b6182352d Copilot-Session: 4f14f83d-1868-45c1-be1a-12f49a58ac36 |
||
|
|
b64a2e2f82 |
Python: add feature-usage User-Agent telemetry (#7420)
* Python: add first-pass feature usage telemetry Add the 128-bit feature accumulator, package-local indexes, activation markers, and destination-scoped User-Agent emission for the initial Python implementation slice. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 346bf168-b668-4c4a-a8db-a67282ee5e5f * Python: track declarative feature usage Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 346bf168-b668-4c4a-a8db-a67282ee5e5f * Python: complete feature usage telemetry Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 346bf168-b668-4c4a-a8db-a67282ee5e5f * Python: report core version in User-Agent Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 346bf168-b668-4c4a-a8db-a67282ee5e5f * Python: configure Lab telemetry import path Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 346bf168-b668-4c4a-a8db-a67282ee5e5f * Python: preserve telemetry transport behavior Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 346bf168-b668-4c4a-a8db-a67282ee5e5f * Python: preserve caller-owned Foundry transports Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 346bf168-b668-4c4a-a8db-a67282ee5e5f * Python: remove stale Anthropic test import Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 346bf168-b668-4c4a-a8db-a67282ee5e5f --------- Copilot-Session: 346bf168-b668-4c4a-a8db-a67282ee5e5f |
||
|
|
217912a2c0 |
Python: Support async credentials in FoundryToolbox (#7208)
* Python: Support async credentials in `FoundryToolbox` * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Refactor: Use AzureCredentialTypes for credential type annotations in Toolbox classes * Remove auth_flow method from _ToolboxAuth class --------- Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> |
||
|
|
0841116330 |
Python: fix(foundry_hosting): preserve auth credentials across FoundryToolbox reconnections (#7202)
* fix(foundry_hosting): preserve auth credentials across FoundryToolbox reconnections * Address copilot comments * fix syntax check * Fix tests * Fix formatting * Fix formatting --------- Co-authored-by: Tao Chen <taochen@microsoft.com> |
||
|
|
1466d68cf1 |
Python: make FoundryToolbox.as_skills_provider() disable_caching effective (#7135)
* Python: make FoundryToolbox.as_skills_provider() disable_caching effective as_skills_provider() forwarded disable_caching to SkillsProvider, which ignores it for a caller-supplied SkillsSource, so it was a no-op and the toolbox re-read skill://index.json on every agent run. Compose caching in as_skills_provider() instead: wrap the context-independent _FoundryToolboxSkillsSource in DeduplicatingSkillsSource(CachingSkillsSource(...)). Add a cache_refresh_interval param, fix the docstring, and add tests covering cached, disabled, and refresh-interval behavior. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 84150ec4-6f7c-4ef8-b9fb-12fa11652773 * Clarify caller-invariant skill-set wording in as_skills_provider docs Emphasize that the toolbox advertises the same skill set to every caller (the per-request call-id governs execution/authorization, not which skills are listed) rather than leaning on 'ignores SkillsSourceContext'. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 84150ec4-6f7c-4ef8-b9fb-12fa11652773 * Make MCP skills reconnect-safe via session_provider Cached MCPSkill objects captured the MCP ClientSession at construction, so after a FoundryToolbox reconnect (which replaces its session) load_skill and read_skill_resource would fail against the closed session. This regressed once as_skills_provider() started caching discovery by default. Add an optional session_provider callable to MCPSkillsSource and MCPSkill (exactly one of client or session_provider). When supplied, the session is resolved on every fetch, mirroring how MCPTool resolves self.session live at call time. _FoundryToolboxSkillsSource now passes a provider that returns the toolbox's current session, so cached skills always use the live session. The fixed client= path is unchanged and backward-compatible. Update core tests, foundry_hosting tests, and core AGENTS.md. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 84150ec4-6f7c-4ef8-b9fb-12fa11652773 * Fix ty error: type captured session_provider as Callable in test ty could not call the provider narrowed from \object\ (Top callable). Type the captured value as Callable[[], object] and drop the redundant callable() assert. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 84150ec4-6f7c-4ef8-b9fb-12fa11652773 * Simplify _resolve_mcp_session_provider per review Address review feedback: replace the dense (client is None) == (session_provider is None) guard with explicit branches, and drop the cast by binding the narrowed client to a typed local. Keeps strict 'exactly one' semantics (raises on both and on neither), matching the codebase convention (e.g. security.py mcp_tool/url). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 84150ec4-6f7c-4ef8-b9fb-12fa11652773 * Add PR #7135 entries to the 1.12.0 changelog Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 84150ec4-6f7c-4ef8-b9fb-12fa11652773 * Drop redundant @pytest.mark.asyncio from MCP skills tests asyncio_mode is 'auto', so the marker is unnecessary. Remove it from the whole file for consistency with the async-by-default convention. Per review feedback. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 84150ec4-6f7c-4ef8-b9fb-12fa11652773 --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> |
||
|
|
56e9a8f74c |
Python: Make foundry toolbox MCP skills sample self-contained (#7099)
* Python: Make foundry toolbox MCP skills sample self-contained 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 * Python: Address PR review on toolbox MCP skills sample - Remove the unused parameters section from agent.manifest.yaml (TOOLBOX_ENDPOINT is supplied via environment_variables, matching sample 04). - README: state the sample is self-contained directly instead of contrasting with the C# sample. - README: describe skill discovery behaviour without naming the internal MCPSkillsSource class. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 4f14f83d-1868-45c1-be1a-12f49a58ac36 --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> |
||
|
|
12b029858e |
Build(deps): consolidate Dependabot dependency updates (#6984)
* Consolidate Dependabot dependency updates * Restore method assignment suppression |
||
|
|
9c4cd07899 |
Python: Add SkillsSourceContext to SkillsSource.get_skills (#6895)
* Python: Add SkillsSourceContext to SkillsSource.get_skills Thread an invocation context (agent + optional session) through the skill source pipeline so sources and decorators can make context-aware decisions. - Add frozen, experimental SkillsSourceContext(agent, session). - Change SkillsSource.get_skills and all sources/decorators to accept and forward the context. - Make FilteringSkillsSource predicate context-aware: (skill, context) -> bool. - Add optional cache_isolation_key_selector to CachingSkillsSource for per-key cache isolation (None keeps the shared-bucket behavior). - Build the context in SkillsProvider from before_run agent/session. - Update foundry_hosting toolbox source, exports, tests, and docs. Python port of .NET PR #6797. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Python: Clarify skills source docstring examples Address PR review: docstring examples referenced `context` without constructing it. Add a `SkillsSourceContext` construction line (with a placeholder agent) to each source example and a note that the provider normally supplies it. Use `source_context` in the FilteringSkillsSource example to avoid clashing with the predicate's `context` parameter. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Python: Fix CI type errors and skill_filtering sample predicate Address CI failures from the SkillsSourceContext change: - Update the skill_filtering sample to the 2-arg predicate signature (skill, context); the old 1-arg lambda would fail at runtime. - Replace ad-hoc _StubAgent test stubs with the shared MockAgent / MockAgentSession from conftest so all type checkers (incl. ty) accept the SupportsAgentRun-typed agent. Add a small _NamedMockAgent subclass for tests needing distinct agent names, and drop now-unnecessary attr-defined ignores. - Use cast(SupportsAgentRun, ...) in foundry_hosting tests, which have no shared mock infrastructure. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Python: Make SkillsProvider caching safe-by-default; clarify context docstrings Address PR review comments: - Do not auto-wrap a caller-supplied SkillsSource in the provider's default CachingSkillsSource. A shared, unkeyed cache around a context-aware source replays the first invocation's skills for later SkillsSourceContexts, leaking skills across agents/tenants. Default caching now applies only to the built-in, context-independent file/in-memory leaf sources (Deduplicating(Caching(leaf))), matching the .NET provider. Callers who want caching on a custom pipeline compose CachingSkillsSource (optionally with a cache_isolation_key_selector) themselves. disable_caching now only affects the built-in leaves. Adds a leak-prevention test. - Reword the misleading "Unused by this source" context docstrings on the File/InMemory/MCP sources: the param is part of the get_skills contract; these sources just return the same skills regardless of context. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> |
||
|
|
7f3a2aec38 |
[BREAKING] Python: Foundry Hosted Agent V2 protocol upgrade (#6811)
* Upgrade to FHA protocol v2 + toolbox integration
* Scope checkpoints and approval storage by user id
* Add toolbox skills integration
* Fix formatting
* Add httpx lower and upper bound
* Update foundry-hosting package version
* Remove custom http client
* Revert "Remove custom http client"
This reverts commit
|