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.
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/amd64builds, oraz acr buildto build server-side. The hosting platform is x86_64 only. - Python 3.12 locally to run
deploy.pyandprobe.py, withazure-ai-projects>=2.6.0,azure-identityandpython-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
| Who | Role | Scope | For |
|---|---|---|---|
| You, deploying | Foundry Project Manager | Project | Create the agent and its versions |
| You, deploying | ACR push rights (or use az acr build) | Registry | Push the image |
| The account and project system managed identities | Classic AcrPull | Registry | Pull the image |
| The agent's instance identity | Foundry User | Foundry account | Call the model |
| The agent's instance identity | App role or caller allowlist | Each outbound server | The 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.
| Round | Decide | Lands in |
|---|---|---|
| 1. The task | The 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 spend | Orchestrator 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 shows | Which 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 outputs | Scheduled, 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-loop | Does 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/
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 anythingSKILL.mdlinks to. An allowlist that dropsreferences/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'sstate/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
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.
Related
Last updated 2026-10-21