Browse the docs

Project layout and placeholders

Every file the creator generates, what each one does, and how each placeholder is filled.

Before you startRead the [overview](/developers/self-hosted-overview/) first.View as Markdown

The creator writes a new directory, by default azurefoundry_<short>_skills/, next to the source skill in your repository. The skill folder stays where it is; the Dockerfile copies it into the image at build time. That is why every build runs from the repository root.

Generated layout (plain-HTTP surface)
<repo-root>/
├── skills/report-generator/              # the source skill (unchanged)
│   ├── SKILL.md
│   └── scripts/
└── azurefoundry_report_generator_skills/
    ├── __main__.py                       # entry point: health probe, run_turn, uvicorn
    ├── claude_skills_agent.py            # the SDK core: options, prompt, events, tokens
    ├── claude_skills_agent_executor.py   # shared agent instance + per-context session map
    ├── http_agent_server.py              # transport: /health, /execute, /tasks/{id}/cancel
    ├── Dockerfile
    ├── .dockerignore
    ├── .env.example
    ├── pyproject.toml                    # what the image installs from
    ├── requirements.txt                  # kept in step with pyproject.toml
    ├── README.md
    └── tests/                            # local diagnostics, never shipped in the image
        ├── README.md
        ├── test_run.py
        ├── test_foundry_direct.py
        ├── test_anthropic_sdk_foundry.py
        ├── inspect_foundry_client.py
        └── debug_sdk.py

For the AMC variant, keep claude_skills_agent.py and the build files, skip http_agent_server.py, claude_skills_agent_executor.py, and the HTTP parts of __main__.py, and add app.py. See Serve the agent to AMC with amc_sdk.

What each file does

