Browse the docs

Prerequisites and project setup

What to install, which Azure resources and roles you need, the decisions to settle before writing code, and how to vendor the skill safely.

Before you startAn Azure subscription with an AI Foundry project and Claude model deployments. Python 3.12 and the Azure CLI.View as Markdown

What you need

  • A Claude skill folder with a SKILL.md, plus whatever it references: scripts/, references/, config/, assets.
  • A Microsoft Foundry project and the Foundry resource (account) it belongs to, with Claude deployments for all three tiers (Opus, Sonnet, Haiku).
  • An Azure Container Registry the project can pull from.
  • The Azure CLI, signed in with az login. The agent itself uses no API keys: you deploy as yourself and the sandbox authenticates as its own identity.
  • Docker with linux/amd64 builds, or az acr build to build server-side. The hosting platform is x86_64 only.
  • Python 3.12 locally to run deploy.py and probe.py, with azure-ai-projects>=2.6.0, azure-identity and python-dotenv.
  • The `foundry-hybrid-agent-creator` skill in your Claude Code project's .claude/skills/, if you want Claude to scaffold the agent for you.

Roles

WhoRoleScopeFor
You, deployingFoundry Project ManagerProjectCreate the agent and its versions
You, deployingACR push rights (or use az acr build)RegistryPush the image
The account and project system managed identitiesClassic AcrPullRegistryPull the image
The agent's instance identityFoundry UserFoundry accountCall the model
The agent's instance identityApp role or caller allowlistEach outbound serverThe skill's integrations

The instance identity only exists after the first deploy. Deploy to Foundry covers when and how to grant it.

Settle these before any code

The creator skill runs a five-round interview. Every answer maps to a template value, so nothing is left to a guess. Decide these yourself if you build by hand.

RoundDecideLands in
1. The taskThe request fields, each with a default (a scheduler sends {}). What the skill produces and where it lands. What the skill must never do here. Whether the task data is confidential.Request parsing, DENY_RULES, EMIT_STEP_ACTIVITY_LOG, OUTPUT_GLOBS
2. Models and spendOrchestrator tier, whether sub-agents are allowed, the three deployment names, expected spend of one run.CLAUDE_AGENT_MODEL, disallowed_tools, ANTHROPIC_DEFAULT_*_MODEL, ceilings
3. What the run showsWhich method stages appear as steps, which produced items appear one by one, which consumer watches.PHASE_TITLES, PHASE_RULES, ARTIFACT_WATCHES, STEP_TOKENS
4. Invocation and outputsScheduled, watched live or on demand. Expected wall clock. Who publishes the output. Outbound integrations. Hosts the skill's scripts must reach. Timezone.RUN_TIMEOUT_SECONDS, session idle timeout, _postprocess, MCP servers, BASH_ALLOWED_HOSTS
5. Human-in-the-loopDoes a run ever need a person mid-run? Is the pause mandatory or conditional?HITL, HITL_QUESTIONS

The port is always 8088. There are no API keys to collect.

Project layout

For an invoice-summarizer skill, the scaffolded agent directory looks like this. The .tpl suffix is dropped when the templates are filled in.

invoice-summarizer-agent/
invoice-summarizer-agent/
  main.py             entrypoint: ResponsesHostServer(create_agent()).run()
  agent.py            clientless BaseAgent: the shell
  runner.py           one skill turn: prompt, options, policy hook, query() loop, gates, publish
  steps.py            phase rules, artifact watches, per-phase tokens, host steps
  probe.py            preflight / stream / background / failed probes
  deploy.py           immutable version from a container image, routes 100% to it
  requirements.txt    exact pins
  Dockerfile          python:3.12-slim + tzdata + ca-certificates, skill baked in, non-root
  .env.example        endpoint, image, pins, tier, ceilings, timeouts, reporting switches
  .dockerignore
  README.md           request contract, what a run reports, build / deploy / invoke / logs
external/
  invoice-summarizer/ the SANITIZED copy of the skill (see below)

Vendor the skill, never copy it raw

The skill is baked into the image unchanged at /app/.claude/skills/<skill-name>/. Copy it with a script, not a raw COPY of the author's folder.

  • Copy everything the method references: SKILL.md, config/, scripts/, references/, assets, and anything SKILL.md links to. An allowlist that drops references/ ships a skill that passes the existence check and runs without its method.
  • Exclude credential and state paths by name, wherever they sit: state/, .env*, credentials/, secrets/, *.token, *.pem, *.key, plus the skill's own local state or credential folders. A live token has been found in a skill's state/ folder.
  • Fail loudly when an excluded path is present in the copy. The Dockerfile checks again, recursively, at build time.
  • Never patch the source skill. Host-imposed changes go in the prompt as numbered items.

Check the templates offline

Parse every Python file
python -c "import ast; [ast.parse(open(f, encoding='utf-8').read()) for f in ['main.py','agent.py','runner.py','steps.py','deploy.py','probe.py']]"

Then unit-test steps.py with a fake message sequence. Every declared phase appears, item steps appear when the data file changes, and for every model the sum of step tokens equals the attributed tokens and stays within the totals. Include the idle-gap pair described in Run steps, token markers and cost.

Last updated 2026-10-21