* 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
Python Samples
This directory contains samples demonstrating the capabilities of Microsoft Agent Framework for Python.
Structure
| Folder | Description |
|---|---|
01-get-started/ |
Progressive tutorial: hello agent → hosting |
02-agents/ |
Deep-dive by concept: tools, middleware, providers, orchestrations |
03-workflows/ |
Workflow patterns: sequential, concurrent, state, declarative, explicit output designation |
04-hosting/ |
Deployment: Azure Functions, Durable Tasks, A2A |
05-end-to-end/ |
Full applications, evaluation, demos |
Getting Started
Start with 01-get-started/ and work through the numbered files:
- 01_hello_agent.py — Create and run your first agent
- 02_add_tools.py — Add function tools with
@tool - 03_multi_turn.py — Multi-turn conversations with
AgentSession - 04_memory.py — Agent memory with
ContextProvider - 05_functional_workflow_with_agents.py — Call agents inside a functional workflow
- 06_functional_workflow_basics.py — Write a workflow as a plain async function
- 07_first_graph_workflow.py — Build a workflow with executors and edges
- 08_host_your_agent.py — Host your agent via Azure Functions
Prerequisites
pip install agent-framework-foundry
Install only the specific Agent Framework distributions required by the sample you want to run. Individual sample READMEs and PEP 723 metadata list any additional packages.
Environment Variables
Samples call load_dotenv() to automatically load environment variables from a .env file in the python/ directory. This is a convenience for local development and testing.
For local development, set up your environment using any of these methods:
Option 1: Using a .env file (recommended for local development):
- Copy
.env.exampleto.envin thepython/directory:cp .env.example .env - Edit
.envand set your values (API keys, endpoints, etc.)
Option 2: Export environment variables directly:
export FOUNDRY_PROJECT_ENDPOINT="your-foundry-project-endpoint"
export FOUNDRY_MODEL="gpt-4o"
Option 3: Using env_file_path parameter (for per-client configuration):
All client classes (e.g., OpenAIChatClient, OpenAIChatCompletionClient) support an env_file_path parameter to load environment variables from a specific file:
from agent_framework.openai import OpenAIChatClient
# Load from a custom .env file
client = OpenAIChatClient(env_file_path="path/to/custom.env")
This allows different clients to use different configuration files if needed.
For the generic OpenAI clients (OpenAIChatClient and OpenAIChatCompletionClient), routing
precedence is:
- Explicit Azure inputs such as
credential,azure_endpoint, orapi_version OPENAI_API_KEY/ explicit OpenAI API-key parameters- Azure environment fallback such as
AZURE_OPENAI_ENDPOINTandAZURE_OPENAI_API_KEY
If you keep both OpenAI and Azure variables in your shell, the generic clients stay on OpenAI until you pass an explicit Azure input.
For the getting-started samples, you'll need at minimum:
FOUNDRY_PROJECT_ENDPOINT="your-foundry-project-endpoint"
FOUNDRY_MODEL="gpt-4o"
Consolidated sample env inventory
This is the single source of truth for package-level environment variables read by packages included by
agent-framework-core[all]. It intentionally excludes variables that are only read by standalone samples,
package sample folders, or tests. When package code adds, removes, or renames an environment variable,
update this table in the same change.
Example values below are illustrative. For entries not backed by a single public class, the class
column names the closest public surface, helper, or package-level initialization point that reads the
variable.
| package | class/module | env var | example value |
|---|---|---|---|
agent-framework-anthropic |
AnthropicClient |
ANTHROPIC_API_KEY |
sk-ant-api03-... |
agent-framework-anthropic |
AnthropicClient |
ANTHROPIC_CHAT_MODEL |
claude-sonnet-4-5-20250929 |
agent-framework-foundry |
FoundryEmbeddingClient |
FOUNDRY_MODELS_ENDPOINT |
https://my-endpoint.inference.ai.azure.com |
agent-framework-foundry |
FoundryEmbeddingClient |
FOUNDRY_MODELS_API_KEY |
env-key |
agent-framework-foundry |
FoundryEmbeddingClient |
FOUNDRY_EMBEDDING_MODEL |
text-embedding-3-small |
agent-framework-foundry |
FoundryEmbeddingClient |
FOUNDRY_IMAGE_EMBEDDING_MODEL |
Cohere-embed-v3-english |
agent-framework-azure-ai-search |
AzureAISearchContextProvider |
AZURE_SEARCH_ENDPOINT |
https://my-search.search.windows.net |
agent-framework-azure-ai-search |
AzureAISearchContextProvider |
AZURE_SEARCH_API_KEY |
search-key |
agent-framework-azure-ai-search |
AzureAISearchContextProvider |
AZURE_SEARCH_INDEX_NAME |
hotels-index |
agent-framework-azure-ai-search |
AzureAISearchContextProvider |
AZURE_SEARCH_KNOWLEDGE_BASE_NAME |
hotels-kb |
agent-framework-azure-cosmos |
CosmosHistoryProvider |
AZURE_COSMOS_ENDPOINT |
https://my-cosmos.documents.azure.com:443/ |
agent-framework-azure-cosmos |
CosmosHistoryProvider |
AZURE_COSMOS_DATABASE_NAME |
agent-history |
agent-framework-azure-cosmos |
CosmosHistoryProvider |
AZURE_COSMOS_CONTAINER_NAME |
messages |
agent-framework-azure-cosmos |
CosmosHistoryProvider |
AZURE_COSMOS_KEY |
C2F...== |
agent-framework-bedrock |
BedrockChatClient |
BEDROCK_REGION |
us-east-1 |
agent-framework-bedrock |
BedrockChatClient |
BEDROCK_CHAT_MODEL |
anthropic.claude-3-5-sonnet-20241022-v2:0 |
agent-framework-bedrock |
BedrockEmbeddingClient |
BEDROCK_REGION |
us-east-1 |
agent-framework-bedrock |
BedrockEmbeddingClient |
BEDROCK_EMBEDDING_MODEL |
amazon.titan-embed-text-v2:0 |
agent-framework-bedrock |
BedrockChatClient / BedrockEmbeddingClient |
AWS_ACCESS_KEY_ID |
AKIAIOSFODNN7EXAMPLE |
agent-framework-bedrock |
BedrockChatClient / BedrockEmbeddingClient |
AWS_SECRET_ACCESS_KEY |
wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY |
agent-framework-bedrock |
BedrockChatClient / BedrockEmbeddingClient |
AWS_SESSION_TOKEN |
IQoJb3JpZ2luX2VjEO7//////////wEaCXVzLXdlc3QtMiJHMEUCIQD... |
agent-framework-copilotstudio |
CopilotStudioAgent |
COPILOTSTUDIOAGENT__ENVIRONMENTID |
00000000-0000-0000-0000-000000000000 |
agent-framework-copilotstudio |
CopilotStudioAgent |
COPILOTSTUDIOAGENT__SCHEMANAME |
cr123_agentname |
agent-framework-copilotstudio |
CopilotStudioAgent |
COPILOTSTUDIOAGENT__TENANTID |
11111111-1111-1111-1111-111111111111 |
agent-framework-copilotstudio |
CopilotStudioAgent |
COPILOTSTUDIOAGENT__AGENTAPPID |
22222222-2222-2222-2222-222222222222 |
agent-framework-core |
observability |
ENABLE_INSTRUMENTATION |
true |
agent-framework-core |
observability |
ENABLE_SENSITIVE_DATA |
false |
agent-framework-core |
observability |
ENABLE_CONSOLE_EXPORTERS |
true |
agent-framework-core |
agent_framework._telemetry |
AGENT_FRAMEWORK_FEATURE_MASK_DISABLED |
true |
agent-framework-core |
agent_framework._telemetry |
AGENT_FRAMEWORK_USER_AGENT_DISABLED |
true |
agent-framework-core |
observability |
OTEL_EXPORTER_OTLP_ENDPOINT |
http://localhost:4317 |
agent-framework-core |
observability |
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT |
http://localhost:4318/v1/traces |
agent-framework-core |
observability |
OTEL_EXPORTER_OTLP_METRICS_ENDPOINT |
http://localhost:4318/v1/metrics |
agent-framework-core |
observability |
OTEL_EXPORTER_OTLP_LOGS_ENDPOINT |
http://localhost:4318/v1/logs |
agent-framework-core |
observability |
OTEL_EXPORTER_OTLP_PROTOCOL |
grpc |
agent-framework-core |
observability |
OTEL_EXPORTER_OTLP_HEADERS |
api-key=demo |
agent-framework-core |
observability |
OTEL_EXPORTER_OTLP_TRACES_HEADERS |
api-key=trace-demo |
agent-framework-core |
observability |
OTEL_EXPORTER_OTLP_METRICS_HEADERS |
api-key=metric-demo |
agent-framework-core |
observability |
OTEL_EXPORTER_OTLP_LOGS_HEADERS |
api-key=log-demo |
agent-framework-core |
observability |
OTEL_SERVICE_NAME |
sample-agent |
agent-framework-core |
observability |
OTEL_SERVICE_VERSION |
1.0.0 |
agent-framework-core |
observability |
OTEL_RESOURCE_ATTRIBUTES |
deployment.environment=dev,service.namespace=agent-framework |
agent-framework-devui |
DevUI server |
DEVUI_AUTH_TOKEN |
my-devui-token |
agent-framework-foundry |
FoundryChatClient |
FOUNDRY_PROJECT_ENDPOINT |
https://my-project.services.ai.azure.com/api/projects/my-project |
agent-framework-foundry |
FoundryChatClient |
FOUNDRY_MODEL |
gpt-4o |
agent-framework-foundry |
FoundryAgent |
FOUNDRY_AGENT_NAME |
travel-planner |
agent-framework-foundry |
FoundryAgent |
FOUNDRY_AGENT_VERSION |
v1 |
agent-framework-github-copilot |
GitHubCopilotAgent |
GITHUB_COPILOT_CLI_PATH |
copilot |
agent-framework-github-copilot |
GitHubCopilotAgent |
GITHUB_COPILOT_MODEL |
gpt-5 |
agent-framework-github-copilot |
GitHubCopilotAgent |
GITHUB_COPILOT_TIMEOUT |
60 |
agent-framework-github-copilot |
GitHubCopilotAgent |
GITHUB_COPILOT_LOG_LEVEL |
info |
agent-framework-mem0 |
agent_framework_mem0 package import |
MEM0_TELEMETRY |
false |
agent-framework-ollama |
OllamaChatClient |
OLLAMA_HOST |
http://localhost:11434 |
agent-framework-ollama |
OllamaChatClient |
OLLAMA_MODEL |
llama3.1:8b |
agent-framework-openai |
OpenAIChatClient / OpenAIChatCompletionClient / OpenAIEmbeddingClient |
OPENAI_API_KEY |
sk-proj-... |
agent-framework-openai |
OpenAIChatClient / OpenAIChatCompletionClient / OpenAIEmbeddingClient |
OPENAI_MODEL |
gpt-4o-mini |
agent-framework-openai |
OpenAIChatClient |
OPENAI_CHAT_MODEL |
gpt-4.1-mini |
agent-framework-openai |
OpenAIChatCompletionClient |
OPENAI_CHAT_COMPLETION_MODEL |
gpt-4o |
agent-framework-openai |
OpenAIEmbeddingClient |
OPENAI_EMBEDDING_MODEL |
text-embedding-3-small |
agent-framework-openai |
OpenAIChatClient / OpenAIChatCompletionClient / OpenAIEmbeddingClient |
OPENAI_BASE_URL |
https://api.openai.com/v1/ |
agent-framework-openai |
OpenAIChatClient / OpenAIChatCompletionClient / OpenAIEmbeddingClient |
OPENAI_ORG_ID |
org_123456789 |
agent-framework-openai |
OpenAIChatClient / OpenAIChatCompletionClient / OpenAIEmbeddingClient |
AZURE_OPENAI_ENDPOINT |
https://my-resource.openai.azure.com/ |
agent-framework-openai |
OpenAIChatClient / OpenAIChatCompletionClient / OpenAIEmbeddingClient |
AZURE_OPENAI_API_KEY |
sk-azure-... |
agent-framework-openai |
OpenAIChatClient / OpenAIChatCompletionClient / OpenAIEmbeddingClient |
AZURE_OPENAI_API_VERSION |
2024-10-21 |
agent-framework-openai |
OpenAIChatClient / OpenAIChatCompletionClient / OpenAIEmbeddingClient |
AZURE_OPENAI_BASE_URL |
https://my-resource.openai.azure.com/openai/v1/ |
agent-framework-openai |
OpenAIChatClient / OpenAIChatCompletionClient / OpenAIEmbeddingClient |
AZURE_OPENAI_MODEL |
gpt-4o |
agent-framework-openai |
OpenAIChatClient |
AZURE_OPENAI_CHAT_MODEL |
gpt-4.1 |
agent-framework-openai |
OpenAIChatCompletionClient |
AZURE_OPENAI_CHAT_COMPLETION_MODEL |
gpt-4o-mini |
agent-framework-openai |
OpenAIEmbeddingClient |
AZURE_OPENAI_EMBEDDING_MODEL |
text-embedding-3-large |
agent-framework-openai |
OpenAIChatClient / OpenAIChatCompletionClient / OpenAIEmbeddingClient |
AZURE_OPENAI_RESOURCE_URL |
https://cognitiveservices.azure.com/ |
agent-framework-openai supports the Azure OpenAI client-specific deployment aliases listed above; keep
packages/openai/README.md as the authoritative reference for the exact fallback order and package-specific
behavior.
Note for production: In production environments, set environment variables through your deployment platform (e.g., Azure App Settings, Kubernetes ConfigMaps/Secrets) rather than using .env files. The load_dotenv() call in samples will have no effect when a .env file is not present, allowing environment variables to be loaded from the system.
For Azure authentication, run az login before running samples.
Note on XML tags
Some sample files include XML-style snippet tags (for example <snippet_name> and </snippet_name>). These are used by our documentation tooling and can be ignored or removed when you use the samples outside this repository.
Additional Resources
- Agent Framework Documentation
- AGENTS.md — Structure documentation for maintainers
- SAMPLE_GUIDELINES.md — Coding conventions for samples