FileKindJob
claude_skills_agent.pyTemplatedBuilds ClaudeAgentOptions, wraps the user message in the prompt strategy, runs query(), translates SDK messages into wire events, and reads tokens and cost off ResultMessage.
claude_skills_agent_executor.pyTemplatedOwns one shared agent instance behind a lock, initializes it at startup, and maps each context_id to a session id.
__main__.pyTemplatedLoads .env, defines the health probe, maps agent strings to NDJSON events in _run_turn, and starts uvicorn.
http_agent_server.pyVerbatimThe plain-HTTP transport: request validation, NDJSON streaming, concurrency cap, and cancellation.
DockerfileTemplatedpython:3.12-slim image with the skill baked in at /app/.claude/skills/<skill-name>/, running as a non-root user.
.dockerignoreVerbatimKeeps .env, tests/, .git/, virtualenvs, and caches out of the image.
pyproject.tomlTemplatedThe dependency list the image installs (uv pip install --system -e .).
requirements.txtTemplatedThe same dependencies for local installs. The image never reads it.
.env.exampleTemplatedEvery environment variable the agent reads, with defaults. Copy to .env.
README.mdTemplatedBuild, local smoke test, and troubleshooting for this agent.
tests/test_run.pyTemplatedPhased local smoke test: env, SDK auth, skill discovery, adapter wiring.
tests/*.py (other four), tests/README.mdVerbatimCredential and SDK diagnostics. See Test the agent locally.

Simple placeholders

These are plain search-and-replace across every .tpl file. The examples are for a skill whose frontmatter says name: report-generator.

PlaceholderWhat it isExample
{{AGENT_DIR}}New agent directoryazurefoundry_report_generator_skills
{{AGENT_CLASS}}Agent class nameReportGeneratorSkillsAgent
{{EXECUTOR_CLASS}}Executor class nameReportGeneratorSkillsExecutor
{{CREATE_EXECUTOR_FN}}Executor factory functioncreate_report_generator_executor
{{INIT_FN}}Startup-init functioninitialize_report_generator_agents_at_startup
{{SKILL_NAME}}The skill's frontmatter name:, exactlyreport-generator
{{SKILL_SOURCE_PATH}}Skill folder, relative to the repo rootskills/report-generator
{{AGENT_NAME_HUMAN}}Display nameReport Generator Agent
{{AGENT_DESCRIPTION}}One-line descriptionBuilds a weekly report with the report-generator skill.
{{PORT}}TCP port for the plain-HTTP server9024
{{PACKAGE_NAME}}pyproject.toml nameclaude-skills-report-generator-agent
{{PACKAGE_DESCRIPTION}}pyproject.toml descriptionReport generator agent powered by claude-agent-sdk
{{AGENT_DOCKER_TAG}}Image tag prefix and container app namereport-generator-skills-agent
{{SKILL_DISPLAY_NAME}}Skill entry name in the README's registry snippetReport Generator
{{SKILL_DESCRIPTION_SHORT}}Skill entry descriptionWeekly report rendered as HTML.
{{SKILL_ID_SNAKE}}Skill entry idreport_generator
{{SKILL_TAGS}}Comma-separated tagsreports, claude, skill

Naming rules

IdentifierRuleExample
Skill name:kebab-case, used in full wherever {{SKILL_NAME}} appearsreport-generator
{{AGENT_DIR}}azurefoundry_ + snake_case + _skillsazurefoundry_report_generator_skills
{{AGENT_CLASS}}PascalCase, no underscores, suffix SkillsAgentReportGeneratorSkillsAgent
{{PACKAGE_NAME}}claude-skills- + kebab-case + -agentclaude-skills-report-generator-agent
{{AGENT_DOCKER_TAG}}short kebab-case + -skills-agentreport-generator-skills-agent
Container app name<short>-skills-agent-uamireport-generator-skills-agent-uami

If the derived names get unwieldy, strip filler words from the agent-side identifiers only. {{SKILL_NAME}} always keeps the skill's full name, because the SDK matches it.

Block markers

Block markers are replaced with code rather than a single value. Each sits next to a comment in the template that explains the choice.

MarkerFileReplace with
{{OPTIONAL_OUTPUTS_DIR_CONST}}claude_skills_agent.pyOUTPUTS_DIR = os.getenv("REPORT_OUTPUTS_DIR", "/outputs") if the skill writes files, else remove
{{OPTIONAL_MAKEDIRS}}claude_skills_agent.py, in create_agent()os.makedirs(OUTPUTS_DIR, exist_ok=True), or remove
{{SKILL_ENV_VARS}}claude_skills_agent.py, in ClaudeAgentOptions(env=...)Env vars the skill's scripts read at runtime, or nothing
{{PROMPT_STRATEGY_BODY}}claude_skills_agent.py, _build_prompt()Pattern A or B from the prompt strategies
{{TOOL_SCRIPT_LABELS}}claude_skills_agent.py, _describe_tool()A map of scripts/*.py filenames to short progress labels
{{OPTIONAL_OUTPUTS_HEALTH_CHECK}}__main__.py, _health()An outputs-dir writability check, or remove
{{OPTIONAL_OUTPUTS_MISSING_CHECK}}__main__.py, _health()The matching missing.append(...) line, or remove
{{DOCKERFILE_SKILL_PREP_BLOCK}}DockerfileOne or more filesystem prep patterns, concatenated
{{OPTIONAL_OUTPUTS_ENV}}DockerfileENV REPORT_OUTPUTS_DIR=/outputs, or remove
{{SKILL_EXTRA_DEPS}}pyproject.toml and requirements.txtOnly the packages the skill's scripts import, one per line
{{AGENT_TURN_DESCRIPTION}}README.mdWhat one run does, end to end
{{PREREQUISITES_BLOCK}}README.mdVolume mounts, network access, external accounts
{{ADDITIONAL_VOLUMES_BLOCK}}README.md, in the docker run example-v "${PWD}/outputs:/outputs" and similar, or empty

A few more markers appear in the templates: {{NONROOT_EXTRA_DIRS}} (extra directories the non-root user must own, in the Dockerfile), {{OUTPUTS_ENV_VAR}} and {{OPTIONAL_OUTPUTS_ENV_VAR}} (the outputs variable's name, in the agent and .env.example), {{OPTIONAL_OUTPUTS_ENV_SETUP}} (pointing that variable at a temp dir in tests/test_run.py), and {{PROMPT_DOCSTRING}} (the _build_prompt() docstring).

Last updated 2026-10-21