A real coding agent inside a Foundry hosted container.
This repository demonstrates a bring-your-own Azure AI Foundry hosted agent built
with the OpenAI Agents SDK for Python. The container exposes Foundry's
responses:1.0.0 contract, runs a model-and-tools loop, and adds bounded
file, terminal, browser, memory, retrieval, safety, skill, and telemetry integrations.
Foundry owns the front door. This repository owns the agent loop.
A caller targets a versioned hosted-agent endpoint. Foundry routes the request to
ResponsesAgentServerHost in the custom container, which streams the
turn through the OpenAI Agents SDK and a keyless Azure OpenAI client.
server_responses.py
ResponsesAgentServerHost, port 8088, and
responses:1.0.0.
One turn, seven deliberate stages.
The orchestration lives in src/agent/core.py. Optional dependencies
degrade to a smaller tool surface; safety tripwires return a refusal and blocked
content is not persisted to memory.
Accept and scope the request
The host reads input text and prefers the platform caller key for per-user memory scope.
Search memory
When enabled, up to five relevant snippets are read and appended to the agent instructions.
Connect Toolbox and load skills
The immutable MCP endpoint is opened for the turn. The exact eight-skill set is read as resources and injected all-or-none.
Build the Agent
Instructions, optional context, the configured model, local tools, MCP servers, and enabled guardrails are assembled.
Run and stream
Runner.run_streamed(..., max_turns=60) drives model decisions and tool calls, yielding text deltas as they arrive.
Handle safety outcomes
SDK tripwires and model content-filter responses become explicit refusals instead of partial success.
Queue memory update and clean up
Successful exchanges start a non-blocking memory extraction operation; Toolbox connections close in finally.
A coding toolchain, not a chat-only demo.
The SDK derives tool schemas from Python functions. Configuration flags choose the active local surface, and an optional Toolbox adds centrally managed MCP tools without replacing local tools.
| Capability | Tools | Boundary | Availability |
|---|---|---|---|
| Deterministic demos | get_current_utc_time, add_numbers |
In-process Python | core |
| Grounded retrieval | web_search, knowledge_base_search |
Azure OpenAI Responses and Foundry IQ / AI Search | feature-gated |
| Workspace files | write_file, read_file, list_files, apply_patch |
Paths confined beneath AGENT_WORKSPACE |
enabled by default |
| Short execution | run_python |
Fresh subprocess, 10-second timeout | enabled by default |
| Real terminal | run_shell |
Fresh process group, timeout, audit, redaction, deny-lists | Linux container |
| Rendered verification | browser_open, browser_open_local |
Fresh Chromium context with strict network policy | Linux container |
Workspace confinement
Resolved file paths must remain under the configured workspace; patch paths reject absolute paths and traversal.
Fresh subprocesses
Shell calls get independent process groups, bounded timeouts, truncated output, and per-session file-backed state.
Verification, not general browsing
Remote targets must be public HTTPS; local artifacts use a tool-owned loopback server and a symlink-free directory.
Search before the turn. Extract after success.
Foundry memory is optional and store names are configuration, not architecture.
The public sample defaults to agent-memory-store, but deployers supply
their own existing resource and retention policy.
Search
Query the configured store for up to MEMORY_MAX_RESULTS relevant items and inject only returned snippets.
Use
Memory context is appended to instructions; it does not become a new tool or bypass safety controls.
Update
A successful user/assistant exchange starts server-side extraction without waiting for the long-running operation.
MEMORY_SCOPE, then caller token
{oid}_{tid}, then default-user. The last fallback is useful
for demos but should not be treated as multi-user isolation.
Defense in depth, with the optional layers labeled.
The old explainer implied every layer was always active. In this sample, custom
Content Safety guardrails are controlled by GUARDRAILS_ENABLED; the
model deployment's platform content filter remains a separate control.
Agent instructions and tool boundaries
coreRefusal guidance, workspace confinement, network restrictions, token redaction, command deny-lists, and bounded cloud identity reduce the reachable blast radius.
Agents SDK input and output guardrails
optionalAzure AI Content Safety Prompt Shields inspect input, and text moderation checks input and output. Positive detections fail closed; service errors fail open and are logged.
Azure OpenAI platform content filter
platformA model content_filter response is converted into a clean refusal. This is independent of the app-level Content Safety feature flag.
A VM-isolated service contract, with defense-in-depth runtime design.
Foundry documents a VM-isolated sandbox per session. The repository's probes also
observed shared host and parent-process identity while confirming isolated
filesystem, environment, and /proc views. The implementation therefore
keeps no mutable cross-session state in Python globals.
Contract and observation stay distinct
The service contract is per-session VM isolation. Concurrent probes still avoid assuming process separation: they reported the same host and parent process, but could not read each other's workspace, environment, or credential files.
State lives in session files
Every shell or browser action starts a fresh subprocess. Persistent working directory and audit state are stored beneath the session home.
One pinned workspace
Agent file tools and Foundry session file operations address the pinned session workspace, enabling cross-turn artifact workflows.
Platform and deployer boundary
The service owns isolation mechanics. The deployer still owns session retention, identity scope, data policy, and deletion.
One immutable MCP endpoint, exactly eight skills.
coding-toolbox is additive to local function tools. A created Toolbox
version can contain the eight versioned skills and, when configured, a project
connection such as Web IQ.
MCP tools stay model-callable
The Agents SDK connects to the Toolbox through MCPServerStreamableHttp using a Foundry-scoped Entra token.
MCP resources become instructions
The runtime reads each skill://.../SKILL.md, removes frontmatter, sorts the set, enforces a 16,000-character budget, then injects all eight or none.
- backend-dev
- cloud-auth
- code-review
- frontend-design
- frontend-dev
- systematic-debugging
- web-research-citation
- webapp-testing
latest.
Agent, tool, token, and latency signals with correct identity.
When an Application Insights connection string is supplied, the Foundry host and OpenAI Agents instrumentation export OpenTelemetry data. The sample corrects per-agent attribution at span creation time and includes streaming token usage.
Canonical agent ID
The live semantic processor is configured with the Foundry front-door identity before subsequent spans are created.
Streaming token counts
ModelSettings(include_usage=True) makes usage available to the instrumentor for token metrics.
Full capture for the sample
OTEL_TRACES_SAMPLER=always_on avoids the distro's default rate limit while increasing telemetry volume and cost.
Local gates first; cloud and acceptance gates by intent.
Unit and publication checks are cloud-free. Separate scripts cover deployed smoke, evaluation, red teaming, runtime probes, and deterministic end-to-end acceptance for Space Invaders and Todo applications.
Repository gates
Pytest, Python compilation, Node syntax, dry-run acceptance, and publication-safe source scanning.
Smoke and evaluation
Targeted invocation, traffic generation, Monitor queries, quality evaluation, and AI red-team scripts use explicit cloud configuration.
Independent acceptance
Harnesses download artifacts, run final Node tests independently, and verify deterministic browser contracts. Live-URL-only mode is explicitly partial.
python -m pytest -p no:cacheprovider tests -q python -m compileall -q src scripts server_responses.py node --check src/agent/browser_probe.js python scripts/build_space_invaders_demo.py --dry-run python scripts/build_todo_demo.py --dry-run
Parameterize existing resources; never invent infrastructure.
The deployment script builds a linux/amd64 image in an existing
registry, registers a hosted-agent version, and applies tightly bounded roles.
azure.yaml is an Agent Optimizer and deployment-discovery overlay,
not Bicep, Terraform, or one-click IaC.
Supply existing-resource identifiers
Foundry project, model deployment, registry, dedicated deployment resource group, and two distinct Static Web Apps are required; optional integrations need their own existing resources.
Build the target architecture
ACR builds the image for linux/amd64. The runtime contains Python 3.12, Node 20, Azure CLI, azd, GitHub CLI, SWA CLI, and Chromium tooling.
Register a hosted-agent version
python scripts/deploy_foundry.py --tag <image-tag> validates parameters and creates the hosted version using the preview SDK path.
Apply bounded roles
Model-use roles are limited to AI_ACCOUNT_RESOURCE_ID; Contributor is limited to DEPLOY_RESOURCE_GROUP_ARM_ID. Subscription-wide Contributor is rejected.
Persist state only after success
The script fails on missing principals or role-assignment errors and writes local generated deployment state only after full RBAC success.
Follow the runtime from the outside in.
These files are the shortest path from platform contract to behavior. Generated state stays outside source control under ignored directories.
| Path | Responsibility | Read when |
|---|---|---|
server_responses.py | Foundry responses host, streaming handler, telemetry identity | Tracing a request from the platform |
src/agent/core.py | Agent construction and turn orchestration | Understanding the model/tool loop |
src/agent/config.py | Environment contract, flags, defaults, optimizer hook | Changing behavior safely |
src/agent/azure_client.py | Keyless Azure OpenAI model and Responses clients | Reviewing auth or model wiring |
src/agent/tools.py | Demo, web, knowledge, and tool assembly | Seeing the active tool catalog |
src/agent/fs_tools.py | Workspace-confined file, patch, and Python tools | Reviewing artifact operations |
src/agent/shell_tools.py | Real terminal, auth gates, browser wrapper, session-safe state | Reviewing execution boundaries |
src/agent/browser_probe.js | Chromium network and rendered-output policy | Reviewing browser trust controls |
src/agent/memory.py | Scope resolution, search-before, update-after | Reviewing personalization |
src/agent/guardrails.py | Optional Prompt Shields and text moderation hooks | Reviewing Content Safety behavior |
src/agent/toolbox.py | Immutable MCP connection and exact skill injection | Reviewing centrally managed capabilities |
scripts/ | Setup, deploy, verify, evaluate, probe, and acceptance entry points | Operating the sample |