1
0
mirror of https://github.com/docker/docs.git synced 2026-10-23 18:09:02 +03:00

docker-agent: single-source docs via Hugo module mount (#25480)

Single-sources the Docker Agent documentation from the product repo via
a Hugo module mount, joining the pattern used by CLI, Buildx, Compose,
BuildKit, Model Runner, and the Moby API. Closes the docs.docker.com
side of docker/docker-agent#3371.

## Context

The pages under `content/manuals/ai/docker-agent/` were hand-authored
duplicates of the product repo docs and drifted on every upstream
change. The source docs were converted to portable Hugo-clean Markdown
and prepared for mounting in docker/docker-agent#3413 and
docker/docker-agent#3414, released in docker-agent v1.96.0.

## What changed

| Change | Detail |
|---|---|
| `go.mod` | `github.com/docker/docker-agent v1.96.0` added to `require`
and `tool` |
| `hugo.yaml` | `module.imports` mounts the eight docs sections and
`demo.gif` onto `content/manuals/ai/docker-agent/` |
| Content | The 16 hand-authored pages are removed; `_index.md` stays
hand-authored (landing page), with links rewritten to the mounted pages
|
| `_vendor/` | 99 vendored files from the module |
| `.github/workflows/sync-docker-agent-docs.yml` | Daily poll that
vendors the latest docker-agent release and opens a bot PR, modeled on
`sync-cli-docs.yml` |

## URL preservation

Every removed page keeps its URL: the mounted pages carry `aliases`
front matter (added upstream in docker/docker-agent#3414), and
`_index.md` keeps aliases for the removed section indexes
(`integrations/`, `reference/`, `reference/examples/`).

| Old URL | Redirects to |
|---|---|
| `/ai/docker-agent/tutorial/` | `.../getting-started/quickstart/` |
| `/ai/docker-agent/best-practices/` | `.../guides/tips/` |
| `/ai/docker-agent/evals/` | `.../features/evaluation/` |
| `/ai/docker-agent/local-models/` | `.../providers/local/` |
| `/ai/docker-agent/model-providers/` | `.../providers/overview/` |
| `/ai/docker-agent/rag/` | `.../tools/rag/` |
| `/ai/docker-agent/sharing-agents/` | `.../concepts/distribution/` |
| `/ai/docker-agent/integrations/{a2a,acp,mcp}/` | `.../features/a2a/`,
`.../features/acp/`, `.../tools/mcp/` |
| `/ai/docker-agent/reference/{cli,config,toolsets}/` |
`.../features/cli/`, `.../configuration/overview/`,
`.../configuration/tools/` |
| `/ai/docker-agent/integrations/`, `/ai/docker-agent/reference/`,
`/ai/docker-agent/reference/examples/` | `/ai/docker-agent/` |

## Mount scope

All eight sections are mounted rather than a core subset:
`refLinksErrorLevel: ERROR` makes partial mounts fail the build for any
relative link into an unmounted page, and the link closure from the core
sections already pulls in 68 of the 90 pages. Mounting everything keeps
the build link-safe and removes all drift.

## Keeping the pin fresh

On each docker-agent release, `sync-docker-agent-docs.yml` (daily poll,
mirrors `sync-cli-docs.yml`) compares the latest release tag against the
pinned version and opens a bot PR with the re-vendored module.
docker/docker-agent also runs `validate-upstream.yml` on every docs PR,
so upstream changes that would break this site fail CI before they merge
(two source-side issues were caught exactly this way while preparing
this change).

## Validation

`docker buildx bake validate` passes on this branch:

| Target | Result |
|---|---|
| test (Hugo build + htmltest) | pass (3196 pages, 1515 aliases, 3110
documents tested) |
| lint | pass |
| unused-media | pass |
| test-go-redirects | pass |
| validate-vendor | pass |
| dockerfile-lint | pass |

`actionlint` passes on the new workflow.
This commit is contained in:
Sayt0
2026-07-03 14:31:26 +02:00
committed by GitHub
parent 915f25ad6b
commit d55fcce4fe
122 changed files with 17461 additions and 4166 deletions

View File

@@ -0,0 +1,130 @@
name: sync-docker-agent-docs
on:
schedule:
# Run daily at 02:30 UTC, offset from sync-cli-docs
- cron: '30 2 * * *'
workflow_dispatch:
inputs:
version:
description: "(optional) docker-agent version - defaults to the latest release tag"
required: false
default: ""
pull_request:
paths:
- '.github/workflows/sync-docker-agent-docs.yml'
permissions:
contents: write
pull-requests: write
env:
BRANCH_NAME: "bot/sync-docker-agent-docs"
MODULE_NAME: "github.com/docker/docker-agent"
jobs:
sync-docker-agent-docs:
runs-on: ubuntu-24.04
steps:
-
name: Checkout docs repo
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5
with:
fetch-depth: 0
-
name: Get target version
id: get-version
env:
GH_TOKEN: ${{ github.token }}
run: |
if [ -n "${{ inputs.version }}" ]; then
VERSION="${{ inputs.version }}"
else
VERSION=$(gh release view --repo docker/docker-agent --json tagName --jq .tagName)
fi
CURRENT=$(go list -m -f '{{ .Version }}' "$MODULE_NAME" || true)
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
echo "current=$CURRENT" >> "$GITHUB_OUTPUT"
echo "docker-agent version: **$VERSION** (currently pinned: **$CURRENT**)" | tee -a "$GITHUB_STEP_SUMMARY"
-
name: Set up Docker Buildx
if: steps.get-version.outputs.version != steps.get-version.outputs.current
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4
-
# go mod edit sets the version before plain make vendor: passing
# VENDOR_MODULE would run hugo mod get, which records docker-agent's
# full transitive graph as indirect requires in go.mod.
name: Update vendor
if: steps.get-version.outputs.version != steps.get-version.outputs.current
run: |
go mod edit -require "${MODULE_NAME}@${{ steps.get-version.outputs.version }}"
make vendor
-
name: Detect changes
id: changes
if: steps.get-version.outputs.version != steps.get-version.outputs.current
run: |
if [ -n "$(git status --porcelain)" ]; then
echo "changes=true" >> "$GITHUB_OUTPUT"
echo "Changes detected - syncing Docker Agent docs" >> "$GITHUB_STEP_SUMMARY"
else
echo "changes=false" >> "$GITHUB_OUTPUT"
echo "No changes to sync - Docker Agent docs are up to date" >> "$GITHUB_STEP_SUMMARY"
fi
-
name: Commit changes
if: steps.changes.outputs.changes == 'true'
env:
BRANCH_NAME: ${{ env.BRANCH_NAME }}
VERSION: ${{ steps.get-version.outputs.version }}
run: |
git checkout -b "$BRANCH_NAME"
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add -A
git commit -m "docker-agent: sync docs with docker-agent $VERSION"
-
name: Create or update Pull Request
if: steps.changes.outputs.changes == 'true' && github.event_name != 'pull_request'
env:
GH_TOKEN: ${{ github.token }}
BRANCH_NAME: ${{ env.BRANCH_NAME }}
PR_TITLE: "docker-agent: sync docs with docker-agent ${{ steps.get-version.outputs.version }}"
PR_BODY: |
## Summary
Automated sync of Docker Agent documentation from the docker/docker-agent repository.
**docker-agent version:** ${{ steps.get-version.outputs.version }}
---
> [!IMPORTANT]
> **Reviewer:** Please close and reopen this PR to trigger CI checks.
> See: https://docs.github.com/en/actions/how-tos/write-workflows/choose-when-workflows-run/trigger-a-workflow#triggering-a-workflow-from-a-workflow
run: |
# Check for existing open PR from this branch
EXISTING_PR=$(gh pr list --state open --head "$BRANCH_NAME" --json url --jq ".[0].url // empty")
if [ -n "$EXISTING_PR" ]; then
echo "Updating existing PR: $EXISTING_PR" >> "$GITHUB_STEP_SUMMARY"
git push -u origin "$BRANCH_NAME" --force
gh pr edit "$EXISTING_PR" --title "$PR_TITLE" --body "$PR_BODY"
else
# Check if a closed PR with the same title already exists
CLOSED_PR=$(gh pr list --state closed --search "$PR_TITLE in:title" --json url --jq ".[0].url // empty")
if [ -n "$CLOSED_PR" ]; then
echo "A closed PR already exists for this version: $CLOSED_PR" >> "$GITHUB_STEP_SUMMARY"
echo "Skipping PR creation."
exit 0
fi
echo "Creating new PR" >> "$GITHUB_STEP_SUMMARY"
git push -u origin "$BRANCH_NAME"
gh pr create \
--title "$PR_TITLE" \
--body "$PR_BODY" \
--base main \
--head "$BRANCH_NAME"
fi

View File

@@ -0,0 +1,5 @@
---
title: "Community"
description: "Contributing, troubleshooting, and telemetry."
weight: 80
---

View File

@@ -0,0 +1,130 @@
---
title: "Contributing"
description: "docker-agent is open source. Here's how to set up your development environment and contribute."
keywords: docker agent, ai agents, community, contributing
weight: 10
---
_docker-agent is open source. Here's how to set up your development environment and contribute._
## Development Setup
### Prerequisites
- [Go 1.26](https://go.dev/dl/) or higher
- API key(s) for your chosen AI provider
- [Task](https://taskfile.dev/installation/)
- [golangci-lint](https://golangci-lint.run/docs/welcome/install/local/)
> [!NOTE]
> **Platform Support**
>
> macOS and Linux are fully supported for development. On Windows, use `task build-local` to build via Docker.
### Build from Source
```bash
# Clone and build
git clone https://github.com/docker/docker-agent.git
cd docker-agent
task build
# Set API keys
export OPENAI_API_KEY=your_key_here
export ANTHROPIC_API_KEY=your_key_here
# Run an example
./bin/docker-agent run examples/code.yaml
```
### Development Commands
| Command | Description |
| ------------------ | ----------------------------------------------- |
| `task build` | Build the binary to `./bin/docker-agent` |
| `task test` | Run all tests (clears API keys for determinism) |
| `task lint` | Run golangci-lint |
| `task format` | Format code |
| `task dev` | Run lint, test, and build in sequence |
| `task build-local` | Build for local platform via Docker |
| `task cross` | Cross-platform builds (all architectures) |
## Dogfooding
Use docker-agent to work on docker-agent! The project includes a specialized developer agent:
```bash
cd docker-agent
docker agent run ./golang_developer.yaml
```
This agent is an expert Go developer that understands the docker-agent codebase. Ask it questions, request fixes, or have it implement features.
## Core Concepts
- **Root Agent** — Main entry point that coordinates the system
- **Sub-Agents** — Specialized agents for specific domains
- **Tools** — External capabilities via MCP
- **Models** — AI provider configurations
## Code Style
The project uses `golangci-lint` with strict rules. As long as `task lint` passes, the code is stylistically acceptable.
Key conventions:
- Use `fmt.Errorf("context: %w", err)` for error wrapping
- Always pass `context.Context` as the first parameter
- Use `slog` for structured logging
- Use functional options pattern for constructors
- In tests: use `t.Context()`, `t.TempDir()`, `t.Setenv()`, and `t.Parallel()`
## Opening Issues
File issues on the [GitHub issue tracker](https://github.com/docker/docker-agent/issues). Please:
> [!NOTE]
> **See also**
>
> [Troubleshooting](../troubleshooting/index.md) — Common issues and debug mode. [Telemetry](../telemetry/index.md) — What data is collected and how to opt out.
- Use the included issue template
- Search for existing issues before creating new ones
- Only use issues for bugs and feature requests (not support)
## Submitting Pull Requests
1. **Fork** the repository and create a branch for your changes
2. **Write** your code following the style and testing guidelines above
3. **Test** your changes: run `task lint` and `task test`
4. **Sign** your commits with `git commit -s` (DCO required)
5. **Open a pull request** against the `main` branch
> [!TIP]
> Use the dogfooding agent (`docker agent run ./golang_developer.yaml`) to help write and review your changes before submitting.
## Sign Your Work
All contributions require a Developer Certificate of Origin (DCO) sign-off:
```bash
# Sign commits automatically
git config user.name "Your Name"
git config user.email "your.email@example.com"
git commit -s -m "Your commit message"
```
## Community
Find us on [Slack](https://dockercommunity.slack.com/archives/C09DASHHRU4) for questions and discussions.
## Code of Conduct
We want to keep the docker-agent community welcoming, inclusive, and collaborative. Key guidelines:
- **Be nice** — Be courteous, respectful, and polite. No abuse of any kind will be tolerated.
- **Encourage diversity** — Make everyone feel welcome regardless of background.
- **Keep it legal** — Share only content you own and don't break the law.
- **Stay on topic** — Post to the correct channel and avoid off-topic discussions.
The governance for this repository is handled by Docker Inc.

View File

@@ -0,0 +1,98 @@
---
title: "OpenTelemetry Tracing"
description: "Export docker-agent traces to any OTLP backend, including Langfuse and LangSmith, for debugging agentic workflows."
keywords: docker agent, ai agents, community, opentelemetry tracing
weight: 40
---
_docker-agent can export OpenTelemetry traces of an agent run to any OTLP/HTTP backend. This is separate from [product-analytics telemetry](../telemetry/index.md) and is opt-in via the `--otel` flag._
When enabled, docker-agent emits OpenTelemetry GenAI (`gen_ai.*`) and MCP (`mcp.*`) spans following the [OpenTelemetry semantic conventions](https://opentelemetry.io/docs/specs/semconv/gen-ai/). Spans cover the agent turn, model calls (with token usage and cost attributes), tool calls, MCP client/server activity, sub-agent hand-offs, and provider fallbacks. W3C `traceparent` context is propagated so the whole run renders as a single connected trace tree.
## Enabling
```bash
docker agent run agent.yaml --otel
```
Without an exporter endpoint configured, spans are recorded locally as no-ops. To ship them somewhere, set the standard OTLP environment variables described below.
## Configuration
docker-agent reads the standard OTLP environment variables:
| Variable | Purpose |
| --- | --- |
| `OTEL_EXPORTER_OTLP_ENDPOINT` | Base OTLP/HTTP endpoint. The signal subpath (`/v1/traces`, `/v1/metrics`, `/v1/logs`) is appended automatically. |
| `OTEL_EXPORTER_OTLP_HEADERS` | Comma-separated `key=value` headers sent with every export request (for example, an `Authorization` header). |
| `OTEL_RESOURCE_ATTRIBUTES` | Extra resource attributes merged into every span. |
| `OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT` | Set to `true` to capture prompt and response message content as span attributes. Off by default. |
> [!NOTE]
> **Base endpoint, not the full signal URL**
>
> Set `OTEL_EXPORTER_OTLP_ENDPOINT` to the **base** endpoint (for example `https://cloud.langfuse.com/api/public/otel`). docker-agent appends `/v1/traces` for you, matching the value documented by Langfuse and LangSmith. A bare `host:port` is also accepted and gets `https://` (or `http://` for localhost).
> [!WARNING]
> **Message content can contain sensitive data**
>
> `OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT` is off by default because chat history routinely contains PII, secrets, and internal documents. Enable it only for backends and environments where exporting that content is acceptable.
## Backends
Protocol support is OTLP over HTTP (`http/protobuf`). gRPC endpoints are not currently supported.
### Langfuse
[Langfuse](https://langfuse.com) exposes an OTLP endpoint and authenticates with HTTP Basic auth built from a project's public and secret keys.
```bash
# Base64 of "public_key:secret_key"
LANGFUSE_AUTH=$(echo -n "pk-lf-...:sk-lf-..." | base64)
export OTEL_EXPORTER_OTLP_ENDPOINT="https://cloud.langfuse.com/api/public/otel"
export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Basic ${LANGFUSE_AUTH}"
docker agent run agent.yaml --otel
```
Regional and self-hosted hosts use the same `/api/public/otel` base path:
| Region | Endpoint |
| --- | --- |
| EU | `https://cloud.langfuse.com/api/public/otel` |
| US | `https://us.cloud.langfuse.com/api/public/otel` |
| Self-hosted (>= v3.22.0) | `http://localhost:3000/api/public/otel` |
### LangSmith
[LangSmith](https://docs.langchain.com/langsmith/trace-with-opentelemetry) authenticates with an `x-api-key` header (the raw API key, with no `Basic`/`Bearer` prefix). An optional `Langsmith-Project` header routes traces to a named project.
```bash
export OTEL_EXPORTER_OTLP_ENDPOINT="https://api.smith.langchain.com/otel"
export OTEL_EXPORTER_OTLP_HEADERS="x-api-key=<your-api-key>,Langsmith-Project=<project>"
docker agent run agent.yaml --otel
```
### OpenTelemetry Collector
Any OTLP/HTTP collector (the OpenTelemetry Collector, Grafana Alloy, Jaeger, and so on) works by pointing at its base endpoint:
```bash
export OTEL_EXPORTER_OTLP_ENDPOINT="http://localhost:4318"
docker agent run agent.yaml --otel
```
> [!NOTE]
> **Langfuse and LangSmith ingest traces only**
>
> Both backends accept the traces signal only. docker-agent also wires metric and log exporters at the same endpoint, so their periodic exports return `404` against trace-only backends. This is harmless to traces but appears in the debug log. Point a full OTLP collector at the endpoint if you also want metrics and logs.
## Inspecting traces locally
Use `--debug` to print telemetry activity to the debug log (`~/.cagent/cagent.debug.log` by default) without standing up a backend:
```bash
docker agent run agent.yaml --otel --debug
```

View File

@@ -0,0 +1,78 @@
---
title: "Telemetry"
description: "docker-agent collects anonymous usage data to help improve the tool. Telemetry can be disabled at any time."
keywords: docker agent, ai agents, community, telemetry
weight: 30
---
_docker-agent collects anonymous usage data to help improve the tool. Telemetry can be disabled at any time._
On first startup, docker-agent displays a notice about telemetry collection so you're always informed. All events are processed synchronously when recorded.
## Disabling Telemetry
```bash
# Disable via environment variable
$ TELEMETRY_ENABLED=false docker agent run agent.yaml
# Or export it in your shell profile
$ export TELEMETRY_ENABLED=false
```
> [!NOTE]
> **Default**
>
> Telemetry is **enabled by default**. Set `TELEMETRY_ENABLED=false` to opt out.
## What's Collected ✅
- Command names and success/failure status
- Agent names and model types
- Tool names and whether calls succeed or fail
- Token counts (input/output totals) and estimated costs
- Session metadata (durations, error counts)
## What's NOT Collected ❌
- User input or prompts
- Agent responses or generated content
- File contents
- API keys or credentials
- Personally identifying information (PII)
> [!TIP]
> **See events locally**
>
> Use `--debug` to see telemetry events printed to the debug log without sending them anywhere additional.
```bash
docker agent run agent.yaml --debug
```
## Event Types
The telemetry system uses structured, type-safe events:
| Event Type | What It Tracks |
| ----------- | ------------------------------------------------------------------- |
| **Command** | CLI command execution with success status |
| **Tool** | Agent tool calls with timing and error information |
| **Token** | LLM token usage by model, session, and cost |
| **Session** | Agent session lifecycle with start/end events and aggregate metrics |
## For Developers
Telemetry is automatically wrapped around all commands. To record additional events, use the context-based API:
```bash
// Recommended: context-based telemetry (clean, testable)
if telemetryClient := telemetry.FromContext(ctx); telemetryClient != nil {
telemetryClient.RecordToolCall(ctx, "filesystem", "session-id", "agentName", time.Millisecond*500, nil)
telemetryClient.RecordTokenUsage(ctx, "gpt-4", 100, 50, 0.01)
}
// Or use direct calls
telemetry.TrackCommand("run", args)
```
Events are processed synchronously when `Track()` is called, sending HTTP requests immediately.

View File

@@ -0,0 +1,254 @@
---
title: "Troubleshooting"
description: "Common issues and how to resolve them when working with docker-agent."
keywords: docker agent, ai agents, community, troubleshooting
weight: 20
---
_Common issues and how to resolve them when working with docker-agent._
## Common Errors
### Context Window Exceeded
Error message: `context_length_exceeded` or similar.
- Use `/compact` in the TUI to summarize and reduce conversation history
- Set `num_history_items` in agent config to limit messages sent to the model
- Switch to a model with larger context (Claude Sonnet 4.5 supports 1M tokens, Gemini up to 2M)
- Break large tasks into smaller conversations
### Max Iterations Reached
The agent hit its `max_iterations` limit without completing the task.
- Increase `max_iterations` in agent config (default is unlimited, but many agents set 20-50)
- Check if the agent is stuck in a loop (enable `--debug` to see tool calls)
- Break complex tasks into smaller steps
### Model Fallback Triggered
When the primary model fails, docker-agent automatically switches to fallback models. Look for log messages like `"Switching to fallback model"`.
- **429 errors:** Rate limited — the cooldown period keeps using the fallback
- **5xx errors:** Server issues — retries with exponential backoff first, then falls back
- **4xx errors:** Client errors — skips directly to next model
Configure fallback behavior in your agent config:
```yaml
agents:
root:
model: anthropic/claude-sonnet-4-5
fallback:
models: [openai/gpt-5-mini, openai/gpt-4o-mini]
retries: 2 # retries per model for 5xx errors
cooldown: 1m # how long to stick with fallback after 429
```
## Debug Mode
The first step for any issue is enabling debug logging. This provides detailed information about what docker-agent is doing internally.
```bash
# Enable debug logging (writes to ~/.cagent/cagent.debug.log)
$ docker agent run config.yaml --debug
# Write debug logs to a custom file
$ docker agent run config.yaml --debug --log-file ./debug.log
# Enable OpenTelemetry tracing for deeper analysis
$ docker agent run config.yaml --otel
```
> [!TIP]
> Always enable `--debug` when reporting issues. The log file contains detailed traces of API calls, tool executions, and agent interactions.
## Agent Not Responding
### API keys not set
Each model provider requires its own API key as an environment variable:
| Provider | Environment Variable |
| ------------- | --------------------------------------------------- |
| OpenAI | `OPENAI_API_KEY` |
| Anthropic | `ANTHROPIC_API_KEY` |
| Google Gemini | `GOOGLE_API_KEY` or `GEMINI_API_KEY` |
| Mistral | `MISTRAL_API_KEY` |
| xAI | `XAI_API_KEY` |
| Nebius | `NEBIUS_API_KEY` |
| MiniMax | `MINIMAX_API_KEY` |
| Requesty | `REQUESTY_API_KEY` |
| OpenRouter | `OPENROUTER_API_KEY` |
| GitHub Copilot | `GITHUB_TOKEN` (PAT with `copilot` scope) |
| Azure OpenAI | `AZURE_API_KEY` (override with `token_key`) |
| AWS Bedrock | `AWS_BEARER_TOKEN_BEDROCK` or AWS credentials chain |
```bash
# Verify your keys are set
$ env | grep API_KEY
```
### Incorrect model name
Model names must match the provider's naming exactly. Common mistakes:
- Using a deprecated model name (e.g. `gpt-4` instead of `gpt-5-mini` or `gpt-4o`)
- Model references are case-sensitive: `openai/gpt-5-mini` ≠ `openai/GPT-5-mini`
### Network connectivity
If the agent hangs or times out, check that you can reach the provider's API endpoint. Firewalls, VPNs, or proxy settings may block requests.
## Tool Execution Failures
### MCP tools not found or failing
- Ensure the MCP tool command is installed and on your `PATH`
- Check file permissions — tools need to be executable
- Test MCP tools independently before integrating with docker-agent
- For Docker-based MCP tools (`ref: docker:*`), ensure Docker Desktop is running
### Filesystem / shell tool errors
- Verify the agent has the correct toolset configured (`type: filesystem`, `type: shell`)
- Check that the working directory exists and is accessible
- On macOS, ensure terminal has the necessary permissions (e.g., Full Disk Access)
### Tool lifecycle issues
MCP and LSP toolsets are managed by a supervisor that auto-restarts them when they crash or drop their session. The TUI exposes that supervisor through two slash commands:
- `/tools` — the unified tools dialog. Its top section lists every toolset with its current state (`Stopped`, `Starting`, `Ready`, `Degraded`, `Restarting`, `Failed`), restart count, and last error; the bottom section lists every tool the agent can call. Start here whenever a tool seems missing or stuck.
- `/toolset-restart <name>` — force a supervisor-driven reconnect of the named toolset. Useful after completing OAuth, when a remote MCP server has been redeployed, or when a language server like `gopls` is unresponsive.
Remote MCP servers that return `401 invalid_token` (e.g. because the stored OAuth token was revoked or rotated) are now self-healing: docker-agent silently exchanges the refresh token for a new one when possible, or surfaces an OAuth re-authentication prompt on your next message when refresh is not possible. No more stuck toolsets that require a process restart — but if you want to trigger re-auth immediately, `/toolset-restart <name>` forces it right away.
MCP tools using stdio transport must complete the initialization handshake before becoming available. If tools fail silently:
1. Run `/tools` to see whether the toolset is `Failed` or stuck in `Restarting`, and what the last error was.
2. Enable `--debug` and look for MCP protocol messages in the log
3. Check that the MCP server process starts and responds to `initialize`
4. Verify environment variables required by the tool are set (check `env` and `env_file` in the toolset config)
> [!NOTE]
> **Startup tool-listing timeout**
>
> At startup, docker-agent queries each toolset for its tool list. If a toolset does not respond within 10 seconds (e.g. a wedged MCP stdio server that never answers `tools/list`), that toolset is skipped with a warning and the remaining toolsets load normally. The sidebar resolves showing whichever tools did load — no infinite spinner. Enable `--debug` to see the warning message, and use `/toolset-restart <name>` once the server becomes responsive.
If a toolset keeps crashing in a tight loop, tune the [`lifecycle`](../../configuration/tools/index.md#toolset-lifecycle) block on the toolset (e.g. raise `backoff.initial`, lower `max_restarts`, or switch to the `best-effort` profile) so a flaky dependency does not amplify into a restart storm.
## Configuration Errors
### YAML syntax issues
docker-agent validates config at startup and reports errors with line numbers. Common problems:
- Incorrect indentation (YAML is whitespace-sensitive)
- Missing quotes around values containing special characters (`:`, `#`, `{`, `}`)
- Using tabs instead of spaces
### Missing references
- Local agents in `sub_agents` must be defined in the `agents` section (external OCI references like `agentcatalog/pirate` are resolved from registries automatically)
- Named model references must exist in the `models` section (or use inline format like `openai/gpt-5`)
- RAG source names referenced by agents must be defined in the `rag` section
### Toolset validation
- The `path` field is valid for `memory` and `tasks` toolsets, and for the agent-level `cache` block
- MCP toolsets need either `command` (stdio), `remote` (Streamable HTTP/SSE), or `ref` (Docker)
- Provider names must be one of: `openai`, `anthropic`, `google`, `amazon-bedrock`, `dmr`, etc.
> [!NOTE]
> **Schema Validation**
>
> Use the [JSON schema](https://github.com/docker/docker-agent/blob/main/agent-schema.json) in your editor for real-time config validation and autocompletion.
## Session &amp; Connectivity Issues
### Port conflicts
When running docker-agent as an API server or MCP server, ensure the port is not already in use:
```bash
# Check if port 8080 is in use
$ lsof -i :8080
# Use a different port
$ docker agent serve api config.yaml --listen :9090
```
### MCP endpoint accessibility
For remote MCP servers, verify the endpoint is reachable:
```bash
# Test streamable HTTP endpoint
$ curl -v https://mcp-server.example.com/mcp
```
### Session isolation
The API server stores every conversation as a distinct session in the SQLite database (`session.db` by default). Each session is identified by its UUID and only mixes messages when the same session ID is reused. If conversations seem to bleed into each other:
- Make sure each client creates a fresh session via `POST /api/sessions` (don't reuse session IDs across users).
- Confirm `--session-db` points to the path you expect — a stale database from another run can resurface old sessions.
- Use `GET /api/sessions/:id` to inspect what is actually stored, and `DELETE /api/sessions/:id` to clear sessions you don't want anymore.
## Performance Issues
### High memory usage
- Large context windows (64K+ tokens) consume significant memory — consider reducing `max_tokens`
- Use `num_history_items` in agent config to limit conversation history
- For DMR (local models), tune `runtime_flags` for your hardware (e.g., `--ngl` for GPU layers)
### Slow responses
- Check if MCP tools are adding latency (visible in debug logs)
- Use the `/cost` command in TUI to see token usage and identify expensive interactions
- For DMR, consider enabling [speculative decoding](../../providers/dmr/index.md) for faster inference
### Tool resource leaks
Monitor for tools that don't clean up properly — check debug logs for MCP server start/stop lifecycle events. Orphaned tool processes can consume system resources.
## Agent Store Issues
### Pull / push failures
```bash
# Test registry connectivity
$ docker pull docker.io/username/agent:latest
# Verify pulled agent content
$ docker agent share pull docker.io/username/agent:latest
```
### Agent content issues
- Ensure the pushed YAML is valid — run `docker agent run` locally before pushing
- Check that referenced resources (MCP tools, files) are available on the target machine
- For auto-refresh (`--pull-interval`), verify the registry is accessible from the server
## Log Analysis
When reviewing debug logs, search for these key patterns:
| Log Pattern | What It Indicates |
| --------------------------- | ------------------------------------------------------------------------------------------------ |
| `"Starting runtime stream"` | Agent execution beginning |
| `"Tool call"` | A tool is being executed |
| `"Tool call result"` | Tool execution completed |
| `"Stream stopped"` | Agent finished processing |
| `HTTP 429` | Rate limiting — consider adding a [fallback model](../../configuration/agents/index.md) |
| `context canceled` | Operation was interrupted (timeout or user cancel) |
| `[RAG Manager]` | RAG retrieval operations |
| `[Reranker]` | Reranking operations |
> [!WARNING]
> **Still stuck?**
>
> If these steps don't resolve your issue, file a bug on the [GitHub issue tracker](https://github.com/docker/docker-agent/issues) with your debug log attached, or ask on [Slack](https://dockercommunity.slack.com/archives/C09DASHHRU4).

View File

@@ -0,0 +1,5 @@
---
title: "Core Concepts"
description: "The core ideas behind Docker Agent: agents, models, tools, and multi-agent teams."
weight: 20
---

View File

@@ -0,0 +1,113 @@
---
title: "Agents"
description: "Agents are the core building blocks of docker-agent. Each agent is an AI-powered entity with a model, instructions, tools, and optional sub-agents."
keywords: docker agent, ai agents, concepts, agents
weight: 10
---
_Agents are the core building blocks of docker-agent. Each agent is an AI-powered entity with a model, instructions, tools, and optional sub-agents._
## What is an Agent?
An agent in docker-agent is defined by:
- **Model** — The AI model powering it (e.g., Claude, GPT-5, Gemini). See [Models](../models/index.md).
- **Description** — A brief summary of what the agent does (used by other agents for delegation)
- **Instruction** — The system prompt that defines the agent's behavior and personality
- **Tools** — Capabilities like filesystem access, shell commands, or external APIs
- **Sub-agents** — Other agents it can delegate tasks to
```yaml
agents:
root:
model: anthropic/claude-sonnet-4-5
description: Expert software developer
instruction: |
You are an expert developer. Write clean, efficient code
and explain your reasoning step by step.
toolsets:
- type: filesystem
- type: shell
- type: think
```
## The Root Agent
Every docker-agent configuration has a **root agent** — the entry point that receives user messages. In a single-agent setup, this is the only agent. In a multi-agent setup, the root agent acts as a coordinator, delegating tasks to specialized sub-agents.
> [!NOTE]
> **Naming**
>
> The first agent defined in your YAML (or the one named `root`) is the root agent by default. You can also specify which agent to start with using `docker agent run config.yaml -a agent_name`.
## Agent Properties
| Property | Type | Required | Description |
| ---------------------- | ------- | -------- | -------------------------------------------------------------- |
| `model` | string | ✓ | Model reference (inline like `openai/gpt-5` or a named model) |
| `description` | string | ✓ | What the agent does — used by other agents for delegation |
| `instruction` | string | ✓ | System prompt defining behavior |
| `toolsets` | array | ✗ | List of tool configurations |
| `sub_agents` | array | ✗ | Names of agents this agent can delegate to |
| `fallback` | object | ✗ | Fallback model configuration for resilience |
| `add_date` | boolean | ✗ | Include current date in context |
| `add_environment_info` | boolean | ✗ | Include OS, working directory, git info in context |
| `max_iterations` | int | ✗ | Max tool-calling loops (default: unlimited) |
| `commands` | object | ✗ | Named prompts callable via `/command` |
| `skills` | boolean \| list | ✗ | Enable skill discovery and loading. `true` = `["local"]`; list values may combine `"local"` with remote skill-server URLs. |
## Model Fallbacks
Agents can automatically fail over to alternative models when the primary model is unavailable:
```yaml
agents:
root:
model: anthropic/claude-sonnet-4-5
fallback:
models:
- openai/gpt-5
- google/gemini-3.5-flash
retries: 2 # retries per model for 5xx errors
cooldown: 1m # stick with fallback after 429
```
## Named Commands
Define reusable prompts that can be invoked as commands:
```yaml
agents:
root:
model: openai/gpt-5
instruction: You are a helpful assistant.
commands:
df: "Check how much free space I have on my disk"
greet: "Say hello to ${env.USER}"
```
```bash
# Run a named command
$ docker agent run agent.yaml /df
$ docker agent run agent.yaml /greet
```
Commands support environment variable interpolation using JavaScript template literal syntax. Undefined variables expand to empty strings.
## Default Agent
Running `docker agent run` without a config file uses a built-in default agent. This is a capable general-purpose agent for quick tasks without needing any configuration.
```bash
# Use the default agent
$ docker agent run
# Override the default with an alias
$ docker agent alias add default /path/to/my-agent.yaml
$ docker agent run # now runs your custom agent
```
> [!TIP]
> **See also**
>
> For reusable task-specific instructions, see [Skills](../../features/skills/index.md). For multi-agent patterns, see [Multi-Agent](../multi-agent/index.md). For full config reference, see [Agent Config](../../configuration/agents/index.md).

View File

@@ -0,0 +1,146 @@
---
title: "Agent Distribution"
description: "Package, share, and run agents via OCI-compatible registries — just like container images."
keywords: docker agent, ai agents, concepts, agent distribution
weight: 50
aliases:
- /ai/docker-agent/sharing-agents/
---
_Package, share, and run agents via OCI-compatible registries — just like container images._
## Overview
docker-agent agents can be pushed to any OCI-compatible registry (Docker Hub, GitHub Container Registry, etc.) and pulled/run anywhere. This makes sharing agents as easy as sharing Docker images.
> [!TIP]
> For CLI commands related to distribution, see [CLI Reference](../../features/cli/index.md) (`docker agent share push`, `docker agent share pull`, `docker agent alias`).
## Pushing Agents
```bash
# Push to Docker Hub
$ docker agent share push ./agent.yaml docker.io/username/my-agent:latest
# Push to GitHub Container Registry
$ docker agent share push ./agent.yaml ghcr.io/username/my-agent:v1.0
```
## Pulling Agents
```bash
# Pull an agent
$ docker agent share pull docker.io/username/my-agent:latest
# Pull from the agent catalog
$ docker agent share pull agentcatalog/pirate
```
## Running from a Registry
Run agents directly from a registry without pulling first:
```bash
# Run directly from Docker Hub
$ docker agent run docker.io/username/my-agent:latest
# Run from the agent catalog
$ docker agent run agentcatalog/pirate
# Run with a specific agent from a multi-agent config
$ docker agent run docker.io/username/dev-team:latest -a developer
```
## Agent Catalog
The `agentcatalog` namespace on Docker Hub hosts pre-built agents you can try:
```bash
# Try the pirate-themed assistant
$ docker agent run agentcatalog/pirate
# Try the coding agent
$ docker agent run agentcatalog/coder
```
## Using as Sub-Agents
Registry agents can be used directly as sub-agents in a multi-agent configuration — no need to define them locally:
```yaml
agents:
root:
model: openai/gpt-5
description: Coordinator
instruction: Delegate tasks to the right sub-agent.
sub_agents:
- agentcatalog/pirate # auto-named "pirate"
- my_reviewer:myorg/reviewer # explicitly named "my_reviewer"
```
External sub-agents are automatically named after their last path segment. Use the `name:reference` syntax to give them a custom name.
Tag references are checked against the registry on every `docker agent run`, which adds a network round-trip per sub-agent at startup. Pin them to a digest (`agentcatalog/pirate@sha256:…`) to serve them from cache instead.
See [Pin external sub-agents to a digest](../multi-agent/index.md#pin-external-sub-agents-to-a-digest) and [External Sub-Agents](../multi-agent/index.md#external-sub-agents-from-registries) for details.
## Using with Aliases
Combine OCI references with aliases for convenient access:
```bash
# Create an alias for a registry agent
$ docker agent alias add coder agentcatalog/coder --yolo
# Now just run
$ docker agent run coder
```
## Using with API Server
The API server supports OCI references with auto-refresh:
```bash
# Start API from registry, auto-pull every 10 minutes
$ docker agent serve api docker.io/username/agent:latest --pull-interval 10
```
## Private Repositories
docker-agent supports pulling from private GitHub repositories and registries that require authentication. Use standard Docker login or GitHub authentication:
```bash
# Login to a registry
$ docker login docker.io
# Now push/pull works with private repos
$ docker agent share push ./agent.yaml docker.io/myorg/private-agent:latest
$ docker agent run docker.io/myorg/private-agent:latest
```
> [!NOTE]
> **Docker Desktop credentials**
>
> When pulling or running an agent from a `docker.com` or `*.docker.com` HTTPS URL (e.g. `desktop.docker.com`), docker-agent automatically forwards your Docker Desktop JWT for authentication — no explicit login required when Docker Desktop is running and signed in.
>
> Note: `docker.io` (the standard Docker Hub registry domain) is a separate domain and is **not** covered by automatic JWT forwarding. Agents pulled from `docker.io` or `registry-1.docker.io` still require `docker login docker.io` for private repositories.
> [!NOTE]
> **Troubleshooting**
>
> Having issues with push/pull? See [Troubleshooting](../../community/troubleshooting/index.md) for common registry issues.
## Local Development
For local development and testing, you can run an agent directly from a local HTTP server without a registry:
```bash
# Serve an agent config locally
$ python3 -m http.server 8080
# Run it directly via HTTP
$ docker agent run http://localhost:8080/agent.yaml
$ docker agent run http://127.0.0.1:8080/agent.yaml
```
This is useful for iterating on agent configs served from a local dev server before pushing to a registry. Both `localhost` and `127.0.0.1` addresses are supported with plain `http://` URLs.

View File

@@ -0,0 +1,160 @@
---
title: "Models"
description: "Models are the AI brains behind your agents. docker-agent supports multiple providers and flexible configuration."
keywords: docker agent, ai agents, concepts, models
weight: 20
---
_Models are the AI brains behind your agents. docker-agent supports multiple providers and flexible configuration._
## Inline vs. Named Models
There are two ways to assign a model to an agent:
### Inline (Quick)
Use the `provider/model` shorthand directly in the agent definition:
```yaml
agents:
root:
model: openai/gpt-5
instruction: You are a helpful assistant.
```
### Named (Full Control)
Define models in a `models` section and reference them by name:
```yaml
models:
claude:
provider: anthropic
model: claude-sonnet-4-5
max_tokens: 64000
temperature: 0.7
agents:
root:
model: claude
instruction: You are a helpful assistant.
```
Named models let you configure temperature, token limits, thinking budgets, and other parameters. They're also reusable across multiple agents.
## First Available Models
A named model can also select the first usable model from a priority list. This
is useful for shared configs that should prefer paid cloud models when their API
keys are present, but still work with a local fallback:
```yaml
models:
smart:
first_available:
- anthropic/claude-sonnet-4-5
- openai/gpt-5
- dmr/ai/qwen3
agents:
root:
model: smart
instruction: You are a helpful assistant.
```
At load time, docker-agent selects the first candidate whose credentials are
configured. You only need credentials for one candidate. See
[Model Configuration](../../configuration/models/index.md#first-available-models)
for details.
## Supported Providers
| Provider | Key | Example Models | API Key Env Var |
| ------------------- | ---------------- | ------------------------------------ | ----------------------------------- |
| OpenAI | `openai` | gpt-5, gpt-5-mini, gpt-4o | `OPENAI_API_KEY` |
| Anthropic | `anthropic` | claude-sonnet-4-5, claude-opus-4-7 | `ANTHROPIC_API_KEY` |
| Google | `google` | gemini-3.5-flash, gemini-3-pro | `GOOGLE_API_KEY` / `GEMINI_API_KEY` |
| AWS Bedrock | `amazon-bedrock` | Claude, Nova, Llama models | AWS credentials |
| Docker Model Runner | `dmr` | ai/qwen3, ai/llama3.2 | None (local) |
| Mistral | `mistral` | Mistral models | `MISTRAL_API_KEY` |
| xAI | `xai` | Grok models | `XAI_API_KEY` |
| Nebius | `nebius` | Open-source and specialised models | `NEBIUS_API_KEY` |
| MiniMax | `minimax` | MiniMax models | `MINIMAX_API_KEY` |
| Baseten | `baseten` | DeepSeek, Kimi, GLM, Llama models | `BASETEN_API_KEY` |
| OVHcloud | `ovhcloud` | Qwen, Llama, Mistral, DeepSeek (EU-hosted) | `OVH_AI_ENDPOINTS_ACCESS_TOKEN` |
| Groq | `groq` | Llama, Qwen, GPT-OSS (fast inference) | `GROQ_API_KEY` |
| Fireworks AI | `fireworks` | Kimi, Llama, Qwen, DeepSeek, GLM (open models) | `FIREWORKS_API_KEY` |
| DeepSeek | `deepseek` | DeepSeek-V3 chat and R1 reasoner | `DEEPSEEK_API_KEY` |
| Cerebras | `cerebras` | GPT-OSS, GLM (fast inference) | `CEREBRAS_API_KEY` |
| Together AI | `together` | Llama, Qwen, DeepSeek, Kimi (open models) | `TOGETHER_API_KEY` |
| Hugging Face | `huggingface` | Llama, Qwen, DeepSeek, GLM (open models) | `HF_TOKEN` |
| Cloudflare Workers AI | `cloudflare-workers-ai` | Llama, Mistral, Qwen, Gemma (edge-hosted open models) | `CLOUDFLARE_API_TOKEN` + `CLOUDFLARE_ACCOUNT_ID` |
| Moonshot AI | `moonshot` | Kimi K2 chat, reasoning, and coding models | `MOONSHOT_API_KEY` |
| Vercel AI Gateway | `vercel` | Multi-provider gateway | `AI_GATEWAY_API_KEY` |
| Cloudflare AI Gateway | `cloudflare-ai-gateway` | Multi-provider gateway | `CLOUDFLARE_API_TOKEN` + `CLOUDFLARE_ACCOUNT_ID` + `CLOUDFLARE_GATEWAY_ID` |
| Requesty | `requesty` | Multi-provider gateway | `REQUESTY_API_KEY` |
| OpenRouter | `openrouter` | Multi-provider gateway | `OPENROUTER_API_KEY` |
| Azure OpenAI | `azure` | gpt-4o, gpt-5 on Azure | `AZURE_API_KEY` + `base_url` |
| Ollama | `ollama` | Any local Ollama model | None (local; optional `base_url`) |
| GitHub Copilot | `github-copilot` | Copilot-hosted OpenAI/Anthropic | `GITHUB_TOKEN` (PAT with `copilot`) |
See the [Model Providers](../../providers/overview/index.md) section for detailed configuration guides.
## Model Properties
| Property | Type | Description |
| ------------------- | ---------- | ------------------------------------------------- |
| `provider` | string | Provider identifier (required) |
| `model` | string | Model name (required) |
| `temperature` | float | Randomness: 0.0 (deterministic) to 1.0 (creative) |
| `max_tokens` | int | Maximum response length |
| `top_p` | float | Nucleus sampling: 0.0 to 1.0 |
| `frequency_penalty` | float | Reduce repetition: 0.0 to 2.0 |
| `presence_penalty` | float | Encourage topic diversity: 0.0 to 2.0 |
| `base_url` | string | Custom API endpoint |
| `thinking_budget` | string/int | Reasoning effort configuration |
| `task_budget` | int/object | Total token budget for an agentic task (Anthropic; honored by Opus 4.7 today) |
| `provider_opts` | object | Provider-specific options |
## Reasoning / Thinking Budget
Control how much the model "thinks" before responding:
| Provider | Format | Values | Default |
| ---------- | ---------- | ------------------------------------------------------------------- | -------------------------------- |
| OpenAI | string | `minimal`, `low`, `medium`, `high`, `xhigh` | `medium` (always-reasoning models only) |
| Anthropic | int or str | 1024–32768 tokens, or `adaptive`, `adaptive/<effort>`, effort level | off |
| Gemini 2.5 | int | 0 (off), -1 (dynamic), or token count | -1 (dynamic) |
| Gemini 3 | string | `minimal`, `low`, `medium`, `high` | varies |
| All | string/int | `none` or `0` to disable | — |
```yaml
models:
deep-thinker:
provider: anthropic
model: claude-sonnet-4-5
thinking_budget: 16384
fast-responder:
provider: openai
model: gpt-5
thinking_budget: none # disable thinking
```
> [!NOTE]
> **Multi-provider teams**
>
> Different agents can use different providers in the same config. See [Multi-Agent](../multi-agent/index.md) for patterns.
## Alloy Models
"Alloy models" let you use more than one model in the same conversation — docker-agent alternates between them to leverage the strengths of each:
```yaml
agents:
root:
model: anthropic/claude-sonnet-4-5,openai/gpt-5
instruction: You are a helpful assistant.
```
Read more about the alloy model concept at [xbow.com/blog/alloy-agents](https://xbow.com/blog/alloy-agents).

View File

@@ -0,0 +1,420 @@
---
title: "Multi-Agent Systems"
description: "Build teams of specialized agents that collaborate and delegate tasks to each other."
keywords: docker agent, ai agents, concepts, multi-agent systems
linkTitle: "Multi-Agent"
weight: 40
---
_Build teams of specialized agents that collaborate and delegate tasks to each other._
## Why Multi-Agent?
Complex tasks benefit from specialization. Instead of one monolithic agent trying to do everything, you can create a **team** of focused agents:
- A **coordinator** that understands the overall goal and delegates
- A **developer** that writes code with filesystem and shell access
- A **reviewer** that checks code quality
- A **researcher** that searches the web for information
Each agent has its own model, tools, and instructions — optimized for its specific role.
## Two Patterns: Delegation vs. Handoffs
Docker Agent supports two multi-agent patterns:
| | **Delegation** (`sub_agents`) | **Handoffs** (`handoffs`) |
|---|---|---|
| **Topology** | Hierarchical (parent → child → parent) | Peer-to-peer graph (A → B → C → A) |
| **Session** | Child runs in a **sub-session** | Conversation stays in the **same session** |
| **Context** | Child gets a clean task description | Next agent sees the **full conversation history** |
| **Control flow** | Parent blocks until child finishes, then continues | Active agent switches — previous agent is no longer in the loop |
| **Tool** | `transfer_task` | `handoff` |
| **Best for** | Task delegation to specialists | Pipeline workflows, conversational routing |
You can combine both patterns in the same configuration — an agent can have both `sub_agents` and `handoffs`.
> [!TIP]
> **When to use which**
>
> **`sub_agents`** — Use when a coordinator needs to send tasks to specialists and synthesize their results.
>
> **`handoffs`** — Use when agents should take turns processing the same conversation (pipelines, routing).
>
> **`background_agents`** — Use when multiple independent tasks can run simultaneously.
## Delegation with `sub_agents`
Agents delegate tasks using the built-in `transfer_task` tool, which is automatically available to any agent with `sub_agents`. The parent agent sends a task to a child agent, waits for the result, and then continues.
1. **User** sends a message to the root agent
2. **Root agent** analyzes the request and decides which sub-agent should handle it
3. **Root agent** calls `transfer_task` with the target agent, task description, and expected output
4. **Sub-agent** processes the task in its own agentic loop using its tools
5. **Results** flow back to the root agent, which responds to the user
```bash
# The transfer_task tool call looks like:
transfer_task(
agent="developer",
task="Create a REST API endpoint for user authentication",
expected_output="Working Go code with tests"
)
```
> [!NOTE]
> **Auto-Approved**
>
> Unlike other tools, `transfer_task` is always auto-approved — no user confirmation needed. This allows seamless delegation between agents.
## Handoffs Routing
Handoffs are a peer-to-peer routing pattern where agents **hand off the entire conversation** to another agent. Unlike delegation, there is no sub-session — the conversation stays in a single session and the active agent simply switches.
This pattern is ideal for:
- **Pipeline workflows** — data flows through a chain of specialized agents
- **Conversational routing** — a coordinator routes the user to the right specialist, who can route back when done
- **Graph topologies** — agents can form cycles (A → B → C → A), enabling iterative workflows
### How It Works
1. **User** sends a message to the starting agent
2. **Agent A** processes the message, then calls `handoff` to route to **Agent B**
3. **Agent B** becomes the active agent and sees the **full conversation history**
4. **Agent B** can respond, use its own tools, or hand off to another agent
5. This continues until an agent responds directly without handing off
```bash
# The handoff tool call looks like:
handoff(
agent="summarizer"
)
```
> [!NOTE]
> **Scoped Handoff Targets**
>
> Each agent can only hand off to agents listed in its own `handoffs` array. The `handoff` tool is automatically injected — you don't need to add it manually.
### Example
A coordinator routes to a researcher, who hands off to a summarizer, who returns to the coordinator:
```text
Root ──→ Researcher ──→ Summarizer ──→ Root
```
```yaml
agents:
root:
model: anthropic/claude-sonnet-4-5
description: Coordinator that routes queries
instruction: |
Route research queries to the researcher.
handoffs:
- researcher
researcher:
model: openai/gpt-5
description: Web researcher
instruction: |
Search the web, then hand off to the summarizer.
toolsets:
- type: mcp
ref: docker:duckduckgo
handoffs:
- summarizer
summarizer:
model: openai/gpt-5
description: Summarizes findings
instruction: |
Summarize the research results, then hand off
back to root.
handoffs:
- root
```
> [!TIP]
> **Full pipeline example**
>
> For a more complex handoff graph with branching and multiple processing stages, see [`examples/handoff.yaml`](https://github.com/docker/docker-agent/blob/main/examples/handoff.yaml).
### Forced Handoffs
With `handoffs`, the **model decides** whether to call the `handoff` tool — which means it can forget to, breaking pipelines that depend on a strict order. `force_handoff` removes that uncertainty: whenever the agent produces a final response, the **runtime itself** routes the conversation to the named agent, bypassing the LLM's tool-calling entirely. The full conversation context carries over.
```yaml
agents:
root:
model: anthropic/claude-sonnet-4-5
description: Extracts key facts from the input
instruction: |
Extract the key facts from the user's input as a bullet list.
force_handoff: summarizer
summarizer:
model: anthropic/claude-sonnet-4-5
description: Produces the final summary
instruction: |
Summarize the extracted facts for the user.
```
Rules enforced at config load time:
- The target must be an agent defined in the config (or an external reference)
- An agent cannot `force_handoff` to itself
- Chains of `force_handoff` edges must not form a cycle (A → B → A is rejected)
See [`examples/force_handoff.yaml`](https://github.com/docker/docker-agent/blob/main/examples/force_handoff.yaml) for a runnable example.
## Parallel Delegation with Background Agents
`transfer_task` is **sequential** — the coordinator waits for the sub-agent to finish before continuing. When you need to fan out work to multiple agents at the same time, use the `background_agents` toolset instead.
Add it to your coordinator's toolsets:
```yaml
agents:
root:
model: anthropic/claude-sonnet-4-5
description: Research coordinator
sub_agents: [researcher, analyst, writer]
toolsets:
- type: think
- type: background_agents
```
The coordinator can then:
1. **Dispatch** several tasks at once with `run_background_agent` — each returns a task ID immediately
2. **Monitor** progress with `list_background_agents` or `view_background_agent`
3. **Collect** results once tasks complete
4. **Cancel** tasks that are no longer needed with `stop_background_agent`
```bash
# Start two tasks in parallel
run_background_agent(agent="researcher", task="Find recent papers on LLM agents")
run_background_agent(agent="analyst", task="Analyze our current architecture")
# Check on all tasks
list_background_agents()
# Read results when ready
view_background_agent(task_id="agent_task_abc123")
```
## External Sub-Agents from Registries
Sub-agents don't have to be defined locally — you can reference agents from OCI registries (such as the [Docker Agent Catalog](https://hub.docker.com/u/agentcatalog)) directly in your `sub_agents` list. This lets you compose teams using pre-built, shared agents without duplicating their configuration.
```yaml
agents:
root:
model: openai/gpt-5
description: Coordinator that delegates to local and catalog sub-agents
instruction: |
Delegate tasks to the most appropriate sub-agent.
sub_agents:
- local_helper
- agentcatalog/pirate # pulled from registry automatically
local_helper:
model: openai/gpt-5
description: A local helper agent for simple tasks
instruction: You are a helpful assistant.
```
External sub-agents are automatically named after their last path segment — for example, `agentcatalog/pirate` becomes `pirate`. You can also give them an explicit name using the `name:reference` syntax:
```yaml
sub_agents:
- my_pirate:agentcatalog/pirate # available as "my_pirate"
- reviewer:docker.io/myorg/review-agent:latest
```
### Pin external sub-agents to a digest
External references use a tag by default: `agentcatalog/pirate` is shorthand for `agentcatalog/pirate:latest`. Tag references are re-resolved against the registry on **every** `docker agent run`: each unpinned external sub-agent triggers a digest lookup at startup, even when that sub-agent is never invoked in the session. On a healthy connection this typically adds a second or two per reference (it depends on your network and registry), and it is one of the paths that can stall if the registry or credential helper misbehaves.
Pinning a reference to an immutable digest (`@sha256:…`) makes the runtime serve it straight from the local cache with no network round-trip, so startup stays fast and your team is fully reproducible:
```yaml
sub_agents:
- reviewer:docker.io/myorg/review-agent@sha256:44117e73263afa5c861bdf3730dae7925918ffdd146827eee5bcff20bc55e8fa
```
Copy the digest from your registry (Docker Hub shows it next to the tag) or read it with a standard OCI tool such as `docker buildx imagetools inspect <reference>`. Docker Agent logs a startup warning for any external OCI reference that still uses a tag instead of a digest.
External references in `handoffs` and `force_handoff` carry the same per-run cost, so pin those to a digest too.
> [!TIP]
> External sub-agents work with any OCI-compatible registry, not just the Docker Agent Catalog. See [Agent Distribution](../distribution/index.md) for more on registry references.
>
> See [`examples/sub-agents-from-catalog.yaml`](https://github.com/docker/docker-agent/blob/main/examples/sub-agents-from-catalog.yaml) for a complete example mixing local and catalog sub-agents.
## Harness-Backed Sub-Agents
Sub-agents can be backed by external coding CLIs — Claude Code, Codex, opencode, or pi — instead of a model API. Add a `harness:` block in place of a `model:` field to create a harness sub-agent:
```yaml
agents:
root:
model: anthropic/claude-sonnet-4-5
description: Orchestrator that plans and delegates
instruction: |
Break down coding tasks and delegate to the coding agents.
sub_agents:
- claude-coder
- codex-coder
claude-coder:
description: Claude Code specialist
harness:
type: claude-code
effort: high
codex-coder:
description: Codex specialist
harness:
type: codex
```
The orchestrator uses `transfer_task` to send work to a harness sub-agent just like any other sub-agent. Docker Agent handles the orchestration and hooks; the external CLI drives the coding loop.
> [!TIP]
> **Learn more**
>
> See [Coding Harnesses](../../features/harnesses/index.md) for the full field reference, parallel dispatch patterns, and what does not work inside harness agents.
## Example: Development Team
```yaml
agents:
root:
model: anthropic/claude-sonnet-4-5
description: Technical lead coordinating development
instruction: |
You are a technical lead managing a development team.
Analyze requests and delegate to the right specialist.
Ensure quality by reviewing results before responding.
sub_agents: [developer, reviewer, tester]
toolsets:
- type: think
developer:
model: anthropic/claude-sonnet-4-5
description: Expert software developer
instruction: |
You are an expert developer. Write clean, efficient code
and follow best practices.
toolsets:
- type: filesystem
- type: shell
- type: think
reviewer:
model: openai/gpt-5
description: Code review specialist
instruction: |
You review code for quality, security, and maintainability.
Provide actionable feedback.
toolsets:
- type: filesystem
tester:
model: openai/gpt-5
description: Quality assurance engineer
instruction: |
You write tests and ensure software quality. Run tests
and report results.
toolsets:
- type: shell
- type: todo
```
## Example: Research Team
```yaml
agents:
root:
model: anthropic/claude-sonnet-4-5
description: Research coordinator
instruction: |
Coordinate research tasks. Delegate web searches to
the researcher and writing to the writer.
sub_agents: [researcher, writer]
toolsets:
- type: think
researcher:
model: openai/gpt-5
description: Web researcher
instruction: Search the web and gather information.
toolsets:
- type: mcp
ref: docker:duckduckgo
- type: memory
path: ./research.db
writer:
model: anthropic/claude-sonnet-4-5
description: Content writer
instruction: Write clear, well-structured content.
toolsets:
- type: filesystem
```
## Multi-Model Teams
A key advantage of multi-agent systems is using different models for different roles — picking the best model for each job:
```yaml
models:
fast:
provider: openai
model: gpt-5-mini
temperature: 0.2 # precise
creative:
provider: openai
model: gpt-5
temperature: 0.8 # creative
local:
provider: dmr
model: ai/qwen3 # runs locally, no API cost
agents:
analyst:
model: fast # cheap and fast for analysis
writer:
model: creative # creative for content
helper:
model: local # free for simple tasks
```
## Shared Tools
Tools like `todo` can be shared between agents for collaborative task tracking:
```yaml
toolsets:
- type: todo
shared: true # all agents see the same todo list
```
## Best Practices
- **Keep agents focused** — Each agent should have a clear, narrow role
- **Write clear descriptions** — The coordinator uses descriptions to decide who to delegate to
- **Give minimal tools** — Only give each agent the tools it needs for its specific role
- **Use the think tool when needed** — For models without native reasoning, give coordinators the think tool so they reason about delegation. Models with built-in thinking (e.g., via `thinking_budget`) don't need it
- **Use the right model** — Use capable models for complex reasoning, cheap models for simple tasks
- **Choose the right pattern** — Use `sub_agents` for hierarchical task delegation, `handoffs` for pipeline workflows and conversational routing
> [!NOTE]
> **Beyond Docker Agent**
>
> For interoperability with other agent frameworks, Docker Agent supports the [A2A protocol](../../features/a2a/index.md) and can expose agents via [MCP Mode](../../features/mcp-mode/index.md).

View File

@@ -0,0 +1,70 @@
---
title: "Tools"
description: "Tools give agents the ability to interact with the world — read files, run commands, search the web, query databases, and more."
keywords: docker agent, ai agents, concepts, tools
weight: 30
---
_Tools give agents the ability to interact with the world — read files, run commands, search the web, query databases, and more._
## How Tools Work
When an agent needs to perform an action, it makes a **tool call**. The docker-agent runtime executes the tool and returns the result to the agent, which can then use it to continue its work.
1. Agent receives a user message
2. Agent decides it needs to use a tool (e.g., read a file)
3. docker-agent executes the tool and returns the result
4. Agent incorporates the result and responds
> [!NOTE]
> **Tool Confirmation**
>
> By default, docker-agent asks for user confirmation before executing tools that have side effects (shell commands, file writes). Use `--yolo` to auto-approve all tool calls.
## Built-in Tools
docker-agent ships with several built-in tools that require no external dependencies. Each is enabled by adding its `type` to the agent's `toolsets` list:
| Tool | Description |
| --- | --- |
| [Filesystem](../../tools/filesystem/index.md) | Read, write, list, search, and navigate files and directories |
| [Shell](../../tools/shell/index.md) | Execute synchronous and background shell commands |
| [Think](../../tools/think/index.md) | Step-by-step reasoning scratchpad for planning and decision-making |
| [Todo](../../tools/todo/index.md) | Task list management for complex multi-step workflows |
| [Tasks](../../tools/tasks/index.md) | Persistent task database shared across sessions |
| [Memory](../../tools/memory/index.md) | Persistent key-value storage backed by SQLite |
| [Fetch](../../tools/fetch/index.md) | Read content from HTTP/HTTPS URLs (GET only) |
| [Script](../../tools/script/index.md) | Define custom shell scripts as named tools |
| [LSP](../../tools/lsp/index.md) | Connect to Language Server Protocol servers for code intelligence |
| [API](../../tools/api/index.md) | Create custom tools that call HTTP APIs without writing code |
| [OpenAPI](../../tools/openapi/index.md) | Generate tools from an OpenAPI 3.x document |
| [RAG](../../tools/rag/index.md) | Retrieval-augmented generation over indexed sources |
| [Model Picker](../../tools/model-picker/index.md) | Let the agent pick between several models per turn |
| [User Prompt](../../tools/user-prompt/index.md) | Ask users questions and collect interactive input |
| [Open URL](../../tools/open-url/index.md) | Open a fixed URL in the user's default browser |
| [Transfer Task](../../tools/transfer-task/index.md) | Delegate tasks to sub-agents (auto-enabled with `sub_agents`) |
| [Background Agents](../../tools/background-agents/index.md) | Dispatch work to sub-agents concurrently |
| [Handoff](../../tools/handoff/index.md) | Hand the conversation off to another local agent in the same config (auto-enabled with `handoffs:`) |
| [A2A](../../tools/a2a/index.md) | Connect to remote agents via the Agent-to-Agent protocol |
| [MCP Catalog](../../tools/mcp-catalog/index.md) | Discover and activate remote MCP servers from the Docker MCP Catalog on demand |
## MCP Tools
docker-agent supports the [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) for extending agents with external tools. There are three ways to connect MCP tools:
- **Docker MCP** (recommended) — Run MCP servers in Docker containers via the [MCP Gateway](https://github.com/docker/mcp-gateway). Browse the [Docker MCP Catalog](https://hub.docker.com/search?q=&type=mcp).
- **Local MCP (stdio)** — Run MCP servers as local processes communicating over stdin/stdout.
- **Remote MCP (Streamable HTTP / SSE)** — Connect to MCP servers running on a network. See [Remote MCP Servers](../../features/remote-mcp/index.md).
```yaml
toolsets:
- type: mcp
ref: docker:duckduckgo
```
See [Tool Config](../../configuration/tools/index.md#mcp-tools) for full MCP configuration reference.
> [!TIP]
> **See also**
>
> For full configuration reference, see [Tool Config](../../configuration/tools/index.md).

View File

@@ -0,0 +1,5 @@
---
title: "Configuration"
description: "Complete reference for configuring agents in YAML or HCL."
weight: 30
---

View File

@@ -0,0 +1,552 @@
---
title: "Agent Configuration"
description: "Complete reference for defining agents in your YAML configuration."
keywords: docker agent, ai agents, configuration, yaml, agent configuration
linkTitle: "Agent Config"
weight: 30
---
_Complete reference for defining agents in your YAML configuration._
## Full Schema
<!-- yaml-lint:skip -->
```yaml
agents:
agent_name:
model: string # Required: model reference
description: string # Required: what this agent does
instruction: string # Required (unless instruction_file): system prompt
instruction_file: string | [list] # Optional: load the system prompt from one or more files relative to this config (mutually exclusive with instruction)
sub_agents: [list] # Optional: local or external sub-agent references
toolsets: [list] # Optional: tool configurations (use `type: rag` for RAG sources)
fallback: # Optional: fallback config
models: [list]
retries: 2
cooldown: 1m
add_date: boolean # Optional: add date to context
add_environment_info: boolean # Optional: add env info to context
add_prompt_files: [list] # Optional: include additional prompt files
add_description_parameter: bool # Optional: add description to tool schema
redact_secrets: boolean # Optional: scrub detected secrets out of tool args, outgoing chat messages, and tool output
code_mode_tools: boolean # Optional: enable code mode tool format
max_iterations: int # Optional: max tool-calling loops
max_consecutive_tool_calls: int # Optional: max identical consecutive tool calls
max_old_tool_call_tokens: int # Optional: token budget for old tool call content (disabled unless positive)
num_history_items: int # Optional: limit conversation history
use_toolsets: [list] # Optional: names of top-level toolsets to merge into this agent
readonly: boolean # Optional: restrict all toolsets to read-only tools only
skills: boolean | [list] # Optional: enable skill discovery (true/false or list of names and/or sources)
use_commands: [list] # Optional: names of top-level commands groups to merge into this agent
use_skills: [list] # Optional: names of top-level skills groups to merge into this agent
commands: # Optional: named prompts
name: "prompt text" # or {instruction: "prompt", agent: "sub_agent_name"} or {url: "https://..."} (TUI only)
welcome_message: string # Optional: message shown at session start
handoffs: [list] # Optional: agent names this agent can hand off to
force_handoff: string # Optional: agent that always receives the conversation when this agent stops
hooks: # Optional: lifecycle hooks
pre_tool_use: [list]
tool_response_transform: [list]
post_tool_use: [list]
session_start: [list]
session_end: [list]
on_user_input: [list]
stop: [list]
notification: [list]
structured_output: # Optional: constrain output format
name: string
schema: object
cache: # Optional: response cache (skip the model on repeat questions)
enabled: boolean
case_sensitive: boolean
trim_spaces: boolean
path: string
harness: # Optional: delegate to an external coding CLI (Claude Code, Codex, opencode, pi)
type: string # Required: claude-code | codex | opencode | pi
model: string # Optional: model override forwarded to the CLI
effort: string # claude-code only: low | medium | high | max
agent: string # opencode only: agent profile name
thinking: boolean # opencode only: enable extended thinking
```
> [!TIP]
> **See also**
>
> For model parameters, see [Model Config](../models/index.md). For tool details, see [Tool Config](../tools/index.md). For multi-agent patterns, see [Multi-Agent](../../concepts/multi-agent/index.md).
## Properties Reference
| Property | Type | Required | Description |
| --------------------------- | ------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `model` | string | ✓ | Model reference. Either inline (`openai/gpt-5`) or a named model from the `models` section. |
| `description` | string | ✓ | Brief description of the agent's purpose. Used by coordinators to decide delegation. |
| `instruction` | string | ✓ | System prompt that defines the agent's behavior, personality, and constraints. Required unless `instruction_file` is set. |
| `instruction_file` | string \| array | ✗ | Path(s) to a file or files (relative to the config file's directory) whose contents become the agent's instruction, loaded at startup. Accepts a single path or a list; multiple files are concatenated in order, separated by a blank line. Mutually exclusive with `instruction`. Each path must be a local relative path inside the config directory (absolute paths and `..` traversal are rejected). Only supported for local file-based configs, not OCI/URL sources. See [External Instruction Files](#external-instruction-files) below. |
| `sub_agents` | array | ✗ | List of agent names or external OCI references this agent can delegate to. Supports local agents, registry references (e.g., `agentcatalog/pirate`), and named references (`name:reference`). Automatically enables the `transfer_task` tool. Pin external OCI references to a digest (`name@sha256:…`) to skip the per-run registry lookup that tag references incur. See [External Sub-Agents](../../concepts/multi-agent/index.md#external-sub-agents-from-registries). |
| `toolsets` | array | ✗ | List of tool configurations. See [Tool Config](../tools/index.md). |
| `fallback` | object | ✗ | Automatic model failover configuration. |
| `add_date` | boolean | ✗ | When `true`, injects the current date into the agent's context. |
| `add_environment_info` | boolean | ✗ | When `true`, injects working directory, OS, CPU architecture, and git info into context. |
| `add_prompt_files` | array | ✗ | List of file paths whose contents are appended to the system prompt. Useful for including coding standards, guidelines, or additional context. |
| `add_description_parameter` | boolean | ✗ | When `true`, adds agent descriptions as a parameter in tool schemas. Helps with tool selection in multi-agent scenarios. |
| `redact_secrets` | boolean | ✗ | When `true`, scrubs detected secrets (API keys, tokens, private keys, etc.) out of tool-call arguments, outgoing chat messages, and tool output before they reach a tool, the model, or downstream consumers. See [Redacting Secrets](#redacting-secrets) below. |
| `code_mode_tools` | boolean | ✗ | When `true`, formats tool responses in a code-optimized format with structured output schemas. Useful for MCP gateway and programmatic access. |
| `max_iterations` | int | ✗ | Maximum number of tool-calling loops. Default: unlimited (0). Set this to prevent infinite loops. |
| `max_consecutive_tool_calls` | int | ✗ | Maximum consecutive identical tool calls before the agent is terminated, preventing degenerate loops. Default: `5`. |
| `max_old_tool_call_tokens` | int | ✗ | Maximum number of tokens to keep from old tool call arguments and results. Older tool calls beyond this budget have their content replaced with a placeholder, saving context space. Tokens are approximated as `len/4`. Truncation is disabled by default; set a positive value to enable it. Set to `-1` to disable truncation (unlimited). |
| `num_history_items` | int | ✗ | Limit the number of conversation history messages sent to the model. Useful for managing context window size with long conversations. Default: unlimited (all messages sent). |
| `skills` | bool/array | ✗ | Enable automatic skill discovery. `true` loads all discovered local skills, `false` disables them. A list can mix skill sources (`local` or `https://…` URLs) and skill names to include — see [Skills](../../features/skills/index.md). |
| `commands` | object | ✗ | Named prompts that can be run with `docker agent run config.yaml /command_name`. Can be simple strings or objects with `instruction` and/or `agent` fields for agent switching, or a `url` field to open a link in the browser (TUI only). See [Named Commands](#named-commands) below. |
| `use_commands` | list of string | ✗ | Names of top-level `commands` groups to merge into this agent. Inline `commands` entries take precedence on name conflicts. Default: `[]`. |
| `use_skills` | list of string | ✗ | Names of top-level `skills` groups to merge into this agent. Inline skills are deduplicated by name against merged entries. Default: `[]`. |
| `use_toolsets` | list of string | ✗ | Names of top-level `toolsets` groups to merge into this agent. See [Reusable Toolsets](../overview/index.md#reusable-toolsets-toolsets). Default: `[]`. |
| `readonly` | boolean | ✗ | When `true`, every toolset on this agent is filtered to expose only read-only tools (those annotated with a read-only hint). Mutating tools are removed at load time and cannot be called even if the model tries. See [Read-Only Agents](#read-only-agents) below. |
| `welcome_message` | string | ✗ | Message displayed to the user when a session starts. Rendered as Markdown in the TUI. **Not sent to the model** — it exists purely for the user's benefit. Useful for telling users what the agent can do and what commands are available. |
| `handoffs` | array | ✗ | List of agent names this agent can hand off the conversation to. Enables the `handoff` tool. See [Handoffs Routing](../../concepts/multi-agent/index.md#handoffs-routing). |
| `force_handoff` | string | ✗ | Name of an agent that unconditionally receives the conversation whenever this agent produces a final response. The runtime performs the switch itself, bypassing the LLM's tool-calling, guaranteeing deterministic pipelines. Must not reference the agent itself, and chains must not form a cycle. See [Forced Handoffs](../../concepts/multi-agent/index.md#forced-handoffs). |
| `hooks` | object | ✗ | Lifecycle hooks for running commands at various points. See [Hooks](../hooks/index.md). |
| `structured_output` | object | ✗ | Constrain agent output to match a JSON schema. See [Structured Output](../structured-output/index.md). |
| `cache` | object | ✗ | Response cache. When the same user question is asked again, the previous answer is replayed verbatim and the model is not called. See [Response Cache](#response-cache) below. |
| `harness` | object | ✗ | Run this agent through an external coding CLI instead of a model. **Note:** Any `toolsets:` defined on the same agent are silently ignored when `harness:` is set — the external CLI brings its own tools. See [Coding Harnesses](../../features/harnesses/index.md). |
> [!WARNING]
> **max_iterations**
>
> Default is `0` (unlimited). Always set `max_iterations` for agents with powerful tools like `shell` to prevent infinite loops. A value of 20–50 is typical for development agents.
## External Instruction Files
Long system prompts can be kept in their own files instead of being inlined in
the YAML, using `instruction_file`. This separates infrastructure configuration
(models, providers, tools) from behavioral content (the prompt), which keeps
version-control diffs focused, reduces merge conflicts on shared configs, and
lets instruction content be edited without risking YAML syntax errors.
```yaml
agents:
coordinator:
model: openai/gpt-5-mini
description: Routes work between specialist agents
instruction_file: instructions/coordinator.md
sub_agents:
- writer
writer:
model: openai/gpt-5-mini
description: Drafts and edits written content
instruction_file: instructions/writer.md
```
The path is resolved relative to the config file's directory and the file's
contents are loaded as the agent's instruction when the config is loaded. Notes:
- **Mutually exclusive** with `instruction`. Setting both is an error.
- Each path must be a **local relative path inside the config directory**.
Absolute paths and `..` traversal are rejected.
- A **list** of files is also accepted; their contents are concatenated in
order, separated by a blank line. This lets a shared preamble be reused
across agents while each agent appends its own specifics:
```yaml
agents:
writer:
model: openai/gpt-5-mini
description: Drafts and edits written content
instruction_file:
- instructions/shared-preamble.md
- instructions/writer.md
```
- Only supported for **local file-based configs**, not agents loaded from OCI
registries or URLs. When an agent is pushed with `docker agent share push`,
the file contents are inlined into the pushed artifact, so the published
agent stays self-contained.
A runnable example lives in [`examples/instruction_file.yaml`](https://github.com/docker/docker-agent/blob/main/examples/instruction_file.yaml).
## Response Cache
The response cache short-circuits the model when the same user question is asked again. The first time a question is asked, the agent calls the model normally and stores the assistant's reply. Subsequent identical questions skip the model entirely and replay the stored reply verbatim.
```yaml
agents:
root:
model: openai/gpt-5
description: Cached assistant
instruction: You are a helpful assistant.
cache:
enabled: true # required to turn the cache on
case_sensitive: false # default: false ("Hello" == "hello")
trim_spaces: true # default: false (" hello " == "hello")
path: ./cache.json # optional: persist to disk; omit for in-memory
```
| Property | Type | Default | Description |
| ---------------- | ------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `enabled` | boolean | `false` | Master switch. When `false` (or when the `cache` section is omitted), no caching is performed. |
| `case_sensitive` | boolean | `false` | When `true`, questions must match exactly (including case) to hit the cache. |
| `trim_spaces` | boolean | `false` | When `true`, leading and trailing whitespace is stripped from the question before it is compared. |
| `path` | string | _empty_ | When set, cache entries are persisted to a JSON file at the given path and reloaded on startup so the cache survives restarts. Relative paths resolve against the agent config directory. When empty, the cache lives in memory only. |
**How it works**
- The cache key is the latest user message in the session, normalized according to `case_sensitive` and `trim_spaces`.
- On a hit, the cached reply is added to the session as the assistant message and stop hooks fire normally — the rest of the agent (tools, sub-agents, the model) is bypassed.
- On a miss, the agent runs normally; the final assistant message produced by the first stop of the run is then stored under the question's key.
- Only the response to the original user question of a run is cached; follow-up turns inside the same `RunStream` are not.
**File-backed storage**
When `path` is set, every `Store` rewrites the entire cache file. Writes are **atomic**: the new content is written to a sibling temp file, `fsync`'d, and renamed over the destination, so a concurrent reader (or a process that crashes mid-write) will always see either the previous content or the new content in full — never a partially written file. The parent directory is also `fsync`'d after the rename so the rename itself is durable.
**Cross-process sharing**
Multiple processes can share the same `path:` cache file safely. Every `Store` takes an exclusive advisory lock on a sibling `<path>.lock` file (POSIX `flock(2)` on Unix, `LockFileEx` on Windows), reloads the current on-disk state under the lock, merges the new entry, and writes back atomically. Two processes that store *different* keys at the same time both see their writes preserved on disk; the lock window is short (one read + one fsync'd write).
`Lookup` watches the file's modification time and reloads the in-memory map when the file has advanced since its last load, so writes from a sibling process become visible without a restart. The `<path>.lock` sentinel file is created on first write and never deleted: removing it would let two processes lock different inodes and lose mutual exclusion.
## Redacting Secrets
The `redact_secrets` flag is a single agent-level switch that scrubs accidentally leaked credentials, tokens, and private keys out of an agent's I/O. It wires up three complementary defenses:
1. A `pre_tool_use` built-in hook that scrubs detected secrets from the **arguments of every tool call**, before the tool sees them.
2. A `before_llm_call` built-in hook that scrubs the same patterns from **outgoing chat messages** — message content, multi-part text content, prior reasoning content, and the JSON-encoded arguments of any tool call still in the conversation — before they reach the model provider.
3. A `tool_response_transform` built-in hook that scrubs **tool output at the source**, so the secret never reaches event consumers, the persisted session file, the `post_tool_use` hook input, or the next LLM call.
```yaml
agents:
root:
model: openai/gpt-5
description: A helpful assistant that scrubs secrets before they leak
instruction: |
You are a helpful assistant. If the user accidentally pastes a token,
do your best work without echoing the secret back.
redact_secrets: true
toolsets:
- type: shell
```
Detection uses the [portcullis](https://github.com/docker/portcullis) ruleset, which recognises common secret patterns including:
- GitHub Personal Access Tokens (`ghp_*`, `gho_*`, `ghu_*`, `ghs_*`, `ghr_*`, fine-grained `github_pat_*`)
- AWS access keys (`AKIA*`, `ASIA*`, …) and secret access keys
- GitLab PATs (`glpat-*`), Hugging Face tokens (`hf_*`)
- Stripe (`sk_live_*`, `pk_test_*`, …), Slack (`xoxb-*`, …), Shopify, Twilio, Discord, Atlassian, Mailchimp, SendGrid, and many more
- JWTs, GCP service-account JSON, Heroku keys, Docker Hub PATs (`dckr_pat_*`)
- PEM-encoded private keys (`-----BEGIN … PRIVATE KEY-----` blocks)
Each detected span is replaced with the literal string `[REDACTED]`; the surrounding text is preserved so a redacted argument still looks like a legitimate flag (e.g. `--token=[REDACTED]`). Redaction is idempotent — applying it twice yields the same result.
> [!NOTE]
> **False positives vs. false negatives**
>
> False positives are extremely rare: every rule pairs a regex with a discriminating keyword, so plain English never trips detection. **False negatives are possible** — only patterns the ruleset recognises are scrubbed, so this is a defense-in-depth feature, not a substitute for keeping secrets out of the conversation in the first place. Pair it with a proper [secret manager](../../guides/secrets/index.md) for the credentials your agent actually needs.
> [!NOTE]
> **Equivalent hook entry**
>
> Setting `redact_secrets: true` on the agent is shorthand for auto-registering all three legs of the feature as hook entries. They share the _same_ built-in name (`type: builtin`, `command: redact_secrets`) on `pre_tool_use`, `before_llm_call`, and `tool_response_transform` respectively — the implementation dispatches on the hook event. You can spell them out by hand to scope a leg to a subset of tools (set `matcher:` to a regex), stack them with other rewriters in a specific order, or enable just one or two legs. See [`examples/redact_secrets_hooks.yaml`](https://github.com/docker/docker-agent/blob/main/examples/redact_secrets_hooks.yaml) for a complete manual wiring and the [Hooks reference](../hooks/index.md#available-built-ins) for the builtin's event coverage.
## Welcome Message
Display a message when users start a session:
```yaml
agents:
assistant:
model: openai/gpt-5
description: Development assistant
instruction: You are a helpful coding assistant.
welcome_message: |
👋 Welcome! I'm your development assistant.
I can help you with:
- Writing and reviewing code
- Running tests and debugging
- Explaining concepts
What would you like to work on?
```
## Deferred Tool Loading
Toolsets support `defer` to load tools on-demand and speed up agent startup. See [Deferred Tool Loading](../tools/index.md#deferred-tool-loading) for details.
```yaml
agents:
root:
model: anthropic/claude-sonnet-4-5
description: Multi-purpose assistant
instruction: You have access to many tools.
toolsets:
- type: mcp
ref: docker:github-official
defer: true
- type: filesystem
```
## Fallback Configuration
Automatically switch to backup models when the primary fails:
| Property | Type | Default | Description |
| ---------- | ------ | ------- | ---------------------------------------------------------- |
| `models` | array | `[]` | Fallback models to try in order |
| `retries` | int | `2` | Retries per model for 5xx errors. `-1` to disable. |
| `cooldown` | string | `1m` | How long to stick with a fallback after a rate limit (429) |
**Error handling:**
- **Retryable** (same model with backoff): HTTP 5xx, 408, network timeouts
- **Non-retryable** (skip to next model): HTTP 429, 4xx client errors
```yaml
agents:
root:
model: anthropic/claude-sonnet-4-5
fallback:
models:
- openai/gpt-5
- google/gemini-3.5-flash
retries: 2
cooldown: 1m
```
## Named Commands
Define reusable prompt shortcuts that can send prompts to the current agent, switch to a different sub-agent, or open a URL in the browser:
> **Note:** Named slash commands execute immediately, even while the agent is processing another message. Unlike regular chat messages (which are queued), slash commands interrupt or direct the agent even while it is mid-response.
```yaml
agents:
root:
model: openai/gpt-5
instruction: You are a system administrator.
commands:
df: "Check how much free space I have on my disk"
logs: "Show me the last 50 lines of system logs"
greet: "Say hello to ${env.USER}"
deploy: "Deploy ${env.PROJECT_NAME || 'app'} to ${env.ENV || 'staging'}"
# Advanced format with agent switching
plan:
agent: planner # Switch to the 'planner' sub-agent
instruction: "Create a detailed plan for: $1" # Optional: send this prompt after switching
# Agent switching without instruction - forwards remaining text as prompt
review:
agent: reviewer # Any text after /review is sent to the reviewer agent
# URL command - opens a link in the browser instead of messaging the agent
docs:
description: "Open the documentation"
url: https://docs.docker.com/
```
### Command Formats
Commands support three formats:
1. **Simple string format**: The string becomes the instruction sent to the current agent
```yaml
df: "Check disk space"
```
2. **Advanced object format**: Supports agent switching and optional instructions
```yaml
plan:
agent: planner # Required: name of sub-agent to switch to
instruction: "Plan: $1" # Optional: prompt to send after switching
description: "Switch to planning mode" # Optional: shown in help text
```
3. **URL format**: Opens a link in the browser instead of messaging the agent
```yaml
docs:
url: https://docs.docker.com/ # Required: URL to open
description: "Open the documentation" # Optional: shown in help text
```
When `agent` is set without `instruction`, any text typed after the slash command (e.g., `/plan build a web app`) is forwarded as a prompt to the target agent. The target agent must be listed in the current agent's `sub_agents` array.
### Agent-Switching Commands
Commands with an `agent` field switch the active agent for that command's scope. This is useful for building workflow shortcuts where `/plan`, `/review`, `/deploy` each route the user to the appropriate specialist.
```yaml
agents:
root:
model: openai/gpt-5
description: Main assistant
instruction: You are a project coordinator.
sub_agents: [planner, reviewer]
commands:
# Switch to planner with a pre-filled prompt
plan:
agent: planner
instruction: "Create a detailed plan for: $1"
# Switch to reviewer; any text after /review is forwarded
review:
agent: reviewer
# Simple prompt command (no switching)
status: "Summarize what we have accomplished so far"
planner:
model: openai/gpt-5
description: Planning specialist
instruction: You create detailed project plans.
reviewer:
model: anthropic/claude-sonnet-4-5
description: Code review specialist
instruction: You review code and suggest improvements.
```
**Agent-switching vs. `handoff`**
| | Agent-switching command | `handoff` tool |
| --- | --- | --- |
| **Trigger** | User runs `/command` | Model calls `handoff()` |
| **Session** | Stays in the same session | Stays in the same session |
| **History** | Target agent sees full conversation | Target agent sees full conversation |
| **Return** | User must explicitly switch back | Target agent can chain to another agent |
**Agent-switching vs. `transfer_task`**
`transfer_task` launches a **sub-session**: the root agent sends a task, the child runs in isolation, and the result is returned to the root. The root agent stays in control and the child's work is never in the main conversation. Use `transfer_task` (via `sub_agents`) when you want delegation with a clean result; use agent-switching commands when you want to *become* a different agent for the rest of the conversation.
See [`examples/agent_switching_commands.yaml`](https://github.com/docker/docker-agent/blob/main/examples/agent_switching_commands.yaml) for a complete example.
```bash
# Run commands from the CLI
$ docker agent run agent.yaml /df
$ docker agent run agent.yaml /greet
$ PROJECT_NAME=myapp ENV=production docker agent run agent.yaml /deploy
```
Commands use JavaScript template literal syntax (`${env.VAR}`) for environment variable interpolation. Undefined variables expand to empty strings.
The same syntax is also expanded in agent and toolset instructions: `agents.<name>.instruction` and `toolsets[*].instruction` support `${env.X}` placeholders (with optional `||` defaults and ternary expressions). `agents.<name>.description` and `agents.<name>.welcome_message` also support it.
Note that path-like fields (`working_dir`, `path`) primarily use a shell-style syntax (`$VAR`, `${VAR}`, `~`), and also accept `${env.X}` as an alias (though not richer JS expressions). See [Variable Expansion in Config Fields](../overview/index.md#variable-expansion-in-config-fields) for the full table.
### URL Commands
A command with a `url` field opens that URL in the user's default browser instead of sending a prompt to the agent. Any URI scheme the OS knows how to dispatch works — both standard web URLs and custom schemes such as `docker-desktop://` for deep links. URL commands are TUI-only — they have no effect when run from the CLI.
```yaml
agents:
root:
model: openai/gpt-5
description: An agent with handy URL shortcuts.
instruction: You are a helpful assistant.
commands:
feedback:
description: "Open the feedback site for this session"
url: https://example.com/feedback?session={{session_id}}
docs:
description: "Open the documentation"
url: https://docs.docker.com/
desktop:
description: "Open this session in Docker Desktop"
url: docker-desktop://dashboard/session/{{session_id}}
```
The `{{session_id}}` token is replaced at invocation time with the current session ID (URL-query-escaped so it can't break the URL or inject extra query parameters), letting a command deep-link to something scoped to the conversation. This token deliberately uses `{{...}}` rather than the `${...}` JS-expansion syntax, since the session ID is only known at dispatch time.
URLs are validated before being handed to the OS opener: a parseable URL with a non-empty scheme is required, and flag-like inputs (those starting with `-`) are rejected to prevent argument injection.
See [`examples/url_commands.yaml`](https://github.com/docker/docker-agent/blob/main/examples/url_commands.yaml) for a complete example.
## Read-Only Agents
Set `readonly: true` on an agent to restrict all of its toolsets to tools that are annotated as read-only. Mutating tools are filtered out at load time — the agent cannot list or call them, even if the model hallucinates a call.
You can also set `readonly: true` on an individual toolset to restrict only that toolset while leaving others unrestricted.
```yaml
agents:
# Agent-level readonly: every toolset is restricted to read-only tools.
inspector:
model: anthropic/claude-sonnet-4-5
description: Read-only inspector that can explore but never modify.
instruction: Explore the project. Do not make changes.
readonly: true
toolsets:
- type: filesystem
- type: shell
# Toolset-level readonly: only the filesystem toolset is restricted;
# the shell toolset keeps all of its tools.
mixed:
model: anthropic/claude-sonnet-4-5
description: Read-only file access, full shell access.
instruction: You can read files and run any shell command.
toolsets:
- type: filesystem
readonly: true
- type: shell
```
See [`examples/readonly.yaml`](https://github.com/docker/docker-agent/blob/main/examples/readonly.yaml) for a complete example.
> [!NOTE]
> **Which tools are read-only?**
>
> Whether a tool is read-only is determined by its `ReadOnlyHint` annotation. For built-in tools, read-only operations (list/read/search) carry the hint; mutating operations (write/delete/execute) do not. Custom and MCP tools expose the hint via their own annotations.
## Complete Example
```yaml
models:
claude:
provider: anthropic
model: claude-sonnet-4-5
max_tokens: 64000
agents:
root:
model: claude
description: Technical lead coordinating development
instruction: |
You are a technical lead. Analyze requests and delegate
to the right specialist. Always review work before responding.
welcome_message: "👋 I'm your tech lead. How can I help today?"
sub_agents: [developer, researcher]
add_date: true
add_environment_info: true
fallback:
models: [openai/gpt-5]
toolsets:
- type: think
commands:
review: "Review all recent code changes for issues"
hooks:
session_start:
- type: command
command: "./scripts/setup.sh"
developer:
model: claude
description: Expert software developer
instruction: Write clean, tested, production-ready code.
max_iterations: 30
toolsets:
- type: filesystem
- type: shell
- type: think
- type: todo
researcher:
model: openai/gpt-5
description: Web researcher with memory
instruction: Search for information and remember findings.
toolsets:
- type: mcp
ref: docker:duckduckgo
- type: memory
path: ./research.db
```

View File

@@ -0,0 +1,244 @@
---
title: "HCL Configuration"
description: "Write docker-agent configs in HCL instead of YAML, using labeled blocks, heredocs, and the same underlying schema."
keywords: docker agent, ai agents, configuration, yaml, hcl configuration
weight: 20
---
_Write docker-agent configs in HCL instead of YAML. It maps to the same docker-agent schema and validation rules._
`docker-agent` supports `.hcl` config files anywhere it supports `.yaml` or `.yml` files. HCL is useful if you prefer labeled blocks, less punctuation, and heredocs for long prompts.
> [!TIP]
> **Same config model, different syntax**
>
> YAML and HCL are just two syntaxes for the same docker-agent configuration model. docker-agent converts HCL to the equivalent YAML structure internally, then runs the normal schema validation and loading pipeline.
## Minimal Example
```hcl
#!/usr/bin/env docker agent run
agent "root" {
model = "openai/gpt-5"
description = "A helpful assistant"
instruction = <<-EOT
You are a helpful assistant.
EOT
toolset "think" {}
}
```
Run it exactly like a YAML config:
```bash
$ docker agent run agent.hcl
$ docker agent run --exec agent.hcl "Summarize this repository"
$ docker agent serve api ./agents/ # directories may mix .yaml, .yml, and .hcl files
```
> [!TIP]
> **See also**
>
> HCL changes the syntax, not the meaning of fields. For what each field does, see [Agent Config](../agents/index.md), [Model Config](../models/index.md), and [Tool Config](../tools/index.md).
## YAML vs HCL
These two configs are equivalent:
```yaml
models:
claude:
provider: anthropic
model: claude-sonnet-4-5
agents:
root:
model: claude
description: Coding assistant
instruction: You help with software development.
toolsets:
- type: filesystem
- type: shell
```
```hcl
model "claude" {
provider = "anthropic"
model = "claude-sonnet-4-5"
}
agent "root" {
model = "claude"
description = "Coding assistant"
instruction = "You help with software development."
toolset "filesystem" {}
toolset "shell" {}
}
```
## Core Conventions
HCL follows a few simple mapping rules:
| HCL syntax | YAML shape |
| --- | --- |
| `agent "root" { ... }` | `agents.root` |
| `model "claude" { ... }` | `models.claude` |
| `provider "team" { ... }` | `providers.team` |
| `mcp "github" { ... }` | `mcps.github` |
| `rag "docs" { ... }` | `rag.docs` |
| `command "fix" { ... }` inside an agent | `commands.fix` |
| `toolset "shell" {}` | list item in `toolsets` with `type: shell` |
| `metadata { ... }`, `permissions { ... }` | singleton blocks with the same top-level name |
### Top-level keyed maps become labeled blocks
In YAML, several sections are maps keyed by name. In HCL, those become labeled blocks:
```hcl
model "claude" {
provider = "anthropic"
model = "claude-sonnet-4-5"
}
agent "root" {
model = "claude"
description = "Primary assistant"
instruction = "You are helpful."
}
```
The supported top-level labeled blocks are:
- `agent`
- `model`
- `provider`
- `mcp`
- `rag`
The supported top-level singleton blocks are:
- `metadata`
- `permissions`
### Toolsets use the block label as `type`
Instead of writing list entries with `type: ...`, HCL uses a `toolset` block whose label becomes the tool type:
```hcl
agent "root" {
model = "openai/gpt-5"
description = "Dev assistant"
instruction = "You can inspect and modify code."
toolset "filesystem" {}
toolset "mcp" {
ref = "docker:github-official"
}
}
```
### Commands use labeled blocks too
Agent commands are often nicer to write in HCL because each command gets its own block:
```hcl
agent "root" {
model = "openai/gpt-5"
description = "Build helper"
instruction = "You help with builds."
command "fix-lint" {
description = "Fix lint issues"
instruction = "Run the linter, then fix any problems."
}
}
```
## Strings and Heredocs
Use quoted strings for short values and heredocs for long prompts, welcome messages, or embedded JSON.
```hcl
agent "root" {
model = "openai/gpt-5"
description = "Friendly assistant"
instruction = <<-EOT
You are a helpful assistant.
Keep answers concise and practical.
EOT
}
```
### Escaping literal `${...}`
HCL treats `${...}` inside strings and heredocs as template interpolation. If you need the literal text `${...}` in your prompt, escape it as `$${...}`.
This matters for command prompts that intentionally show docker-agent template snippets:
```hcl
command "fix-lint" {
instruction = <<-EOT
Run the linter and inspect the result:
$${shell({cmd: "task lint"})}
EOT
}
```
The model will receive the literal `${shell({cmd: "task lint"})}` text.
## Repeated Blocks Become Lists
Some YAML sections are lists. In HCL, those are written as repeated blocks.
For example, model routing rules become repeated `routing { ... }` blocks:
```hcl
model "smart_router" {
provider = "openai"
model = "gpt-5"
routing {
model = "anthropic/claude-sonnet-4-5"
examples = [
"Write a detailed technical document",
"Review this code for security issues",
]
}
routing {
model = "openai/gpt-5"
examples = [
"Generate some creative ideas",
"Help me brainstorm",
]
}
}
```
The same idea applies to other list-shaped sections such as RAG `strategy` blocks and hook event entries.
## Important Differences from Terraform
docker-agent uses HCL as a configuration syntax, not as Terraform:
- There are no modules, `locals`, or `variable` blocks.
- There are no docker-agent-specific HCL functions to call from expressions.
- Prefer normal literal values: strings, numbers, booleans, lists, objects, and nested blocks.
- After conversion, the result is validated exactly like the equivalent YAML config.
If you already know Terraform, think of docker-agent HCL as a thin block-based syntax over the existing config schema.
## Examples
See these real configs in the repository:
- [`examples/pirate.hcl`](https://github.com/docker/docker-agent/blob/main/examples/pirate.hcl)
- [`examples/gopher.hcl`](https://github.com/docker/docker-agent/blob/main/examples/gopher.hcl)

View File

@@ -0,0 +1,882 @@
---
title: "Hooks"
description: "Run shell commands at various points during agent execution for deterministic control over behavior."
keywords: docker agent, ai agents, configuration, yaml, hooks
weight: 60
---
_Run shell commands at various points during agent execution for deterministic control over behavior._
## Overview
Hooks allow you to execute shell commands or scripts at key points in an agent's lifecycle. They provide deterministic control that works alongside the LLM's behavior, enabling validation, logging, environment setup, and more.
> [!NOTE]
> **Use Cases**
>
> - Validate or transform tool inputs before execution
> - Log all tool calls to an audit file
> - Block dangerous operations based on custom rules
> - Validate, redact, or enrich user prompts before they reach the model
> - Programmatically approve or deny tool calls without prompting the user
> - Steer or veto context-window compaction
> - Audit sub-agent handoffs in multi-agent setups
> - Set up the environment when a session starts
> - Clean up resources when a session ends
> - Log or validate model responses before returning to the user
> - Send external notifications on agent errors or warnings
## Hook Types
docker-agent dispatches the following hook events:
| Event | When it fires | Can block? |
| --------------------------- | --------------------------------------------------------------------------------- | ---------- |
| `pre_tool_use` | Before a tool call executes | Yes |
| `tool_response_transform` | Between a tool's execution and the runtime's emission/record of the response | No |
| `post_tool_use` | After a tool completes — fires for both success and failure | Yes |
| `permission_request` | Just before the runtime would prompt the user to approve a tool | Yes |
| `session_start` | When a session begins or resumes | No |
| `user_prompt_submit` | Once per user message, after submission and before the model runs | Yes |
| `user_steering_messages_submit` | Each time queued steering messages are drained (mid-turn, after stop, or while idle) | Yes |
| `user_followup_submit` | Each time a queued follow-up message starts a fresh turn (end-of-turn) | Yes |
| `turn_start` | At the start of every agent turn (each model call) | No |
| `turn_end` | At the end of every agent turn — fires no matter why the turn ended | No |
| `before_llm_call` | Just before every model call (after `turn_start`) | Yes |
| `after_llm_call` | After every successful model call, before the response is recorded | No |
| `session_end` | When a session terminates | No |
| `pre_compact` | Just before the runtime compacts the session transcript | Yes |
| `before_compaction` | Just before a compaction runs — can veto or supply a custom summary | Yes |
| `after_compaction` | After a successful compaction (summary applied to the session) | No |
| `subagent_stop` | When a sub-agent (transferred task / background / skill sub-session) finishes | No |
| `on_user_input` | When the agent is waiting for user input | No |
| `stop` | When the model finishes responding | No |
| `notification` | When the agent emits a notification (error or warning) | No |
| `on_error` | When the runtime hits an error during a turn (fires alongside `notification`) | No |
| `on_max_iterations` | When the runtime reaches its configured `max_iterations` limit | No |
| `on_agent_switch` | When the runtime moves the active agent (transfer_task, handoff, return) | No |
| `on_session_resume` | When the user explicitly approves continuation past `max_iterations` | No |
| `on_tool_approval_decision` | After the runtime's approval chain (yolo / permissions / readonly / ask) resolves | No |
| `worktree_create` | After `docker agent run --worktree` creates a git worktree, before the session | Yes |
> [!NOTE]
> **Two compaction events**
>
> `pre_compact` and `before_compaction` both fire just before a compaction. `pre_compact` is the original event and is best-suited to _steering_ the LLM-generated summary by appending guidance via `additional_context`. `before_compaction` is the newer, structured event: it carries the input/output token counts, the model's context limit, and a `compaction_reason` so handlers can decide based on real session pressure, and it can _replace_ the LLM-generated summary verbatim via `hook_specific_output.summary`.
## Configuration
```yaml
agents:
root:
model: openai/gpt-4o
description: An agent with hooks
instruction: You are a helpful assistant.
hooks:
# Run before specific tools
pre_tool_use:
- matcher: "shell|edit_file"
hooks:
- type: command
command: "./scripts/validate-command.sh"
timeout: 30
# Run after all tool calls
post_tool_use:
- matcher: "*"
hooks:
- type: command
command: "./scripts/log-tool-call.sh"
# Run when session starts
session_start:
- type: command
command: "./scripts/setup-env.sh"
# Run when session ends
session_end:
- type: command
command: "./scripts/cleanup.sh"
# Run when agent is waiting for user input
on_user_input:
- type: command
command: "./scripts/notify.sh"
# Run when the model finishes responding
stop:
- type: command
command: "./scripts/log-response.sh"
# Run on agent errors and warnings
notification:
- type: command
command: "./scripts/alert.sh"
```
## Built-in Hooks
In addition to shell `command` hooks, docker-agent ships a small library of **built-in hooks** — in-process Go functions that run without spawning a subprocess. They're invoked with `type: builtin`, where `command` is the builtin's registered name and `args` are passed through as the builtin's parameters.
```yaml
hooks:
turn_start:
- type: builtin
command: add_date
- type: builtin
command: add_prompt_files
args:
- GUIDELINES.md
- PROJECT.md
session_start:
- type: builtin
command: add_environment_info
before_llm_call:
- type: builtin
command: max_iterations
args: ["50"]
```
Built-ins are typically zero-config and faster than equivalent shell hooks because they don't fork a process. They cover the common "inject context into every turn / session" patterns out of the box.
### Available built-ins
| Builtin | Event | Args | What it does |
| ----------------------- | ----------------------------------------------------------------------------------------- | --------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `add_date` | `turn_start` | _none_ | Prepends `Today's date: YYYY-MM-DD` so the model always knows the current date. |
| `add_environment_info` | `session_start` | _none_ | Adds the working directory, git-repo status, OS, and CPU architecture. |
| `add_prompt_files` | `turn_start` | `[file1, file2, ...]` | Reads each named file from the workdir hierarchy (walking up) and the home directory, and appends their contents. |
| `add_git_status` | `turn_start` | _none_ | Adds the output of `git status --short --branch` (no-op outside a git repo or when git isn't installed). |
| `add_git_diff` | `turn_start` | _none_, or `["full"]` | Adds `git diff --stat` by default. Pass `args: ["full"]` to emit the full unified diff. Output is capped to 4 KB. |
| `add_directory_listing` | `session_start` | _none_ | Adds an alphabetical listing of the cwd's top-level entries (skips dot-files, capped at 100 with a "... and N more"). |
| `add_user_info` | `session_start` | _none_ | Adds the current OS user (username and full name) and the hostname. |
| `add_recent_commits` | `session_start` | _none_, or `["<N>"]` | Adds `git log --oneline -n N`. `N` defaults to 10; pass a positive integer to override. |
| `max_iterations` | `before_llm_call` | `["<N>"]` (required) | Hard-stops the agent after `N` model calls. Stateless: the runtime supplies the iteration counter on every dispatch. |
| `snapshot` | `session_start`, `turn_start`, `turn_end`, `pre_tool_use`, `post_tool_use`, `session_end` | _none_ | Records filesystem snapshots in a shadow git repo under the docker-agent data directory. No-op outside git repos; respects the source repo's ignore rules and skips newly-added files larger than 2 MiB. |
| `redact_secrets` | `pre_tool_use`, `before_llm_call`, `tool_response_transform` | _none_ | Scrubs detected secrets (API keys, tokens, private keys, …) out of tool call arguments, outgoing chat content, and tool output. The same builtin handles all three events and dispatches on the event name. Auto-registered on all three events by `redact_secrets: true` on the agent — see [`examples/redact_secrets_hooks.yaml`](https://github.com/docker/docker-agent/blob/main/examples/redact_secrets_hooks.yaml) for the manual wiring. |
| `limit_large_tool_results` | `tool_response_transform`, `session_end` | _none_ | **Always-on safety hook** — automatically injected by the runtime, no configuration required. When a tool result from the `filesystem`, `shell`, `mcp`, or `a2a` categories exceeds 2,000 lines or 50 KiB, the full payload is written to a per-session temp file and replaced in the conversation with a notice plus a bounded tail (last 2,000 lines, up to 50 KiB). The `session_end` leg deletes the temp directory. Internal toolsets (`memory`, `plan`, `tasks`, `think`, …) are not affected. |
| `safer_shell` | `pre_tool_use` (with `preempt_yolo: true`) | _none_ | Classifies shell commands against an embedded taxonomy. Destructive matches (rm -rf, docker volume rm, mkfs, …) get an Ask verdict with `blast_radius` / `category` metadata; known-safe reads (ls, git status, docker ps, …) flow through silently; everything else asks with `blast_radius=unknown`. Filters by tool name internally (no-op for non-shell calls). Registered with `preempt_yolo: true` so the entry fires before `Decide()` / `--yolo`. Auto-registered by `safer: true` on a shell toolset — see [`examples/shell_safer.yaml`](https://github.com/docker/docker-agent/blob/main/examples/shell_safer.yaml). |
| `unload` | `on_agent_switch` | _none_ | POSTs `{"model": "<id>"}` to each of the previous agent's DMR model endpoints (`/_unload` by default, overridable per-model via `unload_api`) to free the GPU/RAM the just-departing model was holding. Pure HTTP — reads the model snapshot the runtime ships on `on_agent_switch` and depends on no provider-specific runtime state. Non-DMR providers (OpenAI, Anthropic, …) are silently skipped, so cross-provider chains are safe. Errors are logged and swallowed; agent switching never blocks on a slow or unreachable engine (each call has a 10 s timeout). See [`examples/unload_on_switch.yaml`](https://github.com/docker/docker-agent/blob/main/examples/unload_on_switch.yaml). |
> [!NOTE]
> **Per-turn vs. per-session**
>
> `turn_start` built-ins recompute every turn and contribute **transient** context that is _not_ persisted to the session — perfect for fast-moving signals like the date or current git state. `session_start` built-ins run once per session and their context **persists** across turns and resumes — pick this for stable context like the OS user or the initial directory listing.
> [!NOTE]
> **Auto-injected built-ins**
>
> The agent flags `add_date: true`, `add_environment_info: true`, `add_prompt_files: [...]`, and `redact_secrets: true` are shorthands that auto-register the matching built-in hook. You don't need to repeat them under `hooks:` — set the flag _or_ the hook entry(ies), not both. `redact_secrets: true` auto-registers the same builtin on all three of `pre_tool_use`, `before_llm_call`, and `tool_response_transform`; you can also wire any subset of them by hand for finer-grained control (per-tool matchers, ordering with other rewriters, …).
>
> `limit_large_tool_results` is injected unconditionally by the runtime — it is always active and cannot be removed from config.
A minimal snapshot wiring looks like this:
```yaml
hooks:
turn_start:
- type: builtin
command: snapshot
turn_end:
- type: builtin
command: snapshot
session_end:
- type: builtin
command: snapshot
```
The shadow repository stores tree objects only; it never writes commits or touches the source repository's `.git` directory. The source repository's `.gitignore` and `info/exclude` rules are mirrored before each capture so ignored files do not appear in snapshots. The built-in only records undo checkpoints when files changed, so a final no-op model response does not hide the last changed snapshot.
You can also enable snapshots globally for every agent with user config:
```yaml
settings:
snapshot: true
```
Omit `snapshot` or set it to `false` to leave automatic snapshots off; manually configured snapshot hooks still run.
See [`examples/snapshot_hooks.yaml`](https://github.com/docker/docker-agent/blob/main/examples/snapshot_hooks.yaml) for a complete snapshot hook configuration. For an overview of the snapshot feature and the `/undo` / `/snapshots` commands, see [Snapshots](../../features/snapshots/index.md).
> [!WARNING]
> **Two flavors of `max_iterations`**
>
> The `max_iterations` agent field has its own UX (it pauses and asks the user to resume past the limit). The `max_iterations` built-in hook is a **hard stop with no resume** — when its counter trips, the agent terminates with a block decision. Use the agent field for interactive sessions and the built-in hook to enforce non-negotiable caps in unattended runs.
## Matcher Patterns
The `matcher` field uses regex patterns to match tool names:
| Pattern | Matches |
| ------------------ | ----------------------------- |
| `*` | All tools |
| `shell` | Only the `shell` tool |
| `shell\|edit_file` | Either `shell` or `edit_file` |
| `mcp:.*` | All MCP tools (regex) |
## Hook Input
Hooks receive JSON input via stdin with context about the event:
```json
{
"session_id": "abc123",
"cwd": "/path/to/project",
"hook_event_name": "pre_tool_use",
"tool_name": "shell",
"tool_use_id": "call_xyz",
"tool_input": {
"cmd": "rm -rf /tmp/cache",
"cwd": "."
}
}
```
### Common Fields
Every hook event carries:
| Field | Description |
| ----------------- | ------------------------------------- |
| `session_id` | The current session's ID. |
| `cwd` | The runtime's working directory. |
| `hook_event_name` | The event name (e.g. `pre_tool_use`). |
### Per-Event Extra Fields
In addition to the common fields, each event ships its own payload:
| Event | Extra fields |
| --------------------------- | --------------------------------------------------------------------------------------------------------------------- |
| `pre_tool_use` | `agent_name`, `tool_name`, `tool_use_id`, `tool_input` |
| `tool_response_transform` | `tool_name`, `tool_use_id`, `tool_input`, `tool_response` |
| `post_tool_use` | `agent_name`, `tool_name`, `tool_use_id`, `tool_input`, `tool_response`, `tool_error` |
| `permission_request` | `agent_name`, `tool_name`, `tool_use_id`, `tool_input` |
| `session_start` | `source` — one of `startup`, `resume`, `clear`, `compact` |
| `user_prompt_submit` | `prompt` — the text the user just submitted |
| `user_steering_messages_submit` | `steering_messages` — the drained steering messages, in submission order |
| `user_followup_submit` | `prompt` — the text of the dequeued follow-up message |
| `turn_start` | _none_ (just the common fields) |
| `turn_end` | `agent_name`, `reason` — one of `normal`, `continue`, `steered`, `error`, `canceled`, `hook_blocked`, `loop_detected` |
| `before_llm_call` | `iteration` — 1-based run-loop iteration counter (the model call this hook is gating), `model_id` |
| `after_llm_call` | `agent_name`, `stop_response`, `last_user_message`, `model_id`, `usage`, `cost` |
| `session_end` | `reason` — one of `clear`, `logout`, `prompt_input_exit`, `other` |
| `pre_compact` | `source` — one of `manual`, `auto`, `overflow`, `tool_overflow` |
| `before_compaction` | `input_tokens`, `output_tokens`, `context_limit`, `compaction_reason` (one of `threshold`/`overflow`/`manual`) |
| `after_compaction` | `input_tokens`, `output_tokens`, `context_limit`, `compaction_reason`, `summary` |
| `subagent_stop` | `agent_name` (the sub-agent), `parent_session_id`, `stop_response` |
| `on_user_input` | _none_ |
| `stop` | `agent_name`, `stop_response`, `last_user_message` |
| `notification` | `notification_level` (`error` or `warning`), `notification_message` |
| `on_error` | `notification_level` (always `error`), `notification_message` |
| `on_max_iterations` | `notification_level` (always `warning`), `notification_message` |
| `on_agent_switch` | `from_agent`, `to_agent`, `agent_switch_kind` (`transfer_task`, `transfer_task_return`, `handoff`, or `force_handoff`) |
| `on_session_resume` | `previous_max_iterations`, `new_max_iterations` |
| `on_tool_approval_decision` | `tool_name`, `tool_use_id`, `tool_input`, `approval_decision`, `approval_source` |
| `worktree_create` | `worktree_path`, `worktree_branch`, `worktree_source_dir` (`cwd` is also set to the new worktree) |
Notes:
- `tool_response` for `post_tool_use` carries the tool's result; `tool_error` is `true` when the tool failed (the failure detail is surfaced inside `tool_response`).
- `agent_name` on `pre_tool_use`, `post_tool_use`, and `permission_request` identifies the agent that issued the tool call — in multi-agent setups this follows the active sub-agent, not always the root agent.
- `prompt` is only populated for `user_prompt_submit`. Sub-sessions (transferred tasks, background agents, skills) do **not** fire this event because their kick-off message is synthesised by the runtime, not authored by the user.
- `steering_messages` is only populated for `user_steering_messages_submit`. It carries the user messages the runtime just drained from the steering queue — messages submitted while the agent was already working (mid-turn, after the model stopped, or while idle before the first model call).
- `prompt` is also populated for `user_followup_submit`, carrying the text of the dequeued follow-up message (a user message queued for end-of-turn processing via the FollowUp API / queue, as opposed to mid-turn steering).
- `stop_response` carries the model's final assistant text for `stop`, `after_llm_call`, and `subagent_stop`. `last_user_message` carries the latest user message at dispatch time.
- `model_id` is populated for `after_llm_call` (and `before_llm_call`) in the canonical `<provider>/<model>` form (e.g. `anthropic/claude-sonnet-4-5`). For harness agents, `model_id` is the harness label (e.g. `claude-code`) rather than a canonical model name — see [Coding Harnesses](../../features/harnesses/index.md).
- `usage` and `cost` are populated for `after_llm_call` only. `usage` is the per-call token usage object (`input_tokens`, `output_tokens`, `cached_input_tokens`, `cached_write_tokens`, and `reasoning_tokens` — the last is itself omitted for non-reasoning models); the whole object is absent when the provider reported no usage. `cost` is the USD price of that one model response. For a **native model call** it is the price computed from `usage` and the model's pricing table, and equals the cost the session records for the turn: it is **absent** when the response is unpriced (no pricing data on file, or no usage) and an explicit `0` for a priced call that was free — so a present `cost` is authoritative and an absent one means "unpriced", with no need to cross-check `usage`. (For harness agents the meaning differs — see the next note.) A cost ledger can therefore record per-call spend from the payload alone, without subscribing to the runtime event channel.
- For [harness agents](../../features/harnesses/index.md), `cost` is the harness's own reported total for the call rather than a computed price, and is present only when the harness reported a non-zero cost (some harnesses, e.g. `codex`, report token counts but no cost — those turns carry `usage` with `cost` absent, even though the recorded message stores `0`).
- `after_llm_call` fires for **every** model call, including calls made inside sub-sessions (transferred tasks, background agents, skills). For those, `session_id` is the sub-session's id. Summing `cost` across `after_llm_call` events therefore captures **all** spend, including sub-sessions (and even sub-sessions that error before their cost is persisted). Do **not** add a separately-queried session cost total on top: the runtime's own total already recurses into and includes completed sub-session spend, so combining the two double-counts. Pick one source — the summed hook costs — as the authoritative ledger.
- `context_limit` is `0` when the model definition is unavailable (treat `0` as "unknown", not as a real limit).
- `approval_decision` is one of `allow`, `deny`, `canceled`. `approval_source` is a stable classifier of which step decided (e.g. `yolo`, `session_permissions_allow`, `session_permissions_deny`, `team_permissions_allow`, `team_permissions_deny`, `pre_tool_use_hook_allow`, `pre_tool_use_hook_deny`, `readonly_hint`, `user_approved`, `user_approved_session`, `user_approved_tool`, `user_rejected`, `context_canceled`).
## Hook Output
Hooks communicate back via JSON output to stdout:
```json
{
"continue": true,
"stop_reason": "Optional message when continue=false",
"suppress_output": false,
"system_message": "Warning message to show user",
"decision": "block",
"reason": "Explanation for the decision",
"hook_specific_output": {
"hook_event_name": "pre_tool_use",
"permission_decision": "allow",
"permission_decision_reason": "Command is safe",
"updated_input": { "cmd": "modified command" }
}
}
```
All fields are optional. Returning `{}` (or no output at all) means "do nothing, continue normally".
### Output Fields
| Field | Type | Description |
| ----------------- | ------- | ----------------------------------------------- |
| `continue` | boolean | Whether to continue execution (default: `true`) |
| `stop_reason` | string | Message to show when `continue=false` |
| `suppress_output` | boolean | Hide stdout from transcript |
| `system_message` | string | Warning message to display to user |
| `decision` | string | For blocking: `block` to prevent operation |
| `reason` | string | Explanation for the decision |
### Pre-Tool-Use / Permission-Request Specific Output
The `hook_specific_output` for `pre_tool_use` (and `permission_request`) supports:
| Field | Type | Description |
| ---------------------------- | ------ | --------------------------------------- |
| `permission_decision` | string | `allow`, `deny`, or `ask` |
| `permission_decision_reason` | string | Explanation for the decision |
| `updated_input` | object | Modified tool input (replaces original) |
| `metadata` | object | (`permission_request` and `pre_tool_use` entries with `preempt_yolo: true` only) string key/value annotations merged onto the tool-call confirmation prompt — see below |
### Preempting `--yolo` from `pre_tool_use`
`pre_tool_use` entries default to firing AFTER the deterministic approval
pipeline (`--yolo` / permission allow-rules / read-only hint), so a yolo'd
call skips them entirely. For security-critical checks that MUST run on
every call regardless of `--yolo`, set `preempt_yolo: true` on the matcher
entry:
```yaml
hooks:
pre_tool_use:
- matcher: "*"
preempt_yolo: true
hooks:
- type: builtin
command: safer_shell
```
The entry then fires in a dedicated stage 0 BEFORE `Decide()`:
- `deny` rejects the call outright; the user is not prompted.
- `ask` forces user confirmation. The default `pre_tool_use` lane and
`permission_request` are skipped on this path so a policy-level
allow there can't override the security verdict.
- `allow` is advisory — the pipeline still runs `Decide()` and the
rest of `pre_tool_use`. Same shape as a regular `allow` on the
default lane, just observed earlier.
- No verdict (empty `permission_decision`) falls through.
Hook crashes on a `preempt_yolo: true` entry fail closed (deny), matching
the default `pre_tool_use` posture.
Preempting entries can attach structured context via
`hook_specific_output.metadata` (`map[string]string`). The runtime merges
that into the tool-call confirmation event. Two key conventions get
special rendering in the TUI confirmation prompt:
- `blast_radius` — one of `low`, `medium`, `high`, `unknown`. Rendered
as a colored severity badge (green / yellow / red / muted).
- `category` — taxonomy tag (e.g. `fs-delete`, `dk-volume-del`).
Plus a free-form `reason` key that the dialog shows as supporting
context. Other keys render as plain text. Last writer wins on key
clashes across hooks. The `safer_shell` builtin uses this convention.
### Tool-Response-Transform Specific Output
The `hook_specific_output` for `tool_response_transform` supports:
| Field | Type | Description |
| ----------------------- | ------ | --------------------------------------------- |
| `updated_tool_response` | string | Rewritten tool output (replaces the original) |
This is the symmetric counterpart of `pre_tool_use`'s `updated_input`, applied to tool **results** instead of tool **arguments**. The rewrite reaches every downstream consumer — event subscribers, the persisted session file, the `post_tool_use` hook input, and the next LLM call. Use it to truncate excessive output, scrub PII, or normalise tool dialects. The built-in `redact_secrets` registers itself on this event as the third leg of the redact_secrets feature.
### Context-Contributing Events
For `session_start`, `user_prompt_submit`, `user_steering_messages_submit`, `user_followup_submit`, `turn_start`, `post_tool_use`, `pre_compact`, and `stop`, hooks may set `hook_specific_output.additional_context` to inject text into the conversation. `turn_start` context is **transient** (recomputed every turn, never persisted); `session_start` context **persists** for the life of the session. `user_steering_messages_submit` and `user_followup_submit` context is **transient** like `user_prompt_submit` — it is spliced into the steered/follow-up turn only and never persisted. (`worktree_create` also surfaces stdout, but to the CLI user rather than the conversation — the session doesn't exist yet.)
### Before-Compaction Specific Output
For `before_compaction`, the `hook_specific_output.summary` field, when non-empty, replaces the LLM-generated compaction summary. The runtime applies the string verbatim and skips the model call.
```json
{
"hook_specific_output": {
"hook_event_name": "before_compaction",
"summary": "User asked to refactor pkg/foo. Done in commit abc123."
}
}
```
Returning `decision: "block"` (or exit code 2) instead vetoes the compaction entirely. Be cautious about denying when `compaction_reason` is `overflow`: the runtime is recovering from a context-overflow error and a denial there will leave the session unable to make progress.
### Plain Text Output
For `session_start`, `user_prompt_submit`, `user_steering_messages_submit`, `user_followup_submit`, `turn_start`, `post_tool_use`, `pre_compact`, and `stop` hooks, plain text written to stdout (i.e., output that is not valid JSON) is captured as additional context for the agent. For `pre_compact` it is appended to the compaction prompt; for the others it is spliced into the conversation as a (transient or persisted) system message depending on the event.
## Exit Codes
Hook exit codes have special meaning:
| Exit Code | Meaning |
| --------- | -------------------------------------- |
| `0` | Success — continue normally |
| `2` | Blocking error — stop the operation |
| Other | Error — logged but execution continues |
## Per-hook options
Hooks have a default timeout of 60 seconds. You can also give hooks a name, add environment variables, choose a working directory, and control how non-security hook failures behave:
```yaml
hooks:
post_tool_use:
- matcher: "shell"
hooks:
- name: "summarize shell output"
type: command
command: "./summarize.sh"
timeout: 120 # 2 minutes
working_dir: ./hooks
env:
PROFILE: dev
on_error: warn # warn | ignore | block
```
`pre_tool_use` is fail-closed for safety: a failed pre-tool hook blocks the tool call regardless of `on_error`.
`working_dir` and `env` apply to `command` and `builtin` hooks. For `builtin` hooks, `working_dir` is resolved with the same logic as `command` hooks (absolute path wins; relative paths join onto the executor directory). For `model` hooks, both fields are accepted by the schema but have no effect: model hooks render a prompt template and call the LLM API directly — no subprocess is spawned and no file I/O is performed, so working directory and environment variables have no applicable semantics.
> [!WARNING]
> **Performance**
>
> Hooks run synchronously and can slow down agent execution. Keep hook scripts fast and efficient. Consider using `suppress_output: true` for logging hooks to reduce noise.
> [!NOTE]
> **Session End and Cancellation**
>
> `session_end` hooks are designed to run even when the session is interrupted (e.g., Ctrl+C). They are still subject to their configured timeout.
## Examples
### Validation Script
A simple pre-tool-use hook that blocks dangerous shell commands:
```bash
#!/bin/bash
# scripts/validate-command.sh
# Read JSON input from stdin
INPUT=$(cat)
TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name')
CMD=$(echo "$INPUT" | jq -r '.tool_input.cmd // empty')
# Block dangerous commands
if [[ "$TOOL_NAME" == "shell" ]]; then
if [[ "$CMD" =~ ^sudo ]] || [[ "$CMD" =~ rm.*-rf ]]; then
echo '{"decision": "block", "reason": "Dangerous command blocked by policy"}'
exit 2
fi
fi
# Allow everything else (returning {} means "do nothing, continue normally")
echo '{}'
exit 0
```
### Audit Logging
A post-tool-use hook that logs all tool calls:
```bash
#!/bin/bash
# scripts/log-tool-call.sh
INPUT=$(cat)
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name')
SESSION_ID=$(echo "$INPUT" | jq -r '.session_id')
# Append to audit log
echo "$TIMESTAMP | $SESSION_ID | $TOOL_NAME" >> ./audit.log
# Don't block execution
echo '{"continue": true}'
exit 0
```
### Session Lifecycle
Session start and end hooks for environment setup and cleanup:
```yaml
hooks:
session_start:
- type: command
timeout: 10
command: |
INPUT=$(cat)
SESSION_ID=$(echo "$INPUT" | jq -r '.session_id // "unknown"')
echo "Session $SESSION_ID started at $(date)" >> /tmp/agent-session.log
echo '{"hook_specific_output":{"additional_context":"Session initialized."}}'
session_end:
- type: command
timeout: 10
command: |
INPUT=$(cat)
SESSION_ID=$(echo "$INPUT" | jq -r '.session_id // "unknown"')
REASON=$(echo "$INPUT" | jq -r '.reason // "unknown"')
echo "Session $SESSION_ID ended ($REASON) at $(date)" >> /tmp/agent-session.log
```
### Response Logging with Stop Hook
Log every model response for analytics or compliance:
```yaml
hooks:
stop:
- type: command
timeout: 10
command: |
INPUT=$(cat)
SESSION_ID=$(echo "$INPUT" | jq -r '.session_id // "unknown"')
RESPONSE_LENGTH=$(echo "$INPUT" | jq -r '.stop_response // ""' | wc -c | tr -d ' ')
echo "[$(date)] Session $SESSION_ID - Response: $RESPONSE_LENGTH chars" >> /tmp/agent-responses.log
```
The `stop` hook is useful for:
- **Response quality checks** — validate that responses meet criteria before returning
- **Analytics** — track response lengths, patterns, or content
- **Compliance logging** — record all agent outputs for audit
### Error Notifications
Send alerts when the agent encounters errors:
```yaml
hooks:
notification:
- type: command
timeout: 10
command: |
INPUT=$(cat)
LEVEL=$(echo "$INPUT" | jq -r '.notification_level // "unknown"')
MESSAGE=$(echo "$INPUT" | jq -r '.notification_message // "no message"')
echo "[$(date)] [$LEVEL] $MESSAGE" >> /tmp/agent-notifications.log
```
The `notification` hook fires when:
- The model returns an error (all models failed) — also fires `on_error`
- A degenerate tool call loop is detected — also fires `on_error`
- The maximum iteration limit is reached — also fires `on_max_iterations`
Use `on_error` and `on_max_iterations` instead of `notification` when you want a structured handler for one of these conditions without parsing `notification_level`.
### Turn-Start: per-turn context
`turn_start` fires at the start of every agent turn (each model call). Anything you contribute via `additional_context` (or plain stdout) is appended as a **transient** system message for that turn only — it is *not* persisted to the session. Use it for fast-moving signals like the date, current git state, or per-turn prompt files. The built-in hooks `add_date`, `add_prompt_files`, `add_git_status`, and `add_git_diff` all target this event.
### Turn-End: per-turn finalizer
`turn_end` is the symmetric counterpart of `turn_start`. It fires once per turn when the iteration finishes — no matter why. The runtime guarantees the dispatch on every exit path (a normal stop, an error, a hook-driven shutdown, the loop detector, even context cancellation), and it uses `context.WithoutCancel` internally so handlers run to completion on Ctrl+C.
The `reason` field classifies the exit:
| `reason` | When |
| --------------- | ------------------------------------------------------------ |
| `normal` | Model finished cleanly with no follow-up |
| `continue` | More iterations to come (e.g. tool calls, follow-up message) |
| `steered` | Drained steered messages prompted a re-entry |
| `error` | Model call failed (`handleStreamError` exited the loop) |
| `canceled` | Context was cancelled (e.g. Ctrl+C) |
| `hook_blocked` | `before_llm_call` or `post_tool_use` denied the call |
| `loop_detected` | The consecutive-tool-call loop detector terminated the turn |
`turn_end` is observational — the result is ignored. Use it to time turns, accumulate per-turn metrics (token usage, tool counts), or notify external observability pipelines symmetrically with `turn_start`.
### Before/After-LLM-Call: budget guards and model auditing
`before_llm_call` fires immediately before every model call (after `turn_start` has assembled the messages). It cannot contribute context — use `turn_start` for that — but it can **stop the run** by returning `decision: block` (or exit code 2). The built-in `max_iterations` hook implements a hard cap on top of this event.
`after_llm_call` fires immediately after each successful model call, before the response is recorded into the session and tool calls are dispatched. The assistant text is in `stop_response`, and the call's `usage` and `cost` carry the per-turn token usage and computed USD spend (see the field notes above). Use it for response auditing, redaction logging, quality metrics, or a sidecar cost ledger that records per-call spend without subscribing to the runtime event channel. Failed model calls fire `on_error` instead.
### Before/After-Compaction: structured compaction control
`before_compaction` fires immediately before a compaction. Unlike `pre_compact`, it carries structured token-pressure data: `input_tokens`, `output_tokens`, `context_limit`, and a `compaction_reason` (`threshold`, `overflow`, or `manual`). Hooks can either:
- veto compaction by returning `decision: block` (the runtime skips compaction entirely), or
- replace the LLM-generated summary by returning `hook_specific_output.summary` (the runtime applies that summary verbatim and skips the model call).
`after_compaction` fires after a successful compaction. It carries the produced `summary` along with the *pre-compaction* `input_tokens` / `output_tokens` so observability handlers can naturally express "compacted from X to Y". `after_compaction` is purely observational; output is ignored.
### Agent-Switch and Session-Resume: observability for multi-agent and long runs
`on_agent_switch` fires whenever the runtime moves the active agent to a new one — `transfer_task`, `handoff`, `force_handoff`, or the return after a transferred task completes. The cause is in `agent_switch_kind`, the source and destination in `from_agent` and `to_agent`. Use it for audit, transcript, and metrics pipelines that track which agent ran which tools.
The built-in [`unload`](#available-built-ins) hooks into this event to release the resources held by the previous agent's models. It's the canonical way to run two heavy local models on a GPU that can only fit one at a time:
```yaml
agents:
coder:
model: qwen3-large
handoffs: [reviewer]
hooks:
on_agent_switch:
- type: builtin
command: unload
reviewer:
model: qwen3-coder
handoffs: [coder]
hooks:
on_agent_switch:
- type: builtin
command: unload
models:
qwen3-large:
provider: dmr
model: ai/qwen3-large
qwen3-coder:
provider: dmr
model: ai/qwen3-coder
```
At every transfer the runtime ships a snapshot of the previous agent's model endpoints on the `on_agent_switch` hook input, and the `unload` builtin POSTs `{"model": "<id>"}` to each DMR endpoint's `/_unload` URL over plain HTTP. For cloud providers (OpenAI, Anthropic, …) the hook is a silent no-op since they don't expose an HTTP unload endpoint. Cross-provider chains are safe — only DMR endpoints are touched. See [`examples/unload_on_switch.yaml`](https://github.com/docker/docker-agent/blob/main/examples/unload_on_switch.yaml) for the full file.
`on_session_resume` fires when the user explicitly approves the runtime to continue past its configured `max_iterations` limit. `previous_max_iterations` carries the cap that was reached and `new_max_iterations` carries the new cap after approval. Useful for alerting on extended-runtime sessions or for billing / quota pipelines that meter resumes.
### Tool-Approval-Decision: who-approved-what audit trail
`on_tool_approval_decision` fires after the runtime's tool-approval chain (yolo / permissions / readonly / pre_tool_use hooks / interactive prompt) has resolved a verdict for a tool call. `approval_decision` is `allow`, `deny`, or `canceled`; `approval_source` is a stable classifier of which step produced the verdict. Observational only — it gives audit pipelines a single, structured "who approved what" record without re-implementing the chain.
### Worktree-Create: prepare an isolated checkout
`worktree_create` fires once, just after `docker agent run --worktree[=name]` creates a fresh [git worktree](../../features/cli/index.md) and **before** the session starts. Each hook runs **inside** the new worktree — its working directory (and `cwd` in the input) is the fresh checkout — so setup commands operate on the new tree rather than your original one. The worktree path and branch are in `worktree_path` and `worktree_branch`, and `worktree_source_dir` carries the repository root it was branched from.
Use it to prepare the checkout before the agent begins: copy untracked files git won't carry over (`.env`, local config), install dependencies, or warm caches. Because the worktree lives under the docker-agent data directory — not next to your checkout — resolve the original files through `worktree_source_dir` rather than a relative path. A hook may **abort the run** by returning `decision: block` / `{"continue": false}` / exit code 2 (for example, when a setup step fails); plain stdout is surfaced as additional context.
```yaml
hooks:
worktree_create:
# Copy untracked dotfiles git won't bring into the new worktree.
- name: seed local env
type: command
command: |
INPUT=$(cat)
SRC=$(echo "$INPUT" | jq -r '.worktree_source_dir // ""')
[ -n "$SRC" ] && [ -f "$SRC/.env" ] && [ ! -f .env ] && cp "$SRC/.env" .env
echo "Prepared worktree"
# Install dependencies, aborting the run on failure.
- name: install dependencies
type: command
timeout: 600
command: |
if [ -f package.json ]; then
npm install || { echo '{"continue": false, "system_message": "npm install failed"}'; exit 2; }
fi
```
Unlike most events, `worktree_create` is dispatched from the CLI rather than the run loop, because the worktree (and the working directory the runtime, session, tools, and snapshot machinery all capture) must be settled before the runtime and session exist. See [`examples/worktree_create_hook.yaml`](https://github.com/docker/docker-agent/blob/main/examples/worktree_create_hook.yaml) for the full file.
### Pre-Compact: steer the summary
`pre_compact` fires just before the runtime compacts the session transcript. Its `source` field tells you why compaction was triggered:
- `manual` — the user invoked `/compact`
- `auto` — proactive compaction at the configured threshold
- `overflow` — emergency compaction after a context-overflow error
- `tool_overflow` — proactive compaction triggered by tool results pushing the estimated context past the threshold
Return `additional_context` (or plain stdout) to append guidance to the compaction prompt without modifying the agent's instruction. Block the event (`decision: block` / exit code 2) to cancel compaction — useful when you want to handle truncation yourself.
### User-Prompt-Submit: gate or enrich every user message
`user_prompt_submit` fires once per user message, after the prompt is recorded in the session and before the first model call. The submitted text is in `prompt`. Use it to:
- block prompts that violate policy (`decision: block` / exit code 2),
- inject per-prompt context (`additional_context` is spliced as a transient system message for that turn),
- audit user prompts to a log.
It does **not** fire for sub-sessions (transferred tasks, background agents, skill sub-sessions) because their kick-off message is synthesised by the runtime.
### User-Steering-Messages-Submit: gate or enrich mid-flight steering
`user_steering_messages_submit` is the steering-queue analogue of `user_prompt_submit`. It fires each time the runtime drains the steering queue — messages the user submitted while the agent was already working: mid-turn (after a batch of tool calls), after the model stopped, or while idle before the first model call. The drained messages arrive as a JSON array in `steering_messages`. Use it to:
- block a run when steering violates policy (`decision: block` / exit code 2),
- inject context in response to the steering (`additional_context` is spliced as a transient system message for the steered turn — never persisted, exactly like `user_prompt_submit`),
- audit steering messages to a log.
Unlike `turn_end` with `reason: steered`, which only observes the mid-turn and post-stop drains, this event fires on **every** drain — including steering applied while the agent was idle before its first model call.
```yaml
hooks:
user_steering_messages_submit:
- type: command
timeout: 5
command: |
INPUT=$(cat)
COUNT=$(echo "$INPUT" | jq -r '.steering_messages | length')
echo "$INPUT" | jq -r '.steering_messages[]' >> /tmp/agent-steering.log
if [ "$COUNT" -gt 0 ]; then
echo '{"hook_specific_output":{"additional_context":"The user sent new instructions while you were working — re-read the latest user messages and adjust course before continuing."}}'
fi
```
### User-Followup-Submit: gate or enrich queued follow-ups
`user_followup_submit` is the follow-up-queue analogue of `user_prompt_submit`. It fires each time the runtime dequeues a follow-up message at the end of a turn and starts a fresh turn for it. Follow-ups are user messages queued for end-of-turn processing (the FollowUp API / queue) — distinct from mid-turn steering: the model sees a follow-up as fresh input, not an interruption, and each follow-up gets a full undivided turn. The follow-up text is in `prompt`. Use it to:
- block a queued follow-up that violates policy (`decision: block` / exit code 2),
- inject per-follow-up context (`additional_context` is spliced as a transient system message for the follow-up turn — never persisted, exactly like `user_prompt_submit`),
- audit follow-up messages to a log.
This closes the gap left by `user_prompt_submit`, which fires only for the first interactive prompt and never for queued follow-ups.
```yaml
hooks:
user_followup_submit:
- type: command
timeout: 5
command: |
INPUT=$(cat)
echo "$INPUT" | jq -r '.prompt' >> /tmp/agent-followups.log
```
### Subagent-Stop: observe handoff completions
`subagent_stop` fires whenever a sub-agent finishes — `transfer_task` returns, a background agent completes, or a skill sub-session ends. It runs against the *parent* agent's hooks executor, so handlers configured on the orchestrator see every child completion in one place. The sub-agent's name is in `agent_name`, the parent's session ID in `parent_session_id`, and the child's final assistant message in `stop_response`.
### Permission-Request: programmatic tool approval
`permission_request` fires just before the runtime would prompt the user to approve a tool call (i.e. when neither `--yolo` nor a permissions rule short-circuited the decision and the tool is not read-only). Use the same `hook_specific_output.permission_decision` shape as `pre_tool_use` to auto-approve or auto-deny the call:
```yaml
hooks:
permission_request:
- matcher: "shell"
hooks:
- type: command
command: |
INPUT=$(cat)
CMD=$(echo "$INPUT" | jq -r '.tool_input.cmd // ""')
if echo "$CMD" | grep -qE '^(ls|pwd|cat) '; then
echo '{"hook_specific_output":{"permission_decision":"allow","permission_decision_reason":"safe read-only command"}}'
fi
```
Return nothing to fall through to the usual interactive confirmation.
When the hook falls through (returns no `permission_decision`), it can still attach key/value `metadata` to the confirmation prompt the runtime shows the user. The runtime merges it onto any static metadata the toolset attached to the tool (hook keys win on a clash) and emits it on the tool-call confirmation message, so clients (TUI, HTTP) can render extra per-call context. Keys from multiple matching hooks are merged; the last hook in config order wins on a clash.
```yaml
hooks:
permission_request:
- matcher: "shell"
hooks:
- type: command
command: |
INPUT=$(cat)
CMD=$(echo "$INPUT" | jq -r '.tool_input.cmd // ""')
if echo "$CMD" | grep -qE '\brm\b'; then
echo '{"hook_specific_output":{"metadata":{"risk":"high","note":"deletes files"}}}'
fi
```
### LLM as a Judge (Auto-Approving Tool Calls)
The `model` hook type asks an LLM and translates its reply into the
hook's native output — no Go code, no shell glue, no JSON parsing on
your side. Combined with the well-known `pre_tool_use_decision`
schema it gives you a fully-configurable LLM judge that decides
`allow` / `ask` / `deny` per tool call.
```yaml
hooks:
pre_tool_use:
- matcher: "shell|edit_file|mcp:.*"
hooks:
- type: model
model: openai/gpt-4o-mini
timeout: 15
schema: pre_tool_use_decision
prompt: |
You are a security judge for an autonomous agent.
Decide whether this tool call is safe to auto-approve.
Tool: {{ .ToolName }}
Args: {{ .ToolInput | toJSON }}
Project rules:
- Reads under the working directory are safe.
- Writes to ~/.ssh / ~/.aws / ~/.docker are deny.
```
| Field | Required | Description |
| --------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `model` | yes | Model spec (`provider/model`, e.g. `openai/gpt-4o-mini`). The judge model — small/cheap is recommended. |
| `prompt` | yes | Go [`text/template`](https://pkg.go.dev/text/template) body. Sees the hook [Input](#hook-input) as data, plus the `toJSON` and `truncate <n>` helpers. |
| `schema` | no | Well-known response interpretation. `pre_tool_use_decision` produces a `permission_decision` verdict; omit for free-form text injected as `additional_context`. |
| `timeout` | no (default 60s) | Per-call timeout. **Timeouts fail closed (deny) for `pre_tool_use`** regardless of any other setting. Match it to your judge model's typical latency plus a small buffer. |
The `pre_tool_use_decision` schema constrains the judge to reply with
strict `{decision, reason}` JSON. Providers that honor structured
output (OpenAI, ...) are asked to emit that shape directly; on
providers that ignore it the framework still parses tolerant
JSON-in-text. Anything unparseable propagates as a hook error and the
executor falls closed (deny) on `pre_tool_use`.
Pair it with deterministic `permissions:` rules so destructive calls
(e.g. `sudo`, `rm -rf`) are blocked even if the judge is misled, and
obvious read-only calls bypass the LLM entirely. See
[`examples/llm_judge.yaml`](https://github.com/docker/docker-agent/blob/main/examples/llm_judge.yaml)
for a complete configuration.
**Security considerations**:
- **Sensitive data**: Tool arguments (including file paths, command
arguments, and any other parameters) are sent to the judge LLM. Avoid
using the judge on tools that handle secrets, or ensure your judge
model is self-hosted.
- **Defense in depth**: The judge should not be your only security
layer. Use deterministic `permissions:` rules to block obviously
dangerous operations (e.g., `sudo`, `rm -rf`) before the judge sees
them, as shown in the example configuration.
## CLI Flags
You can add hooks from the command line without modifying the agent's YAML file. This is useful for one-off debugging, audit logging, or layering hooks onto an existing agent.
| Flag | Description |
| ---------------------- | ------------------------------------------------ |
| `--hook-pre-tool-use` | Run a command before every tool call |
| `--hook-post-tool-use` | Run a command after every tool call |
| `--hook-session-start` | Run a command when a session starts |
| `--hook-session-end` | Run a command when a session ends |
| `--hook-on-user-input` | Run a command when waiting for input |
| `--hook-stop` | Run a command when the model finishes responding |
All flags are repeatable — pass multiple to register multiple hooks.
```bash
# Add a session-start hook
$ docker agent run agent.yaml --hook-session-start "./scripts/setup-env.sh"
# Combine multiple hooks
$ docker agent run agent.yaml \
--hook-pre-tool-use "./scripts/validate.sh" \
--hook-post-tool-use "./scripts/log.sh"
# Add hooks to an agent from a registry
$ docker agent run agentcatalog/coder \
--hook-pre-tool-use "./audit.sh"
```
> [!NOTE]
> **Merging behavior**
>
> CLI hooks are **appended** to any hooks already defined in the agent's YAML config. They don't replace existing hooks. Pre/post-tool-use hooks added via CLI match all tools (equivalent to `matcher: "*"`).

View File

@@ -0,0 +1,531 @@
---
title: "Model Configuration"
description: "Complete reference for defining models with providers, parameters, and reasoning settings."
keywords: docker agent, ai agents, configuration, yaml, model configuration
linkTitle: "Model Config"
weight: 40
---
_Complete reference for defining models with providers, parameters, and reasoning settings._
## Full Schema
<!-- yaml-lint:skip -->
```yaml
models:
model_name:
first_available: [list] # Optional: candidate model refs, tried in order by available credentials.
# Mutually exclusive with other model settings.
provider: string # Required unless using first_available. One of: openai, anthropic, google, amazon-bedrock,
# dmr, mistral, xai, nebius, minimax, baseten, ovhcloud, groq, fireworks, deepseek, cerebras, together, huggingface, moonshot, vercel, cloudflare-workers-ai, cloudflare-ai-gateway, requesty, openrouter,
# azure, ollama, github-copilot, or a named provider defined
# under the top-level `providers:` section.
model: string # Required: model identifier
temperature: float # Optional: 0.0–2.0 (provider-dependent; e.g. Anthropic caps at 1.0)
max_tokens: integer # Optional: response length limit
top_p: float # Optional: 0.0–1.0
frequency_penalty: float # Optional: -2.0–2.0
presence_penalty: float # Optional: -2.0–2.0
base_url: string # Optional: custom API endpoint
token_key: string # Optional: env var for API token
thinking_budget: string|int # Optional: reasoning effort
task_budget: int|object # Optional: total task token budget (Anthropic)
parallel_tool_calls: boolean # Optional: allow parallel tool calls
track_usage: boolean # Optional: track token usage
routing: [list] # Optional: rule-based model routing
capabilities: # Optional: override attachment capabilities
image: boolean # Optional: whether the model accepts image attachments
pdf: boolean # Optional: whether the model accepts PDF attachments
provider_opts: # Optional: provider-specific options
key: value
title_model: string # Optional: model used for session-title generation
compaction_model: string # Optional: model used for session-compaction (summary generation)
bypass_models_gateway: boolean # Optional: skip the models gateway for this model
```
## Properties Reference
| Property | Type | Required | Description |
| --------------------- | ---------- | -------- | ------------------------------------------------------------------------------------- |
| `first_available` | array | ✗ | Candidate model references tried in order; selects the first whose credentials are configured. Mutually exclusive with other model settings. |
| `provider` | string | ✓/✗ | Required for regular model definitions; omitted for `first_available` selectors. Provider: `openai`, `anthropic`, `google`, `amazon-bedrock`, `dmr`, `mistral`, `xai`, `nebius`, `minimax`, `baseten`, `ovhcloud`, `groq`, `fireworks`, `deepseek`, `cerebras`, `together`, `huggingface`, `moonshot`, `vercel`, `cloudflare-workers-ai`, `cloudflare-ai-gateway`, `requesty`, `openrouter`, `azure`, `ollama`, `github-copilot`, or any [named provider](../../providers/custom/index.md). |
| `model` | string | ✓/✗ | Required for regular model definitions; omitted for `first_available` selectors. Model name (e.g., `gpt-4o`, `claude-sonnet-4-5`, `gemini-3.5-flash`) |
| `temperature` | float | ✗ | Sampling randomness. Range is provider-dependent — typically `0.0–2.0` (Anthropic caps at `1.0`). `0.0` is deterministic. |
| `max_tokens` | int | ✗ | Maximum response length in tokens |
| `top_p` | float | ✗ | Nucleus sampling threshold (`0.0–1.0`) |
| `frequency_penalty` | float | ✗ | Penalize repeated tokens (`-2.0–2.0`) |
| `presence_penalty` | float | ✗ | Encourage topic diversity (`-2.0–2.0`) |
| `base_url` | string | ✗ | Custom API endpoint URL (for self-hosted or proxied endpoints) |
| `token_key` | string | ✗ | Environment variable name containing the API token (overrides provider default) |
| `thinking_budget` | string/int | ✗ | Reasoning effort control |
| `task_budget` | int/object | ✗ | Total token budget for an agentic task (forwarded to Anthropic; see [Task Budget](#task-budget)). |
| `parallel_tool_calls` | boolean | ✗ | Allow model to call multiple tools at once |
| `track_usage` | boolean | ✗ | Track and report token usage for this model |
| `routing` | array | ✗ | Rule-based routing to different models. See [Model Routing](../routing/index.md). |
| `capabilities` | object | ✗ | Override attachment capabilities for this model. See [Attachment Capability Overrides](#attachment-capability-overrides). |
| `provider_opts` | object | ✗ | Provider-specific options (see provider pages) |
| `title_model` | string | ✗ | Model used for session-title generation. Can be a named model from the `models:` section or an inline `provider/model` string. When omitted, the agent's primary model generates titles. Cannot be combined with `first_available`. |
| `compaction_model` | string | ✗ | Model used for session compaction (summary generation). Can be a named model or an inline `provider/model` string. When omitted, the primary model compacts. Cannot be combined with `first_available`. See [Delegating Session Compaction](#delegating-session-compaction). |
| `bypass_models_gateway` | boolean | ✗ | When `true`, this model connects directly to its provider even when a models gateway (`--models-gateway` / `CAGENT_MODELS_GATEWAY`) is configured. See [Gateway Bypass](#gateway-bypass). |
## Attachment Capability Overrides
For custom OpenAI-compatible providers, local models (Ollama, DMR), and any
model the built-in catalogue does not describe, docker-agent cannot
auto-detect whether the endpoint accepts image or PDF attachments. When the
model is absent from the catalogue, docker-agent logs a diagnostic and falls
back to text-only, silently dropping attachments.
Declare `capabilities` to make the model's attachment support authoritative
and skip the catalogue lookup entirely:
```yaml
models:
llava-local:
provider: ollama
model: llava
capabilities:
image: true # accepts image attachments
pdf: false # does not accept PDFs
proxy-vision:
provider: vision-proxy
model: gpt-4o
capabilities:
image: true
pdf: true
```
| Field | Type | Description |
| ---------------------- | ------- | ------------------------------------------------- |
| `capabilities.image` | boolean | Whether the model accepts image attachments |
| `capabilities.pdf` | boolean | Whether the model accepts PDF attachments |
The flags must match what the endpoint actually accepts. Claiming a modality
that the endpoint does not support leads to a provider-side API error. When
`capabilities` is omitted the behaviour is unchanged (catalogue lookup then
conservative text-only fallback).
See [`examples/capability-overrides.yaml`](https://github.com/docker/docker-agent/blob/main/examples/capability-overrides.yaml) for a complete example.
## Delegating Session-Title Generation
The `title_model` field lets a heavyweight primary model hand off the cheap
title-generation call to a smaller, faster model:
```yaml
model: anthropic/claude-opus-4-7
title_model: anthropic/claude-haiku-4-5
```
The value can be a named entry from the `models` stanza or an inline
`provider/model` string. When omitted, the agent's primary model generates
titles.
> [!WARNING]
> **Constraint**
>
> `title_model` cannot be combined with `first_available` model selection — the combination is rejected at validation time.
## Delegating Session Compaction
The `compaction_model` field lets a heavyweight primary model hand off the expensive
compaction (summary generation) call to a smaller, faster model:
```yaml
models:
primary:
provider: anthropic
model: claude-sonnet-4-5
compaction_model: fast
fast:
provider: anthropic
model: claude-haiku-4-5
```
The value can be a named entry from the `models` stanza or an inline
`provider/model` string. When omitted, the primary model compacts.
If the compaction model has a **smaller context window** than the primary,
docker-agent triggers compaction against the smaller window so the summary
call can always ingest the full conversation. Pair the primary with a
compaction model whose window is at least as large to keep the proactive
trigger aligned with the primary's window.
> [!WARNING]
> **Constraint**
>
> `compaction_model` cannot be combined with `first_available` model selection — the combination is rejected at validation time.
See [`examples/compaction_model.yaml`](https://github.com/docker/docker-agent/blob/main/examples/compaction_model.yaml) for a complete example.
## Gateway Bypass
When a models gateway (`--models-gateway` / `CAGENT_MODELS_GATEWAY`) is configured,
all models route through it by default. Set `bypass_models_gateway: true` on a
specific model to make it connect directly to its provider instead:
```yaml
models:
gateway-model:
provider: openai
model: gpt-5
direct-model:
provider: anthropic
model: claude-sonnet-4-5
bypass_models_gateway: true # uses ANTHROPIC_API_KEY directly
```
The bypassed model authenticates with the provider's own credentials
(`OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, `token_key`, etc.) rather than the
gateway's short-lived token. The rest of the agent's models continue routing
through the gateway as before.
Bypass is propagated transparently through router models: a bypass-flagged routing
model passes the flag to all of its routed targets automatically.
> [!WARNING]
> **Security note**
>
> On an untrusted config, a malicious `base_url` combined with `bypass_models_gateway: true` could route provider credentials to an attacker-controlled endpoint. Only enable this on configs you control.
> [!WARNING]
> **Constraint**
>
> `bypass_models_gateway: true` cannot be combined with `first_available` — the combination is rejected at validation time.
See [`examples/bypass_models_gateway.yaml`](https://github.com/docker/docker-agent/blob/main/examples/bypass_models_gateway.yaml) for a complete example.
## First Available Models
Use `first_available` when the same agent should work with whichever provider credentials are available in the current environment. docker-agent checks the candidates in order at load time and replaces the selector with the first candidate whose required environment variables are configured.
```yaml
models:
smart:
first_available:
- anthropic/claude-sonnet-4-6
- openai/gpt-5
- google/gemini-3.5-flash
- dmr/ai/qwen3 # local fallback; no API key required
agents:
root:
model: smart
instruction: You are a helpful assistant.
```
Candidates can be inline `provider/model` references or names from the same `models:` section. Local providers such as `dmr` and `ollama` do not require credentials, so they are useful as final fallbacks.
If none of the candidates has credentials configured, docker-agent reports the missing environment variables grouped by candidate. You only need to configure one group of credentials, not every provider in the list.
A `first_available` model is only a selector. It cannot be combined with `provider`, `model`, `routing`, `token_key`, budgets, sampling options, or other model settings. Put those settings on named candidate models instead:
```yaml
models:
claude:
provider: anthropic
model: claude-sonnet-4-6
max_tokens: 64000
gpt:
provider: openai
model: gpt-5
thinking_budget: low
smart:
first_available:
- claude
- gpt
- dmr/ai/qwen3
```
See [`examples/first_available.yaml`](https://github.com/docker/docker-agent/blob/main/examples/first_available.yaml) for a complete example.
## Thinking Budget
Control how much reasoning the model does before responding. This varies by provider:
### OpenAI
Uses effort levels as strings:
```yaml
models:
gpt:
provider: openai
model: gpt-5
thinking_budget: low # minimal | low | medium | high | xhigh
```
### Anthropic
Uses an integer token budget (1024–32768), or — on adaptive-capable models (Opus 4.6+) — `adaptive`, `adaptive/<effort>`, or a bare effort level:
```yaml
models:
claude:
provider: anthropic
model: claude-sonnet-4-5
thinking_budget: 16384 # must be < max_tokens
opus:
provider: anthropic
model: claude-opus-4-6
thinking_budget: adaptive # or adaptive/<effort>, or low | medium | high | xhigh | max
```
### Google Gemini 2.5
Uses an integer token budget. `0` disables, `-1` lets the model decide:
```yaml
models:
gemini:
provider: google
model: gemini-2.5-flash
thinking_budget: -1 # dynamic (default)
```
### Google Gemini 3
Uses effort levels like OpenAI:
```yaml
models:
gemini3:
provider: google
model: gemini-3-flash
thinking_budget: medium # minimal | low | medium | high
```
### Disabling Thinking
Works for all providers:
```yaml
thinking_budget: none # or 0
```
Models that always reason (OpenAI o-series, gpt-5, Gemini 3) fall back to the API default and still reason internally.
See the [Thinking / Reasoning guide](../../guides/thinking/index.md) for per-provider details, including AWS Bedrock and Docker Model Runner.
## Task Budget
**Anthropic-only.**
`task_budget` caps the **total** number of tokens the model may spend across a
multi-step agentic task — combining thinking, tool calls, and final output
tokens. It lets long-running agents self-regulate effort without having to
choose a tight per-call `max_tokens`.
It is forwarded to Anthropic's
[`output_config.task_budget`](https://platform.claude.com/docs/en/about-claude/models/whats-new-claude-4-7)
request field. docker-agent automatically attaches the required
`task-budgets-2026-03-13` beta header whenever this field is set.
You can configure `task_budget` on **any** Claude model — docker-agent never
gates it by model name. At the time of writing only **Claude Opus 4.7**
actually honors the field; other Claude models will reject requests that
include it. Check the Anthropic release notes linked above for the current
list of supported models.
### Integer shorthand
```yaml
models:
opus:
provider: anthropic
model: claude-opus-4-7
task_budget: 128000 # total tokens for the whole task
thinking_budget: adaptive # works nicely together
```
### Object form
Equivalent, and forward-compatible with future budget types:
```yaml
models:
opus:
provider: anthropic
model: claude-opus-4-7
task_budget:
type: tokens # only "tokens" is supported today
total: 128000
```
Setting `task_budget: 0` (or omitting the field) disables the feature — the
model falls back to the provider's default behavior.
Like other inheritable model settings, `task_budget` can also be declared on a
[provider definition](../../providers/custom/index.md) and is
inherited by every model that references that provider.
See [`examples/task_budget.yaml`](https://github.com/docker/docker-agent/blob/main/examples/task_budget.yaml) for a complete example.
## Interleaved Thinking
For Anthropic and Bedrock Claude models, interleaved thinking allows tool calls during model reasoning. It is auto-enabled whenever a thinking budget is configured:
```yaml
models:
claude:
provider: anthropic
model: claude-sonnet-4-5
thinking_budget: 8192
# interleaved_thinking is auto-enabled when thinking_budget is set
provider_opts:
interleaved_thinking: false # disable if needed
```
## Thinking Display (Anthropic)
For Anthropic Claude models, `thinking_display` controls whether thinking blocks are returned in responses when thinking is enabled. Newer Claude models (Opus 4.7+, Fable 5) hide thinking content by default (`omitted`); docker-agent requests `summarized` thinking by default for adaptive/effort-based budgets so reasoning stays visible. Set this provider option to override:
```yaml
models:
opus-4-7:
provider: anthropic
model: claude-opus-4-7
thinking_budget: adaptive
provider_opts:
thinking_display: omitted # "summarized", "display", or "omitted"
```
See the [Anthropic provider page](../../providers/anthropic/index.md#thinking-display) for details.
## Custom HTTP Headers
For OpenAI-compatible providers (`openai`, `github-copilot`, `mistral`, `xai`,
`nebius`, `minimax`, `baseten`, `ovhcloud`, `groq`, `fireworks`, `deepseek`, `cerebras`, `together`, `huggingface`, `moonshot`, `vercel`, `cloudflare-workers-ai`, `cloudflare-ai-gateway`, `requesty`, `openrouter`, `ollama`, and any custom provider using the OpenAI API),
`provider_opts.http_headers` adds arbitrary HTTP headers to every outgoing
request:
```yaml
models:
my_model:
provider: openai
model: gpt-4o
provider_opts:
http_headers:
X-Request-Source: docker-agent
X-Tenant-Id: my-team
```
Header names are matched case-insensitively. The `github-copilot` provider
automatically sets `Copilot-Integration-Id: copilot-developer-cli` — see the
[GitHub Copilot provider page](../../providers/github-copilot/index.md)
for details.
## Examples by Provider
```yaml
models:
# OpenAI
gpt:
provider: openai
model: gpt-5
# Anthropic
claude:
provider: anthropic
model: claude-sonnet-4-5
max_tokens: 64000
# Google Gemini
gemini:
provider: google
model: gemini-3.5-flash
temperature: 0.5
# AWS Bedrock
bedrock:
provider: amazon-bedrock
model: global.anthropic.claude-sonnet-4-5-20250929-v1:0
provider_opts:
region: us-east-1
# OpenRouter
openrouter:
provider: openrouter
model: meta-llama/llama-3.3-70b-instruct
# Docker Model Runner (local)
local:
provider: dmr
model: ai/qwen3
max_tokens: 8192
```
For detailed provider setup, see the [Model Providers](../../providers/overview/index.md) section.
## Custom Endpoints
Use `base_url` to point to custom or self-hosted endpoints:
```yaml
models:
# Azure OpenAI
azure_gpt:
provider: openai
model: gpt-4o
base_url: https://my-resource.openai.azure.com/openai/deployments/gpt-4o
token_key: AZURE_OPENAI_API_KEY
# Self-hosted vLLM
local_llama:
provider: openai # vLLM is OpenAI-compatible
model: meta-llama/Llama-3.2-3B-Instruct
base_url: http://localhost:8000/v1
# Proxy or gateway
proxied:
provider: openai
model: gpt-4o
base_url: https://proxy.internal.company.com/openai/v1
token_key: INTERNAL_API_KEY
```
The `model` and `base_url` fields accept `${env.VAR}` (or `${VAR}`) references, which are substituted from the environment when the model is loaded. This keeps the model id or endpoint out of the config when it is supplied by the environment, e.g. a Docker Compose / DMR setup:
```yaml
models:
nemotron3:
provider: dmr
model: "${env.NEMOTRON3_MODEL}"
base_url: "${env.DMR_BASE_URL}"
```
See [Variable Expansion in Config Fields](../overview/index.md#variable-expansion-in-config-fields) for the full set of fields and supported syntaxes.
See [Local Models](../../providers/local/index.md) for more examples of custom endpoints.
## Inheriting from Provider Definitions
Models can reference a named provider to inherit shared defaults. Model-level settings always take precedence:
```yaml
providers:
my_anthropic:
provider: anthropic
token_key: MY_ANTHROPIC_KEY
max_tokens: 16384
thinking_budget: 8192
temperature: 0.5
models:
claude:
provider: my_anthropic
model: claude-sonnet-4-5
# Inherits max_tokens, thinking_budget, temperature from provider
claude_fast:
provider: my_anthropic
model: claude-haiku-4-5
thinking_budget: 1024 # Overrides provider default
```
See [Provider Definitions](../../providers/custom/index.md) for the full list of inheritable properties.

View File

@@ -0,0 +1,507 @@
---
title: "Configuration Overview"
description: "docker-agent uses YAML or HCL configuration files to define agents, models, tools, and their relationships."
keywords: docker agent, ai agents, configuration, yaml, configuration overview
linkTitle: "Overview"
weight: 10
aliases:
- /ai/docker-agent/reference/config/
---
_docker-agent uses YAML or HCL configuration files to define agents, models, tools, and their relationships._
## File Structure
A docker-agent config can be written in YAML or HCL. The examples on this page use YAML; see [HCL Configuration](../hcl/index.md) for the block-based HCL syntax.
A docker-agent config has these main sections:
```bash
# 1. Version — configuration schema version (optional but recommended)
version: 10
# 2. Metadata — optional agent metadata for distribution
metadata:
author: my-org
description: My helpful agent
version: "1.0.0"
# 3. Models — define AI models with their parameters
models:
claude:
provider: anthropic
model: claude-sonnet-4-5
max_tokens: 64000
# 4. Agents — define AI agents with their behavior
agents:
root:
model: claude
description: A helpful assistant
instruction: You are helpful.
toolsets:
- type: think
# 5. RAG — define retrieval-augmented generation sources (optional)
rag:
docs:
docs: ["./docs"]
strategies:
- type: chunked-embeddings
embedding_model: openai/text-embedding-3-small
# 6. MCPs — reusable MCP server definitions (optional)
mcps:
github:
remote:
url: https://api.githubcopilot.com/mcp
transport_type: sse
# 7. Providers — optional reusable provider definitions
providers:
my_provider:
provider: anthropic # or openai (default), google, amazon-bedrock, etc.
token_key: MY_API_KEY
max_tokens: 16384
# 8. Permissions — agent-level tool permission rules (optional)
# For user-wide global permissions, see ~/.config/cagent/config.yaml
permissions:
allow: ["read_*"]
deny: ["shell:cmd=sudo*"]
# 9. Commands & Skills — reusable, named groups shared across agents (optional)
commands:
ci:
deploy: "Deploy the application"
skills:
base: [local, git]
# 10. Toolsets — reusable, named toolset definitions shared across agents (optional)
toolsets:
fs:
type: filesystem
```
## Minimal Config
The simplest possible configuration — a single agent with an inline model:
```yaml
agents:
root:
model: openai/gpt-5
description: A helpful assistant
instruction: You are a helpful assistant.
```
The same config in HCL:
```hcl
agent "root" {
model = "openai/gpt-5"
description = "A helpful assistant"
instruction = "You are a helpful assistant."
}
```
## Inline vs Named Models
Models can be referenced inline or defined in the `models` section:
- **Inline** — quick and simple. Use `provider/model` syntax directly: `model: openai/gpt-5`
- **Named** — full control over parameters, reusable across agents: `model: my_claude`
## Config Sections
- [**HCL Configuration**](../hcl/index.md) — write the same agent schema in HCL using labeled blocks, heredocs, and block-based tool definitions.
- [**Agent Config**](../agents/index.md) — all agent properties: model, instruction, tools, sub-agents, hooks, and more.
- [**Model Config**](../models/index.md) — provider setup, parameters, thinking budget, and provider-specific options.
- [**Tool Config**](../tools/index.md) — built-in tools, MCP tools, Docker MCP, LSP, API tools, and tool filtering.
## Advanced Configuration
- [**Hooks**](../hooks/index.md) — run shell commands at lifecycle events like tool calls and session start/end.
- [**Permissions**](../permissions/index.md) — control which tools auto-approve, require confirmation, or are blocked.
- [**Sandbox Mode**](../sandbox/index.md) — run agents in an isolated Docker container for security.
- [**Structured Output**](../structured-output/index.md) — constrain agent responses to match a specific JSON schema.
## Environment Variables
API keys and secrets are read from environment variables — never stored in config files. See [Managing Secrets](../../guides/secrets/index.md) for all the ways to provide credentials (env files, Docker Compose secrets, macOS Keychain, `pass`):
| Variable | Provider |
| -------------------------- | --------------------------------------------------- |
| `OPENAI_API_KEY` | OpenAI |
| `ANTHROPIC_API_KEY` | Anthropic |
| `GOOGLE_API_KEY` / `GEMINI_API_KEY` | Google Gemini |
| `MISTRAL_API_KEY` | Mistral |
| `XAI_API_KEY` | xAI |
| `NEBIUS_API_KEY` | Nebius |
| `MINIMAX_API_KEY` | MiniMax |
| `REQUESTY_API_KEY` | Requesty |
| `OPENROUTER_API_KEY` | OpenRouter |
| `GITHUB_TOKEN` | GitHub Copilot (PAT with `copilot` scope) |
| `AZURE_API_KEY` | Azure OpenAI (override with `token_key`) |
| `AWS_BEARER_TOKEN_BEDROCK` | AWS Bedrock (or the standard AWS credentials chain) |
**Tool Auto-Installation:**
| Variable | Description |
| --------------------- | --------------------------------------------------------------- |
| `DOCKER_AGENT_AUTO_INSTALL` | Set to `false` to disable automatic tool installation |
| `DOCKER_AGENT_TOOLS_DIR` | Override the base directory for installed tools (default: `~/.cagent/tools/`) |
**Runtime overrides:**
| Variable | Description |
| ----------------------------------- | ---------------------------------------------------------------------------------------------------- |
| `DOCKER_AGENT_DEFAULT_MODEL` | Default model used when none is specified, in `provider/model` form (e.g. `openai/gpt-5`). |
| `DOCKER_AGENT_MODELS_GATEWAY` | Route model traffic through a gateway. Equivalent to the `--models-gateway` flag. |
| `DOCKER_AGENT_HIDE_TELEMETRY_BANNER`| Set to `1` to suppress the first-run telemetry notice. |
| `DOCKER_AGENT_AUTO_UPDATE` | Set to a truthy value (`1`, `true`, `yes`, `on`) to let standalone release binaries self-update before running. See [Optional Self-Updates](../../getting-started/installation/index.md#optional-self-updates). |
> [!NOTE]
> **Legacy `CAGENT_*` aliases**
>
> The same variables are also accepted with the legacy `CAGENT_` prefix (e.g. `CAGENT_DEFAULT_MODEL`, `CAGENT_MODELS_GATEWAY`, `CAGENT_HIDE_TELEMETRY_BANNER`) for backward compatibility. Prefer the `DOCKER_AGENT_*` form in new setups.
> [!IMPORTANT]
> Model references are case-sensitive: `openai/gpt-5` is not the same as `openai/GPT-5`.
## Variable Expansion in Config Fields
docker-agent expands `${env.VAR}` references in many config fields. This is the **canonical syntax everywhere** — prefer it for every field. Two engines back it: a full JavaScript evaluator for prompt/HTTP fields (where you also get defaults, ternaries, and tool calls), and a simpler path expander for filesystem/env fields (which additionally accepts the legacy `$VAR` / `${VAR}` / `~` shell forms). Picking `${env.VAR}` everywhere always works; the one caveat is that the path expander does not evaluate richer JS expressions. Using a shell-style `$VAR` in a JS-templated field is currently a silent no-op, so the literal string is passed through. Tracking issue: [#2615](https://github.com/docker/docker-agent/issues/2615).
### JavaScript template literals — `${env.VAR}`
Used wherever the agent prompt or HTTP traffic is templated. Backed by a JS evaluator, so you also get `||` defaults, ternaries, and tool calls (`${tool({...})}`).
Applies to:
- `agents.<name>.description`
- `agents.<name>.welcome_message`
- `agents.<name>.instruction`
- `agents.<name>.commands.*` (string form and `instruction:` field)
- `toolsets[*].instruction`
- `toolsets[*].headers` and `toolsets[*].remote.headers` (MCP, A2A, OpenAPI, fetch, API)
For `api` toolsets, `api_config.endpoint` and `api_config.headers` are also rendered through the JS expander (the same syntax applies).
```yaml
agents:
root:
description: "Assistant for ${env.USER || 'guest'}"
commands:
deploy: "Deploy ${env.PROJECT_NAME || 'app'} to ${env.ENV || 'staging'}"
toolsets:
- type: openapi
url: https://api.example.com
headers:
Authorization: "Bearer ${env.INTERNAL_TOKEN}"
```
Undefined variables expand to the empty string.
### Path & env fields — `${env.VAR}` (canonical), `$VAR` / `${VAR}` / `~` (aliases)
Used for filesystem paths and process environment values. Backed by `os.ExpandEnv` plus tilde expansion against the current user's home directory. The canonical `${env.VAR}` form is accepted here too, so a single syntax works across every field; the bare `$VAR` / `${VAR}` shell forms remain supported as aliases.
Applies to:
- `agents.<name>.toolsets[*].working_dir` (MCP, LSP)
- `agents.<name>.toolsets[*].path` (memory, tasks)
- `agents.<name>.toolsets[*].env` values (MCP, shell, script, LSP)
- The `~` prefix is also accepted in any path-like field documented as such.
```yaml
agents:
root:
toolsets:
- type: memory
path: "~/notes/${env.PROJECT}/memory.db"
- type: mcp
command: my-server
working_dir: "${env.HOME}/work"
```
Unlike the JS-templated fields above, these accept only a plain variable reference: richer JS expressions (e.g. `${env.VAR || 'default'}`) are **not** evaluated here, and the legacy `$VAR` / `${VAR}` forms keep working for backward compatibility.
Model definitions follow the same rule. The `models.<name>.model` and `models.<name>.base_url` fields are expanded when the provider is built, accepting both `${env.VAR}` and `${VAR}`. This is useful when the model id or endpoint is injected by the environment (for example a Docker Compose / DMR setup that exports the model reference as a variable):
```yaml
models:
nemotron3:
provider: dmr
model: "${env.NEMOTRON3_MODEL}" # resolved from the environment at load time
base_url: "${DMR_BASE_URL}" # ${VAR} is accepted as well
```
`token_key` is **not** expanded: it already names the environment variable that holds the API token, so its value is used as a key rather than substituted. An unset variable in `model` or `base_url` is reported as an error instead of dialing with an empty value.
### Quick reference
| Field | `${env.X}` | `$X` / `${X}` | `~` |
| --------------------------------------------- | :--------: | :-----------: | :-: |
| `description`, `welcome_message` | ✓ | ✗ | ✗ |
| `instruction` (agent and toolset) | ✓ | ✗ | ✗ |
| `commands.*` | ✓ | ✗ | ✗ |
| `headers`, `remote.headers`, `api_config.headers` | ✓ | ✗ | ✗ |
| `models.*.model`, `models.*.base_url` | ✓ | ✓ | ✗ |
| `working_dir`, `path` | ✓ | ✓ | ✓ |
| `env` values | ✓ | ✓ | ✗ |
The `~` prefix is meaningful only in path-like fields (`working_dir`, `path`).
Prefer `${env.X}` everywhere. The bare `$X` / `${X}` and `~` forms are accepted only in path and `env` value fields, where they remain supported for backward compatibility.
## Validation
docker-agent validates your configuration at startup:
- Local `sub_agents` must reference agents defined in the config (external OCI references like `agentcatalog/pirate` are pulled from registries automatically; pin them to a digest with `@sha256:…` to avoid a per-run registry lookup)
- Named model references must exist in the `models` section
- Provider names must be valid (`openai`, `anthropic`, `google`, `dmr`, etc.)
- Required environment variables (API keys) must be set
- Tool-specific fields are validated (e.g., `path` is only valid for `memory`)
## JSON Schema
For YAML editor autocompletion and validation, use the [Docker Agent JSON Schema](https://github.com/docker/docker-agent/blob/main/agent-schema.json). Add this to the top of your YAML file:
```bash
# yaml-language-server: $schema=https://raw.githubusercontent.com/docker/docker-agent/main/agent-schema.json
```
## Config Versioning
docker-agent configs are versioned. The current version is `10`. Add the version at the top of your config:
```yaml
version: 10
agents:
root:
model: openai/gpt-5
# ...
```
When you load an older config, docker-agent automatically migrates it to the latest schema. It's recommended to include the version to ensure consistent behavior.
## Metadata Section
Optional metadata for agent distribution via OCI registries:
```yaml
metadata:
author: my-org
license: Apache-2.0
description: A helpful coding assistant
readme: | # Displayed in registries
This agent helps with coding tasks.
version: "1.0.0"
tags: [coding, review]
```
| Field | Description |
| ------------- | ------------------------------------------ |
| `author` | Author or organization name |
| `license` | License identifier (e.g., Apache-2.0, MIT) |
| `description` | Short description for the agent |
| `readme` | Longer markdown description |
| `version` | Semantic version string |
| `tags` | Tags for categorization and discovery |
See [Agent Distribution](../../concepts/distribution/index.md) for publishing agents to registries.
## Reusable MCP Servers (`mcps:`)
The top-level `mcps:` section defines named MCP server configurations that agents can reference with `toolsets: [{type: mcp, ref: <name>}]`. This avoids repeating the same command / URL / headers across agents and keeps credentials in one place.
```yaml
mcps:
github:
remote:
url: https://api.githubcopilot.com/mcp
transport_type: sse
playwright:
command: npx
args: ["-y", "@modelcontextprotocol/server-playwright"]
agents:
root:
model: openai/gpt-5
toolsets:
- type: mcp
ref: github # reuse the definition above
- type: mcp
ref: playwright
```
An `mcps` entry accepts every field a regular `type: mcp` toolset accepts (command/args/env, `remote` with `url`/`transport_type`/`headers`/`oauth`, `tools` filter, `instruction`, `defer`, …) — the `type: mcp` is implicit. See the [Tool Config](../tools/index.md) page for all options and the [Remote MCP Servers](../../features/remote-mcp/index.md) guide for remote setups.
## Reusable Toolsets (`toolsets:`)
The top-level `toolsets:` map defines named toolset configurations that agents can reference by name through `use_toolsets:`. This avoids repeating the same toolset definition across multiple agents — the same pattern as `mcps:` for MCP servers and `commands:` / `skills:` for reusable prompt groups.
Any toolset type is supported, including ones that reference MCP or RAG definitions. Shared toolsets are resolved before the MCP/RAG pass, so they can contain `{type: mcp, ref: <name>}` references.
```yaml
toolsets:
fs: # a named shared toolset
type: filesystem
docs:
type: fetch
allowed_domains:
- docker.com
agents:
root:
model: openai/gpt-5
# Pull in shared toolsets by name; inline toolsets come first.
use_toolsets: [fs, docs]
toolsets:
- type: think
reviewer:
model: openai/gpt-5
# Reuse the same filesystem toolset without copying its definition.
use_toolsets: [fs]
```
Inline `toolsets:` entries listed directly on the agent take precedence in ordering (they come first) and are always included alongside referenced ones.
See [`examples/shared-toolsets.yaml`](https://github.com/docker/docker-agent/blob/main/examples/shared-toolsets.yaml) for a complete example.
## Reusable Commands & Skills (`commands:` / `skills:`)
The top-level `commands:` and `skills:` sections define named, reusable groups that agents pull in by name through `use_commands:` / `use_skills:`. This avoids repeating the same command set or skill configuration across agents. Each group value uses the exact same format as an agent's own `commands` / `skills` field.
Referenced groups are merged into the agent during config loading. An agent's own inline `commands` / `skills` entries take precedence on name conflicts.
```yaml
commands:
ci: # a named command group
deploy: "Deploy the application"
test: "Run the test suite"
skills:
base: [local, git] # a named skill group
agents:
root:
model: openai/gpt-5
use_commands: [ci] # reuse the "ci" command group
use_skills: [base] # reuse the "base" skill group
commands:
lint: "Run the linter" # inline command, merged in (wins on conflict)
reviewer:
model: openai/gpt-5
use_commands: [ci] # same group, reused without duplication
```
See [`examples/shared-commands-skills.yaml`](https://github.com/docker/docker-agent/blob/main/examples/shared-commands-skills.yaml) for a complete example.
## Custom Providers Section
Define reusable provider configurations with shared defaults. Providers can wrap any provider type — not just OpenAI-compatible endpoints:
```yaml
providers:
# OpenAI-compatible custom endpoint
azure:
api_type: openai_chatcompletions
base_url: https://my-resource.openai.azure.com/openai/deployments/gpt-4o
token_key: AZURE_OPENAI_API_KEY
# Anthropic with shared model defaults
team_anthropic:
provider: anthropic
token_key: TEAM_ANTHROPIC_KEY
max_tokens: 32768
thinking_budget: 16384
models:
azure_gpt:
provider: azure
model: gpt-4o
claude:
provider: team_anthropic
model: claude-sonnet-4-5
# Inherits max_tokens, thinking_budget from provider
agents:
root:
model: claude
```
| Field | Description |
| --------------------- | ---------------------------------------------------------------------------------------- |
| `provider` | Underlying provider type: `openai` (default), `anthropic`, `google`, `amazon-bedrock`, etc. |
| `api_type` | API schema: `openai_chatcompletions` (default) or `openai_responses`. OpenAI-only. |
| `base_url` | Base URL for the API endpoint. Required for OpenAI-compatible providers. |
| `token_key` | Environment variable name for the API token. |
| `temperature` | Default sampling temperature. |
| `max_tokens` | Default maximum response tokens. |
| `thinking_budget` | Default reasoning effort/budget. |
| `task_budget` | Default total token budget for an agentic task (Anthropic; honored by Claude Opus 4.7 today). |
| `top_p` | Default top-p sampling parameter. |
| `frequency_penalty` | Default frequency penalty. |
| `presence_penalty` | Default presence penalty. |
| `parallel_tool_calls` | Enable parallel tool calls by default. |
| `track_usage` | Track token usage by default. |
| `provider_opts` | Provider-specific options. |
See [Provider Definitions](../../providers/custom/index.md) for more details.
## Reusable YAML (anchors & aliases)
YAML anchors (`&name`), aliases (`*name`) and merge keys (`<<`) are part of the YAML spec, and Docker Agent's config parser supports them. Use them to declare a value once and reuse it elsewhere in the same file, instead of copy-pasting the same block across agents.
This complements the named-reuse sections above (`mcps:`, `commands:` / `skills:`, `providers:`). Reach for anchors when you want to share something those sections don't cover, such as an instruction string or a block of agent settings.
Reuse a value verbatim with an anchor and an alias:
```yaml
agents:
root:
model: anthropic/claude-sonnet-4-5
description: Coordinator.
instruction: &house_rules |
You are part of the Acme engineering team.
Cite the files you looked at and keep changes minimal.
reviewer:
model: anthropic/claude-sonnet-4-5
description: Reviews code changes.
instruction: *house_rules # the same instruction, declared once
```
Compose a block with a merge key (`<<`), then override individual fields:
```yaml
agents:
reviewer: &specialist
model: anthropic/claude-sonnet-4-5
description: Reviews code changes.
instruction: |
You are a meticulous software professional.
toolsets:
- type: filesystem
documenter:
<<: *specialist # inherit model, instruction, toolsets
description: Writes documentation. # then override one field
```
> [!WARNING]
> **Where anchors can live**
>
> An anchor has to sit on a real value inside a known section (for example a real agent, model, or MCP entry, as above). Parking anchors in a separate top-level block such as `defaults:` or `prompts:` fails, because the parser rejects unknown top-level keys.
> [!WARNING]
> **Overriding merged keys**
>
> Overriding a key that a `<<` merge already set works only in the `agents:` section, as shown above. Every other section (`models:`, `mcps:`, `providers:`, `rag:`) is parsed strictly and reports the override as a duplicate key. There, use `<<` only to add new fields, or use the named-reuse sections above when you need per-entry overrides.
Anchors are for static reuse within a single file, not dynamic values or cross-file composition. For environment-specific settings, see [Variable Expansion in Config Fields](#variable-expansion-in-config-fields), which substitutes `${env.VAR}` at load time. Templating tags such as `!include` are not acted on: the tag is ignored and its argument is kept as a plain string, so no other file is loaded. Circular aliases are not detected, so keep references acyclic.
See [`examples/yaml-anchors.yaml`](https://github.com/docker/docker-agent/blob/main/examples/yaml-anchors.yaml) for a complete example.

View File

@@ -0,0 +1,242 @@
---
title: "Permissions"
description: "Control which tools can execute automatically, require confirmation, or are blocked entirely."
keywords: docker agent, ai agents, configuration, yaml, permissions
weight: 70
---
_Control which tools can execute automatically, require confirmation, or are blocked entirely._
## Overview
Permissions provide fine-grained control over tool execution. You can configure which tools are auto-approved (run without asking), which require user confirmation, and which are completely blocked.
> [!NOTE]
> **Evaluation Order**
>
> Permissions are evaluated in this order: **Deny → Allow → Ask**. Deny patterns take priority, then allow patterns, and anything else defaults to asking for user confirmation.
## Permission Levels
Permissions can be defined at two levels:
| Level | Location | Scope |
| ----- | -------- | ----- |
| **Agent-level** | Agent YAML config (`permissions:` section) | Applies to that specific agent config |
| **Global (user-level)** | `~/.config/cagent/config.yaml` under `settings.permissions` | Applies to every agent you run |
Both levels use the same `allow`/`ask`/`deny` pattern syntax. When both are present, they are **merged** at startup -- patterns from both sources are combined into a single checker. See [Merging Behavior](#merging-behavior) for details.
## Agent-Level Configuration
```yaml
agents:
root:
model: openai/gpt-4o
description: Agent with permission controls
instruction: You are a helpful assistant.
permissions:
# Auto-approve these tools (no confirmation needed)
allow:
- "read_file"
- "read_*" # Glob patterns
- "shell:cmd=ls*" # With argument matching
# Always ask before running these tools, even if an allow pattern would match
ask:
- "shell:cmd=git push*"
- "write_file:path=/home/user/important/*"
# Block these tools entirely
deny:
- "shell:cmd=sudo*"
- "shell:cmd=rm*-rf*"
- "dangerous_tool"
```
The three lists are evaluated in order `deny` → `allow` → `ask`, so an `ask:` entry lets you add a confirmation layer on top of an otherwise-allowed tool.
## Global Permissions
Global permissions let you enforce rules across **all** agents, regardless of which agent config you run. Define them in your user config file:
```yaml
# ~/.config/cagent/config.yaml
settings:
permissions:
deny:
- "shell:cmd=sudo*"
- "shell:cmd=rm*-rf*"
allow:
- "read_*"
- "shell:cmd=ls*"
- "shell:cmd=cat*"
```
This is useful for setting personal safety guardrails that apply everywhere -- for example, always blocking `sudo` or always auto-approving read-only tools -- without relying on each agent config to include those rules.
### Merging Behavior
When both global and agent-level permissions are present, they are merged into a single set of patterns before evaluation. The merge works as follows:
- **Deny patterns from either source block the tool.** A global deny cannot be overridden by an agent-level allow, and vice versa.
- **Allow patterns from either source auto-approve the tool** (as long as no deny pattern matches).
- **Ask patterns from either source force confirmation** (as long as no deny or allow pattern matches).
The evaluation order remains the same after merging: **Deny > Allow > Ask > default Ask**.
> [!TIP]
> **Example: Global deny + agent allow**
>
> If your global config denies `shell:cmd=sudo*` and an agent config allows `shell:cmd=sudo apt update`, the deny wins. Deny patterns always take priority regardless of source.
## Pattern Syntax
Permissions support glob-style patterns with optional argument matching:
### Simple Patterns
| Pattern | Matches |
| -------------- | ------------------------------ |
| `shell` | Exact match for `shell` tool |
| `read_*` | Any tool starting with `read_` |
| `mcp:github:*` | Any GitHub MCP tool |
| `*` | All tools |
### Argument Matching
You can match tools based on their argument values using `tool:arg=pattern` syntax:
```yaml
permissions:
allow:
# Allow shell only when cmd starts with "ls" or "cat"
- "shell:cmd=ls*"
- "shell:cmd=cat*"
# Allow edit_file only in specific directory
- "edit_file:path=/home/user/safe/*"
deny:
# Block shell with sudo
- "shell:cmd=sudo*"
# Block writes to system directories
- "write_file:path=/etc/*"
- "write_file:path=/usr/*"
```
### Multiple Argument Conditions
Chain multiple argument conditions with colons. All conditions must match:
```yaml
permissions:
allow:
# Allow shell with ls in current directory
- "shell:cmd=ls*:cwd=."
deny:
# Block shell with rm -rf anywhere
- "shell:cmd=rm*:cmd=*-rf*"
```
## Glob Pattern Rules
Patterns follow filepath.Match semantics with some extensions:
- `*` — matches any sequence of characters (including spaces)
- `?` — matches any single character
- `[abc]` — matches any character in the set
- `[a-z]` — matches any character in the range
Matching is **case-insensitive**.
> [!TIP]
> **Trailing Wildcards**
>
> Trailing wildcards like `sudo*` match any characters including spaces, so `sudo*` matches `sudo rm -rf /`.
## Decision Types
| Decision | Behavior |
| --------- | --------------------------------------------------- |
| **Allow** | Tool executes immediately without user confirmation |
| **Ask** | User must confirm before tool executes (default) |
| **Deny** | Tool is blocked and returns an error to the agent |
## Examples
### Read-Only Agent
Allow all read operations, block all writes:
```yaml
permissions:
allow:
- "read_file"
- "read_multiple_files"
- "list_directory"
- "directory_tree"
- "search_files_content"
deny:
- "write_file"
- "edit_file"
- "shell"
```
### Safe Shell Agent
Allow specific safe commands, block dangerous ones:
```yaml
permissions:
allow:
- "shell:cmd=ls*"
- "shell:cmd=cat*"
- "shell:cmd=grep*"
- "shell:cmd=find*"
- "shell:cmd=head*"
- "shell:cmd=tail*"
- "shell:cmd=wc*"
deny:
- "shell:cmd=sudo*"
- "shell:cmd=rm*"
- "shell:cmd=mv*"
- "shell:cmd=chmod*"
- "shell:cmd=chown*"
```
### MCP Tool Permissions
Control MCP tools by their qualified names:
```yaml
permissions:
allow:
# Allow all GitHub read operations
- "mcp:github:get_*"
- "mcp:github:list_*"
- "mcp:github:search_*"
deny:
# Block destructive GitHub operations
- "mcp:github:delete_*"
- "mcp:github:close_*"
```
## Combining with Hooks
Permissions work alongside [hooks](../hooks/index.md). The evaluation order is:
1. Check **deny** patterns — if matched, tool is blocked
2. Check **allow** patterns — if matched, tool is auto-approved
3. Run **pre_tool_use hooks** — hooks can allow, deny, or ask
4. If no decision, **ask user** for confirmation
Hooks can override allow decisions but cannot override deny decisions.
> [!WARNING]
> **Security Note**
>
> Permissions are enforced client-side. They help prevent accidental operations but should not be relied upon as a security boundary for untrusted agents. For stronger isolation, use [sandbox mode](../sandbox/index.md).

View File

@@ -0,0 +1,169 @@
---
title: "Model Routing"
description: "Route requests to different models based on the content of user messages."
keywords: docker agent, ai agents, configuration, yaml, model routing
weight: 100
---
_Route requests to different models based on the content of user messages._
## Overview
Model routing lets you define a "router" model that automatically selects the best underlying model based on the user's message. This is useful for cost optimization, specialized handling, or load balancing across models.
> [!NOTE]
> **How It Works**
>
> docker-agent uses NLP-based text similarity (via Bleve full-text search) to match user messages against example phrases you define. The route with the best-matching examples wins, and that model handles the request.
## Configuration
Add `routing` rules to any model definition. The model's `provider`/`model` fields become the fallback when no route matches:
```yaml
models:
smart_router:
# Fallback model when no routing rule matches
provider: openai
model: gpt-5-mini
# Routing rules
routing:
- model: anthropic/claude-sonnet-4-5
examples:
- "Write a detailed technical document"
- "Help me architect this system"
- "Review this code for security issues"
- "Explain this complex algorithm"
- model: openai/gpt-5
examples:
- "Generate some creative ideas"
- "Write a story about"
- "Help me brainstorm"
- "Come up with names for"
- model: openai/gpt-5-mini
examples:
- "What time is it"
- "Convert this to JSON"
- "Simple math calculation"
- "Translate this word"
agents:
root:
model: smart_router
description: Assistant with intelligent model routing
instruction: You are a helpful assistant.
```
## Routing Rules
Each routing rule has:
| Field | Type | Required | Description |
| ---------- | ------ | -------- | ------------------------------------------------------------- |
| `model` | string | ✓ | Target model (inline format or reference to `models` section) |
| `examples` | array | ✓ | Example phrases that should route to this model |
## Matching Behavior
The router:
1. Extracts the last user message from the conversation
2. Searches all examples using full-text search
3. Aggregates match scores by route (best score per route wins)
4. Selects the route with the highest overall score
5. Falls back to the base model if no good match is found
> [!TIP]
> **Writing Good Examples**
>
> - Use diverse phrasing that captures the intent
> - Include keywords users actually use
> - Add 5-10 examples per route for best results
> - Examples don't need to be exact matches — the router uses semantic similarity
## Use Cases
### Cost Optimization
Route simple queries to cheaper models:
```yaml
models:
cost_optimizer:
provider: openai
model: gpt-5-mini # Cheap fallback
routing:
- model: anthropic/claude-sonnet-4-5
examples:
- "Complex analysis"
- "Detailed research"
- "Multi-step reasoning"
```
### Specialized Models
Route coding tasks to code-specialized models:
```yaml
models:
task_router:
provider: openai
model: gpt-5-mini # General fallback
routing:
- model: anthropic/claude-sonnet-4-5
examples:
- "Write code"
- "Debug this function"
- "Review my implementation"
- "Fix this bug"
- model: openai/gpt-5
examples:
- "Write a blog post"
- "Help me with writing"
- "Summarize this document"
```
### Load Balancing
Distribute load across equivalent models from different providers:
```yaml
models:
load_balancer:
provider: openai
model: gpt-5-mini
routing:
- model: anthropic/claude-sonnet-4-5
examples:
- "First request pattern"
- "Another request type"
- model: google/gemini-2.5-flash
examples:
- "Different request pattern"
- "Alternative query style"
```
## Debugging
Enable debug logging to see routing decisions:
```bash
$ docker agent run config.yaml --debug
```
Look for log entries like:
```text
"Rule-based router selected model" router=smart_router selected_model=anthropic/claude-sonnet-4-5
"Route matched" model=anthropic/claude-sonnet-4-5 score=2.45
```
> [!WARNING]
> **Limitations**
>
> - Routing only considers the last user message, not full conversation context
> - Very short messages may not match well — consider your fallback carefully
> - Each routed model creates a separate provider connection

View File

@@ -0,0 +1,209 @@
---
title: "Sandbox Mode"
description: "Run agents in an isolated Docker sandbox VM for enhanced security."
keywords: docker agent, ai agents, configuration, yaml, sandbox mode
weight: 80
---
_Run agents in an isolated Docker sandbox VM for enhanced security._
## Overview
Sandbox mode runs the entire agent inside a disposable sandbox VM instead of directly on the host system. All shell, filesystem, and process activity happens inside that VM, so a misbehaving agent cannot touch files outside the mounted working directory or reach long-lived host state.
The backend is provided by the [`docker sandbox`](https://docs.docker.com/ai/sandboxes/) CLI plugin (ships with Docker Desktop) or the standalone [`sbx`](https://github.com/docker/sbx) CLI if it is on `PATH`.
> [!NOTE]
> **Requirements**
>
> Sandbox mode requires Docker Desktop with sandbox support (or a working `sbx` CLI). docker-agent shells out to these tools, it does not start raw `docker run` containers.
## Usage
Enable sandbox mode with the `--sandbox` flag on the `docker agent run` command:
```bash
docker agent run --sandbox agent.yaml
```
docker-agent launches a sandbox VM, copies itself into it, mounts the current working directory, and re-runs the agent from inside.
## Flags
| Flag | Default | Description |
| ------------- | -------------------------------------------- | --------------------------------------------------------------------------------------------------------- |
| `--sandbox` | `false` | Enable sandbox mode. |
| `--template` | `docker/sandbox-templates:docker-agent` | OCI image used as the sandbox template. Passed to `docker sandbox create -t` / `sbx create -t`. |
| `--sbx` | `true` | Prefer the `sbx` CLI backend when it is available. Set `--sbx=false` to always use `docker sandbox`. |
| `--no-kit` | `false` | Disable the [auto-kit](#auto-kit) — do not stage skills or prompt files into the sandbox. |
```bash
# Use a custom template image
docker agent run --sandbox --template myorg/custom-agent-template:latest agent.yaml
# Force the docker sandbox backend even if sbx is on PATH
docker agent run --sandbox --sbx=false agent.yaml
# Run without staging skills / prompt files into the sandbox
docker agent run --sandbox --no-kit agent.yaml
```
### Always sandbox a given agent
Add `--sandbox` to an [alias](../../features/cli/index.md#docker-agent-alias) so the
sandbox path is taken automatically whenever that alias is invoked:
```bash
docker agent alias add safe-coder agentcatalog/coder --sandbox
docker agent run safe-coder
```
An explicit `--sandbox=false` on the command line still wins, so you can opt
out of the sandbox for a single run without touching the alias.
### Bake the default into the agent config
Agent authors can declare a sandbox default in the YAML itself. Any caller of
the agent then gets the sandbox path automatically, without having to know
(or remember) to pass `--sandbox`:
```yaml
# agent.yaml
runtime:
sandbox: true
agents:
root:
model: openai/gpt-4o
description: A helpful assistant
instruction: You are a helpful assistant.
toolsets:
- type: shell
```
```bash
docker agent run agent.yaml # runs in a sandbox automatically
```
The rule is the same as for aliases: an explicit `--sandbox=false` on the
CLI overrides the config default, so you can debug an agent on the host
without editing its YAML.
### Declare a network allowlist
The runner already opens the [tool install hosts](#network-allowlist) and
the [models gateway](#how-it-works) automatically, but agents that talk
to endpoints those resolvers can't infer (custom MCP servers, third-party
APIs, registries not covered by the aqua resolver) would still see a 403
from the sandbox proxy on first contact.
Declare those hosts in `runtime.network_allowlist` and they are unioned
with the inferred set, so the agent can reach them on its first request:
```yaml
# agent.yaml
runtime:
sandbox: true
network_allowlist:
- api.example.com
- registry.npmjs.org
```
Each entry is a hostname with an optional `:port` suffix. Commas and
whitespace are rejected to keep a single entry from smuggling several
rules into the policy engine. The runner prints the resulting allowlist
before launch so you can audit exactly which hosts the run opens up.
### Persist your own allowlist
For hosts you keep needing across agents (a corporate proxy, a
self-hosted registry, ...) `docker agent sandbox allow` writes the
entry into `~/.config/cagent/config.yaml` once and unions it with the
inferred and agent-declared sets on every subsequent `--sandbox` run:
```bash
# I just got a `Blocked by network policy` 403 on api.example.com.
docker agent sandbox allow api.example.com
# See what's currently persisted.
docker agent sandbox list
# Drop a host you no longer need.
docker agent sandbox deny api.example.com
```
When the kit's per-toolset host resolver fails (the `! using fallback
host set` line in the launch summary), the runner now prints a hint
pointing at this command so you can turn the missing host into a
one-line, persistent fix instead of relying on the wider conservative
fallback host set.
## Example
```yaml
# agent.yaml
agents:
root:
model: openai/gpt-4o
description: Agent with sandboxed shell
instruction: You are a helpful assistant.
toolsets:
- type: shell
```
```bash
docker agent run --sandbox agent.yaml
```
## How It Works
1. `--sandbox` tells docker-agent to prefer the `sbx` CLI (if available and `--sbx` is true), otherwise it falls back to `docker sandbox`.
2. A new sandbox VM is created from the image passed via `--template`.
3. The current working directory is mounted into the VM; the agent binary is copied in.
4. The [auto-kit](#auto-kit) is staged on the host and bind-mounted read-only into the VM, so the agent sees its skills and prompt files inside the sandbox.
5. The default-deny network proxy is opened for the configured [models gateway](../../features/cli/index.md#runtime-configuration-flags) and any package hosts the auto-installer needs for the agent's MCP/LSP toolsets.
6. All tools (shell, filesystem, background jobs, etc.) run inside the VM.
7. When the session ends, docker-agent exits but does not stop or remove the sandbox VM; both the VM and the kit are kept around so subsequent runs from the same workspace can reuse them. A fresh sandbox is created only when the mount set has changed.
## Auto-Kit
The sandbox VM has its own filesystem and `$HOME` — none of the host's `~/.agents/skills/`, `~/.claude/skills/`, project-level `.agents/skills/`, or prompt files like `AGENTS.md` and `CLAUDE.md` are visible inside it. To bridge that gap, docker-agent automatically builds a **kit**: a self-contained directory staged on the host before the sandbox starts and bind-mounted read-only into the VM at the same path.
The kit is built whenever `--sandbox` is used with an agent reference. It is opt-out via `--no-kit`.
### What gets staged
For the agent referenced on the command line, the kit collects:
- **Local [skills](../../features/skills/index.md)** — every `SKILL.md` discovered on the host (global `~/.codex/skills/`, `~/.claude/skills/`, `~/.agents/skills/`, plus project `.claude/skills/` and `.agents/skills/`) is copied under `<kit>/skills/<skill-name>/`. The in-sandbox skills loader reads from the kit instead of the (non-existent) host `$HOME`.
- **Prompt files** — every file referenced via the agent's `add_prompt_files` (`AGENTS.md`, `CLAUDE.md`, …) is collected. Files that already live under the working directory are left alone (the live workspace mount surfaces them); files outside it (e.g. an `AGENTS.md` in `$HOME`) are copied under `<kit>/prompt_files/`.
- **A manifest** — `<kit>/manifest.json` records what was staged. The on-disk copy is sanitised so it cannot be used to map the host filesystem from inside the sandbox.
Before launch, docker-agent prints a summary of what was staged so you can see exactly which skills and prompt files the agent will have access to inside the sandbox.
### Secret redaction
Every text file copied into the kit is run through [portcullis](https://github.com/docker/portcullis), which redacts secrets that match its detection patterns (API keys, tokens, …) in the staged copy. The kit's printed summary marks files as `(redacted)` whenever at least one secret was replaced. Detection is best-effort — portcullis recognises common secret formats but novel or obfuscated tokens may slip through, so the kit is not a substitute for keeping secrets out of skill sources in the first place.
### Network allowlist
The sandbox templates ship with a default-deny network proxy that allows the major model providers but blocks `*.docker.com` and every package-registry / source host the auto-installer reaches for. When the agent declares MCP or LSP toolsets that have a `command` and an installable `version`, the kit build resolves each toolset's package against the [aqua](https://aquaproj.github.io/) registry and computes the minimal set of hosts the in-sandbox auto-installer will need (Go module proxy + toolchain bootstrap for `go_install` packages, GitHub release hosts for `github_release` packages, …). Those hosts, `models.dev` (needed so the in-sandbox agent can resolve model metadata such as context limits, pricing, and capabilities — without it the first catalog lookup fails with a `403 Blocked by network policy` error), and the configured [`--models-gateway`](../../features/cli/index.md#runtime-configuration-flags) — are then allow-listed on the sandbox proxy. If a per-toolset registry lookup fails, a conservative fallback union is used so the run can still succeed; the affected toolsets are surfaced in the printed summary.
### Caching
Kits are stored under the docker-agent cache directory (`~/Library/Caches/cagent/sandbox-kits/<hash>` on macOS) keyed by a content hash of the agent reference. Reusing the same agent across runs reuses the same kit directory in place; disk usage is bounded by the number of distinct agents you have run. Kits are deliberately kept on disk between runs because the reused sandbox VM holds a hard reference to the kit's bind-mount path — deleting it would leave the sandbox un-startable.
### Disabling the kit
Pass `--no-kit` to skip the kit build entirely. The agent then runs without any host-side skills or external prompt files visible inside the sandbox, and the network allowlist falls back to the template defaults. Useful for debugging the sandbox itself, or for agents that don't depend on host skills.
```bash
docker agent run --sandbox --no-kit agent.yaml
```
> [!WARNING]
> **Limitations**
>
> - Sandboxes are reused across runs from the same workspace; if the required mount set changes (e.g. a new kit is staged), the previous sandbox is removed and a fresh one is created.
> - Only the working directory, the agent config directory, and (when staged) the kit directory are mounted; other host files are not visible to the agent.
> - Network egress is constrained by the sandbox backend's default-deny policy plus the per-run allowlist described above.

View File

@@ -0,0 +1,216 @@
---
title: "Structured Output"
description: "Force the agent to respond with JSON matching a specific schema."
keywords: docker agent, ai agents, configuration, yaml, structured output
weight: 90
---
_Force the agent to respond with JSON matching a specific schema._
## Overview
Structured output constrains the agent's responses to match a predefined JSON schema. This is useful for building agents that need to produce machine-readable output for downstream processing, API responses, or integration with other systems.
> [!NOTE]
> **When to Use**
>
> - Building API endpoints that need consistent JSON responses
> - Data extraction and transformation pipelines
> - Agents that feed into other automated systems
> - Ensuring predictable output format for parsing
## Configuration
```yaml
agents:
analyzer:
model: openai/gpt-4o
description: Code analyzer that outputs structured results
instruction: |
Analyze the provided code and identify issues.
Return your findings in the structured format.
structured_output:
name: analysis_result
description: Code analysis findings
strict: true
schema:
type: object
properties:
issues:
type: array
items:
type: object
properties:
severity:
type: string
enum: ["error", "warning", "info"]
line:
type: integer
message:
type: string
required: ["severity", "line", "message"]
summary:
type: string
required: ["issues", "summary"]
```
## Properties
| Property | Type | Required | Description |
| ------------- | ------- | -------- | --------------------------------------------------- |
| `name` | string | ✓ | Name identifier for the output schema |
| `description` | string | ✗ | Description of what the output represents |
| `strict` | boolean | ✗ | Enforce strict schema validation (default: `false`) |
| `schema` | object | ✓ | JSON Schema defining the output structure |
## Schema Format
The schema follows [JSON Schema](https://json-schema.org/) specification. Common schema types:
### Simple Object
```yaml
schema:
type: object
properties:
name:
type: string
count:
type: integer
active:
type: boolean
required: ["name", "count"]
```
### Array of Objects
```yaml
schema:
type: object
properties:
items:
type: array
items:
type: object
properties:
id:
type: string
value:
type: number
required: ["id", "value"]
required: ["items"]
```
### Enum Values
```yaml
schema:
type: object
properties:
status:
type: string
enum: ["pending", "approved", "rejected"]
priority:
type: string
enum: ["low", "medium", "high", "critical"]
required: ["status"]
```
## Strict Mode
When `strict: true`, the model is constrained to only produce output that exactly matches the schema. This provides stronger guarantees but may limit the model's flexibility.
- **`strict: false` (default)** — model aims to match the schema but may include additional fields or slight variations.
- **`strict: true`** — model output is constrained to exactly match the schema. Stronger guarantees.
## Provider Support
Structured output support varies by provider:
| Provider | Support | Notes |
| ------------- | ---------- | --------------------------------------- |
| OpenAI | ✓ Full | Native JSON mode with schema validation |
| Anthropic | ✓ Full | Tool-based structured output |
| Google Gemini | ✓ Full | Native JSON mode |
| AWS Bedrock | ✓ Partial | Depends on underlying model |
| DMR | ⚠️ Limited | Depends on model capabilities |
## Example: Data Extraction Agent
```yaml
agents:
extractor:
model: openai/gpt-4o
description: Extract structured data from text
instruction: |
Extract contact information from the provided text.
Return all found contacts in the structured format.
structured_output:
name: contacts
description: Extracted contact information
strict: true
schema:
type: object
properties:
contacts:
type: array
items:
type: object
properties:
name:
type: string
description: Full name of the contact
email:
type: string
description: Email address
phone:
type: string
description: Phone number
company:
type: string
description: Company or organization
required: ["name"]
total_found:
type: integer
description: Total number of contacts found
required: ["contacts", "total_found"]
```
## Example: Classification Agent
```yaml
agents:
classifier:
model: anthropic/claude-sonnet-4-5
description: Classify support tickets
instruction: |
Classify the support ticket into the appropriate category
and priority level based on its content.
structured_output:
name: ticket_classification
strict: true
schema:
type: object
properties:
category:
type: string
enum:
["billing", "technical", "account", "feature_request", "other"]
priority:
type: string
enum: ["low", "medium", "high", "urgent"]
confidence:
type: number
minimum: 0
maximum: 1
description: Confidence score between 0 and 1
reasoning:
type: string
description: Brief explanation for the classification
required: ["category", "priority", "confidence"]
```
> [!WARNING]
> **Tool Limitations**
>
> When using structured output, the agent typically cannot use tools since its response format is constrained to the schema. Design your agent workflow accordingly — structured output agents work best for single-turn analysis or extraction tasks.

View File

@@ -0,0 +1,503 @@
---
title: "Tool Configuration"
description: "Complete reference for configuring built-in tools, MCP tools, and Docker-based tools."
keywords: docker agent, ai agents, configuration, yaml, tool configuration
linkTitle: "Tool Config"
weight: 50
aliases:
- /ai/docker-agent/reference/toolsets/
---
_Complete reference for configuring built-in tools, MCP tools, and Docker-based tools._
## Built-in Tools
Built-in tools are included with docker-agent and require no external dependencies. Add them to your agent's `toolsets` list by `type`. Each tool's dedicated page covers its full configuration options, available operations, and examples.
| Type | Description | Page |
| --- | --- | --- |
| `filesystem` | Read, write, list, search, navigate | [Filesystem](../../tools/filesystem/index.md) |
| `shell` | Execute shell commands (sync + background jobs) | [Shell](../../tools/shell/index.md) |
| `think` | Reasoning scratchpad | [Think](../../tools/think/index.md) |
| `plan` | Shared persistent scratchpad for multi-agent collaboration | [Plan](../../tools/plan/index.md) |
| `session_plan` | Per-session markdown plan for the draft-review-execute workflow | [Session Plan](../../tools/session_plan/index.md) |
| `session_context` | Reference a previous session as context (read-only) | [Session Context](../../tools/session_context/index.md) |
| `todo` | Task list management | [Todo](../../tools/todo/index.md) |
| `memory` | Persistent key-value storage (SQLite) | [Memory](../../tools/memory/index.md) |
| `tasks` | Persistent task database shared across sessions | [Tasks](../../tools/tasks/index.md) |
| `fetch` | HTTP `GET` requests with text/markdown/html output | [Fetch](../../tools/fetch/index.md) |
| `script` | Custom shell scripts as tools | [Script](../../tools/script/index.md) |
| `lsp` | Language Server Protocol integration | [LSP](../../tools/lsp/index.md) |
| `api` | Custom HTTP API tools | [API](../../tools/api/index.md) |
| `openapi` | Import every operation of an OpenAPI 3.x document as tools | [OpenAPI](../../tools/openapi/index.md) |
| `rag` | Retrieval-augmented generation over indexed sources | [RAG](../../tools/rag/index.md) |
| `model_picker` | Let the agent pick between several models per turn | [Model Picker](../../tools/model-picker/index.md) |
| `user_prompt` | Interactive user input | [User Prompt](../../tools/user-prompt/index.md) |
| `open_url` | Open a fixed URL in the user's default browser | [Open URL](../../tools/open-url/index.md) |
| `transfer_task` | Delegate to sub-agents (auto-enabled) | [Transfer Task](../../tools/transfer-task/index.md) |
| `background_agents` | Parallel sub-agent dispatch | [Background Agents](../../tools/background-agents/index.md) |
| `handoff` | Local conversation handoff to another agent in the same config (auto-enabled by `handoffs:`) | [Handoff](../../tools/handoff/index.md) |
| `a2a` | A2A remote agent connection | [A2A](../../tools/a2a/index.md) |
| `mcp_catalog` | Discover and activate remote MCP servers from the Docker MCP Catalog on demand | [MCP Catalog](../../tools/mcp-catalog/index.md) |
**Example:**
```yaml
toolsets:
- type: filesystem
- type: shell
- type: think
- type: todo
- type: memory
path: ./dev.db
```
## MCP Tools
Extend agents with external tools via the [Model Context Protocol](https://modelcontextprotocol.io/). For a standalone overview of the `mcp` toolset see the [MCP tool page](../../tools/mcp/index.md).
> [!TIP]
> **Reusable MCP definitions**
>
> Repeated MCP server definitions can be hoisted into the top-level `mcps:` section and referenced by name with `{type: mcp, ref: <name>}`. See [Reusable MCP Servers](../overview/index.md#reusable-mcp-servers-mcps).
### Docker MCP (Recommended)
Run MCP servers as secure Docker containers via the [MCP Gateway](https://github.com/docker/mcp-gateway):
```yaml
toolsets:
- type: mcp
ref: docker:duckduckgo # web search
- type: mcp
ref: docker:github-official # GitHub integration
```
Browse available tools at the [Docker MCP Catalog](https://hub.docker.com/search?q=&type=mcp).
| Property | Type | Description |
| ------------- | ------ | ---------------------------------------------------------------- |
| `ref` | string | Docker MCP reference (`docker:name`) |
| `tools` | array | Optional: only expose these tools |
| `instruction` | string | Custom instructions injected into the agent's context |
| `config` | any | MCP server-specific configuration (passed during initialization) |
| `working_dir` | string | Working directory for the MCP gateway subprocess. Only applies when the catalog entry runs as a local process (not remote). Relative paths are resolved against the agent's working directory. Supports `${env.VAR}` (canonical), plus `~` and shell-style `$VAR`/`${VAR}` expansion ([details](../overview/index.md#variable-expansion-in-config-fields)). |
### Local MCP (stdio)
Run MCP servers as local processes communicating over stdin/stdout:
```yaml
toolsets:
- type: mcp
command: python
args: ["-m", "mcp_server"]
tools: ["search", "fetch"]
env:
API_KEY: value
```
| Property | Type | Description |
| --- | --- | --- |
| `command` | string | Command to execute the MCP server |
| `args` | array | Command arguments |
| `tools` | array | Optional: only expose these tools |
| `env` | object | Environment variables (key-value pairs) |
| `working_dir` | string | Working directory for the MCP server process. Relative paths are resolved against the agent's working directory. Defaults to the agent's working directory when omitted. Supports `${env.VAR}` (canonical), plus `~` and shell-style `$VAR`/`${VAR}` expansion ([details](../overview/index.md#variable-expansion-in-config-fields)). |
| `instruction` | string | Custom instructions injected into the agent's context |
| `version` | string | Package reference for [auto-installing](#auto-installing-tools) the command binary |
### Remote MCP (Streamable HTTP / SSE)
Connect to MCP servers over the network:
```yaml
toolsets:
- type: mcp
remote:
url: "https://mcp-server.example.com"
transport_type: "streamable"
headers:
Authorization: "Bearer your-token"
# Optional: allow OAuth helper requests to reach private/internal IPs.
allow_private_ips: true
tools: ["search_web", "fetch_url"]
```
| Property | Type | Description |
| ----------------------- | ------- | --------------------------------------------------------------------------------------------------------------------- |
| `remote.url` | string | Base URL of the MCP server |
| `remote.transport_type` | string | `streamable` or `sse` |
| `remote.headers` | object | HTTP headers (typically for auth) |
| `allow_private_ips` | boolean | Permit remote MCP OAuth helper requests to dial non-public IP addresses. Use only for trusted internal servers. |
## Auto-Installing Tools
When configuring MCP or LSP tools that require a binary command, docker agent can **automatically download and install** the command if it's not already available on your system. This uses the [aqua registry](https://github.com/aquaproj/aqua-registry) — a curated index of CLI tool packages.
### How It Works
1. When a toolset with a `command` is loaded, docker agent checks if the command is available in your `PATH`
2. If not found, it checks the docker agent tools directory (`~/.cagent/tools/bin/`)
3. If still not found, it looks up the command in the aqua registry and installs it automatically
### Explicit Package Reference
Use the `version` property to specify exactly which package to install:
```yaml
toolsets:
- type: mcp
command: gopls
version: "golang/tools@v0.21.0"
args: ["mcp"]
- type: lsp
command: rust-analyzer
version: "rust-lang/rust-analyzer@2024-01-01"
file_types: [".rs"]
```
The format is `owner/repo` or `owner/repo@version`. When a version is omitted, the latest release is used.
### Automatic Detection
If the `version` property is not set, docker agent tries to auto-detect the package from the command name by searching the aqua registry:
```yaml
toolsets:
- type: mcp
command: gopls # auto-detected as golang/tools
args: ["mcp"]
```
### Checksum Verification
Where the aqua registry includes a checksum manifest, downloaded binaries are verified against it before installation. Verification behaviour depends on the checksum type advertised:
- **Strong checksums (sha256, sha512, etc.)** — verified before the binary is installed. If the downloaded archive does not match, the install is aborted and an error is returned (fails closed).
- **Unsupported or weak checksum types (e.g. md5, sha1)** — skipped with a warning; installation proceeds without verification.
- **No manifest** — if no checksum is advertised in the registry entry, the binary is installed without verification.
### version_overrides Resolution
The auto-installer correctly resolves **`version_overrides`** entries in the aqua registry. Many common tools (for example, `fzf`) keep their package configuration — including download URLs and checksums — under `version_overrides` rather than at the top level of their registry entry. These tools previously failed to install silently; they are now handled correctly.
### Disabling Auto-Install
**Per toolset** — set `version` to `"false"` or `"off"`:
```yaml
toolsets:
- type: mcp
command: my-custom-server
version: "false"
```
**Globally** — set the `DOCKER_AGENT_AUTO_INSTALL` environment variable:
```bash
export DOCKER_AGENT_AUTO_INSTALL=false
```
### Environment Variables
| Variable | Default | Description |
| ---------------------------- | ------------------ | ------------------------------------------------ |
| `DOCKER_AGENT_AUTO_INSTALL` | (enabled) | Set to `false` to disable all auto-installation |
| `DOCKER_AGENT_TOOLS_DIR` | `~/.cagent/tools/` | Base directory for installed tools |
| `GITHUB_TOKEN` | — | GitHub token to raise API rate limits (optional) |
Installed binaries are placed in `~/.cagent/tools/bin/` and cached so they are only downloaded once.
> [!TIP]
> Auto-install supports both Go packages (via `go install`) and GitHub release binaries (via archive download). The aqua registry metadata determines which method is used.
## Toolset Lifecycle
Long-running toolsets — local MCP servers (stdio), remote MCP servers (Streamable HTTP / SSE), and LSP servers — are managed by a single supervisor that can auto-reconnect them when they crash, time out, or drop their session. The `lifecycle` block on the toolset lets you tune that supervisor per toolset. It applies to every `type: mcp` and `type: lsp` toolset.
The simplest knob is `profile`, which picks a preset:
| Profile | Auto-restart | Use case |
| --- | --- | --- |
| `resilient` | Yes | Default. Exponential backoff on disconnect; the agent keeps running if the toolset is unavailable. Matches the historical docker-agent behaviour. |
| `strict` | No | Fail-fast. Marks the toolset as required. Intended for CI / headless runs where a missing dependency should be a hard error. |
| `best-effort` | No | Single attempt, no retries. Good for experimental MCPs whose flakiness should not amplify into a restart loop. |
```yaml
toolsets:
- type: mcp
ref: docker:duckduckgo
lifecycle:
profile: resilient # default; shown here for clarity
- type: lsp
command: gopls
file_types: [".go"]
lifecycle:
profile: strict
- type: mcp
ref: docker:openbnb-airbnb
lifecycle:
profile: best-effort
```
### Tuning the defaults
Any field set on `lifecycle` overrides the profile preset, so you can mix-and-match: pick a profile and only override the knobs you care about.
```yaml
toolsets:
- type: mcp
command: ["docker", "mcp", "gateway"]
lifecycle:
profile: resilient
max_restarts: 10 # keep trying longer than the default of 5
backoff:
initial: 500ms
max: 1m
multiplier: 2
jitter: 0.2 # 20% random offset to avoid thundering-herd retries
```
| Property | Type | Description |
| --- | --- | --- |
| `profile` | string | One of `resilient` (default), `strict`, `best-effort`. Picks defaults for every other field. |
| `restart` | string | When the supervisor should reconnect after a disconnect: `never`, `on_failure` (default), or `always`. For **remote** MCP toolsets (Streamable HTTP / SSE), `on_failure` is automatically promoted to `always` so idle-timeout closes reconnect gracefully — `never` is still honored. |
| `max_restarts` | int | Maximum consecutive restart attempts before the toolset is marked `Failed`. `0` uses the profile default (5); `-1` means unlimited. |
| `backoff.initial` | duration | First wait between attempts (Go duration: `500ms`, `1s`, …). Default: `1s`. |
| `backoff.max` | duration | Cap on the wait between attempts. Default: `32s`. |
| `backoff.multiplier` | number | Multiplier applied each attempt. Default: `2`. |
| `backoff.jitter` | number | Fraction (0..1) of the computed delay applied as a uniform random offset. `0` disables jitter (default). |
| `required` | boolean | Marks the toolset as critical. Today this is informational; a future eager-startup phase will refuse to start the agent when a required toolset cannot reach Ready. Defaults to `true` under `strict`, `false` otherwise. |
| `startup_timeout` | duration | Cap on the initial connect+initialize duration. Today this is informational; the eager-startup phase that enforces it ships in a follow-up. |
| `call_timeout` | duration | Documented per-call timeout. Informational; the runtime currently uses the caller's context for cancellation. |
> [!NOTE]
> **`required` and `startup_timeout` are not yet enforced**
>
> The schema validates these fields and the supervisor stores them, but no code path acts on them yet. They are documented now so config files written today keep working when the planned eager-startup phase lands. Picking the `strict` profile is forward-compatible — it will start enforcing `required=true` automatically.
### Inspecting and restarting toolsets at runtime
The TUI exposes the supervisor through two slash commands:
- `/tools` — the unified tools dialog. Its top section lists every toolset on the current agent with its lifecycle state (`Stopped`, `Starting`, `Ready`, `Degraded`, `Restarting`, `Failed`), restart count, and last error; its bottom section lists every tool the agent can call, grouped by category. Use this to answer both "what can the agent do?" and "is anything degraded?" with one command.
- `/toolset-restart <name>` — force the supervisor to reconnect the named toolset. Useful after completing OAuth, when a remote MCP server has been redeployed, or when an LSP like `gopls` is stuck.
See the [TUI reference](../../features/tui/index.md) for the full list of slash commands.
See [`examples/lifecycle.yaml`](https://github.com/docker/docker-agent/blob/main/examples/lifecycle.yaml) for a complete lifecycle configuration example.
## TOON-Encoded Tool Outputs
Many MCP servers return verbose JSON responses that consume a lot of context budget. The `toon` field on a toolset transparently re-encodes matching tools' JSON output as [TOON](https://github.com/alpkeskin/gotoon) — a compact, model-friendly key/value format — before the result is shown to the model.
```yaml
toolsets:
- type: mcp
ref: docker:github-official
toon: ".*" # toonify every tool from this MCP server
- type: mcp
command: my-server
toon: "list_.*,get_.*" # only toonify list_/get_ tools
```
| Property | Type | Description |
| -------- | ------ | ----------- |
| `toon` | string | Comma-delimited list of regular expressions matching tool names whose JSON output should be re-encoded as TOON. Non-JSON outputs and non-matching tools are passed through untouched. |
When a tool's output is not valid JSON, it is returned unchanged — TOON encoding is best-effort and never breaks tools that emit plain text.
> [!NOTE]
> **When to use TOON**
>
> TOON typically yields 30-60% smaller payloads than equivalent JSON for MCP tools that return arrays of records (issue lists, search results, file listings, …). It works best when the schema is regular; one-off responses with deeply nested or heterogeneous shapes may benefit less.
## Per-Toolset Model Routing
The `model` field on a toolset overrides which LLM is invoked for the **next turn** after a tool from that toolset returns — letting you process simple tool results (file reads, knowledge-base lookups, shell stdout) with a cheaper or faster model while keeping the agent's primary model for reasoning.
```yaml
models:
primary:
provider: anthropic
model: claude-sonnet-4-5
fast:
provider: anthropic
model: claude-haiku-4-5
agents:
root:
model: primary
toolsets:
- type: filesystem
model: fast # process file reads with the fast model
- type: shell
model: fast # ditto for shell stdout
- type: mcp
ref: docker:github-official
model: openai/gpt-4o-mini # inline provider/model also works
```
| Property | Type | Description |
| -------- | ------ | ----------- |
| `model` | string | Model used for the LLM turn that processes tool results from this toolset. Either a name from the `models:` section or an inline `provider/model` (e.g. `openai/gpt-4o-mini`). The override is **one-shot**: subsequent turns return to the agent's primary model. |
When multiple tool calls in a single turn come from toolsets with different `model` overrides, the runtime picks the override of the **first** tool call that has one set. See [`examples/per_tool_model_routing.yaml`](https://github.com/docker/docker-agent/blob/main/examples/per_tool_model_routing.yaml) for a complete configuration.
## Tool Filtering
Toolsets may expose many tools. Use the `tools` property to whitelist only the ones your agent needs. This works for any toolset type — not just MCP:
```yaml
toolsets:
- type: mcp
ref: docker:github-official
tools: ["list_issues", "create_issue", "get_pull_request"]
- type: filesystem
tools: ["read_file", "search_files_content"]
- type: shell
tools: ["shell"]
```
> [!TIP]
> Filtering tools improves agent performance — fewer tools means less confusion for the model about which tool to use.
## Tool Instructions
Add context-specific instructions that get injected when a toolset is loaded:
```yaml
toolsets:
- type: mcp
ref: docker:github-official
instruction: |
Use these tools to manage GitHub issues.
Always check for existing issues before creating new ones.
Label new issues with 'triage' by default.
```
By default, the `instruction:` field **replaces** the toolset's built-in instructions (if any). To keep the built-in guidance and add your own rules on top, include the `{ORIGINAL_INSTRUCTIONS}` placeholder anywhere in your instruction text. At runtime it expands to the toolset's default instructions:
```yaml
toolsets:
# Enrich: keep built-in instructions, then add your own rules
- type: filesystem
instruction: |
{ORIGINAL_INSTRUCTIONS}
## Project-specific rules
- Never modify files outside the `src/` directory.
- Always create a backup before overwriting a file.
# Enrich: prepend your rules before the built-in instructions
- type: shell
instruction: |
Important: only run commands inside the project root.
{ORIGINAL_INSTRUCTIONS}
# Replace: omit the placeholder to discard built-in instructions entirely
- type: mcp
ref: docker:github-official
instruction: |
Only read GitHub issues. Never create, edit, or close anything.
```
Three patterns at a glance:
| Pattern | Description |
| --- | --- |
| `{ORIGINAL_INSTRUCTIONS}` then your text | Append your rules after the defaults |
| Your text then `{ORIGINAL_INSTRUCTIONS}` | Prepend your rules before the defaults |
| No placeholder | Replace the defaults entirely |
See [`examples/toolset_instructions.yaml`](https://github.com/docker/docker-agent/blob/main/examples/toolset_instructions.yaml) for a complete example.
## Deferred Tool Loading
Load tools on-demand to speed up agent startup. When a toolset is deferred, its tools are registered lazily — the tool server process is not started until the agent first calls one of its tools. This is useful for large toolsets (e.g., an MCP server with hundreds of tools) where startup time matters.
```yaml
toolsets:
- type: mcp
ref: docker:github-official
defer: true
- type: mcp
ref: docker:slack
defer: true
- type: filesystem
```
Or defer specific tools within a toolset:
```yaml
toolsets:
- type: mcp
ref: docker:github-official
defer:
- "list_issues"
- "search_repos"
```
When `defer` is a list of tool names, only those specific tools are deferred; all other tools in the toolset load eagerly. Setting `defer: true` defers the entire toolset.
See [`examples/deferred.yaml`](https://github.com/docker/docker-agent/blob/main/examples/deferred.yaml) for a complete example.
## Combined Example
```yaml
agents:
root:
model: anthropic/claude-sonnet-4-5
description: Full-featured developer assistant
instruction: You are an expert developer.
toolsets:
# Built-in tools
- type: filesystem
- type: shell
- type: think
- type: todo
- type: memory
path: ./dev.db
- type: user_prompt
# LSP for code intelligence
- type: lsp
command: gopls
file_types: [".go"]
# Custom scripts
- type: script
shell:
run_tests:
description: Run the test suite
cmd: task test
lint:
description: Run the linter
cmd: task lint
# Custom API tool
- type: api
api_config:
name: get_status
method: GET
endpoint: "https://api.example.com/status"
instruction: Check service health
# Docker MCP tools
- type: mcp
ref: docker:github-official
tools: ["list_issues", "create_issue"]
- type: mcp
ref: docker:duckduckgo
# Remote MCP
- type: mcp
remote:
url: "https://internal-api.example.com/mcp"
transport_type: "streamable"
headers:
Authorization: "Bearer ${env.INTERNAL_TOKEN}"
```
> [!WARNING]
> **Toolset Order Matters**
>
> If multiple toolsets provide a tool with the same name, the first one wins. Order your toolsets intentionally.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

View File

@@ -0,0 +1,5 @@
---
title: "Features"
description: "Interfaces and features: TUI, CLI, API server, protocols, and more."
weight: 50
---

View File

@@ -0,0 +1,68 @@
---
title: "A2A Protocol"
description: "Expose docker-agent agents via Google's Agent-to-Agent (A2A) protocol for interoperability with other agent frameworks."
keywords: docker agent, ai agents, features, a2a protocol
weight: 60
aliases:
- /ai/docker-agent/integrations/a2a/
---
_Expose docker-agent agents via Google's Agent-to-Agent (A2A) protocol for interoperability with other agent frameworks._
## Overview
The `docker agent serve a2a` command starts an A2A server that exposes your agents using the [A2A protocol](https://a2a-protocol.org/latest/). This enables communication between Docker Agent and other agent frameworks that support A2A.
> [!WARNING]
> **Early support**
>
> A2A support is functional but still evolving. Tool calls, artifacts, and memory features have limited A2A integration. See limitations below.
## Usage
```bash
# Start A2A server for an agent
$ docker agent serve a2a ./agent.yaml
# Specify a custom address
$ docker agent serve a2a ./agent.yaml --listen 127.0.0.1:9000
# Use an agent from the catalog
$ docker agent serve a2a agentcatalog/pirate
```
## Flags
| Flag | Default | Description |
| --------------------------------- | ---------------- | -------------------------------------------------------------------------------------------------------------------- |
| `-l, --listen <addr>` | `127.0.0.1:8082` | Address to listen on. |
| `-a, --agent <name>` | (first agent) | Name of the agent to expose when the config contains multiple agents. Defaults to the team's first agent. |
| `--working-dir <path>` | current dir | Working directory the agent runs in. |
| `--env-from-file <file>` | (none) | Load additional environment variables from a `.env` file (repeatable). |
| `--models-gateway <url>` | (none) | Route all provider traffic through a models gateway URL. |
| `--code-mode-tools` | `false` | Expose tools as a single "code" toolset that accepts a JavaScript snippet to run. |
| `--hook-pre-tool-use <cmd>` | (none) | Add a pre-tool-use hook (repeatable). See [Hooks](../../configuration/hooks/index.md). |
| `--hook-post-tool-use <cmd>` | (none) | Add a post-tool-use hook (repeatable). |
| `--hook-session-start <cmd>` | (none) | Add a session-start hook (repeatable). |
| `--hook-session-end <cmd>` | (none) | Add a session-end hook (repeatable). |
| `--hook-on-user-input <cmd>` | (none) | Add an on-user-input hook (repeatable). |
| `--hook-stop <cmd>` | (none) | Add a stop hook, fired when the model finishes responding (repeatable). |
## Features
- **Auto port selection** — Picks an available port if not specified
- **Agent card** — Provides standard A2A agent metadata
- **Full docker-agent features** — Supports all tools, models, and gateway features
- **Multiple sources** — Load agents from files or the agent catalog
> [!TIP]
> **See also**
>
> For exposing agents via MCP instead, see [MCP Mode](../mcp-mode/index.md). For stdio-based integration, see [ACP](../acp/index.md). For the HTTP API, see [API Server](../api-server/index.md).
## Current Limitations
- Tool calls are handled internally, not exposed as separate A2A events
- A2A artifact support not yet integrated
- A2A memory features not yet integrated
- Multi-agent (sub-agent) scenarios need further work

View File

@@ -0,0 +1,116 @@
---
title: "ACP (Agent Client Protocol)"
description: "Expose docker-agent agents via the Agent Client Protocol for integration with ACP-compatible hosts like VS Code, IDEs, and other developer tools."
keywords: docker agent, ai agents, features, acp (agent client protocol)
linkTitle: "ACP"
weight: 70
aliases:
- /ai/docker-agent/integrations/acp/
---
_Expose docker-agent agents via the Agent Client Protocol for integration with ACP-compatible hosts like VS Code, IDEs, and other developer tools._
## Overview
The `docker agent serve acp` command starts an ACP server that communicates over **stdio** (standard input/output). This makes it ideal for integration with editors, IDEs, and other tools that spawn agent processes — the host sends JSON-RPC messages to docker-agent's stdin and reads responses from stdout.
ACP is built on the [ACP Go SDK](https://github.com/coder/acp-go-sdk) and provides a standardized way for client applications to interact with AI agents.
> [!NOTE]
> **ACP vs A2A vs MCP**
>
> **ACP** connects an agent to a *host application* (IDE, CLI tool) via stdio. **A2A** connects *agents to other agents* over HTTP. **MCP** exposes agents as *tools* for other MCP clients. Choose based on your integration target.
## Usage
```bash
# Start ACP server on stdio
$ docker agent serve acp ./agent.yaml
# With a multi-agent team config
$ docker agent serve acp ./team.yaml
# From the agent catalog
$ docker agent serve acp agentcatalog/pirate
# With a custom session database
$ docker agent serve acp ./agent.yaml --session-db ./my-sessions.db
```
## How It Works
1. The host application spawns `docker agent serve acp agent.yaml` as a child process
2. Communication happens over **stdin/stdout** using the ACP protocol
3. The host sends user messages, docker-agent processes them through the agent
4. Agent responses, tool calls, and events stream back to the host
5. Sessions are persisted in a SQLite database for continuity
```bash
# Conceptual flow:
Host Application
└── spawns: docker agent serve acp agent.yaml
├── stdin ← JSON-RPC requests from host
└── stdout → JSON-RPC responses to host
```
## Features
- **Stdio transport** — No network ports needed; ideal for subprocess integration
- **Session persistence** — SQLite-backed sessions survive process restarts
- **Full agent support** — All docker-agent features work: tools, multi-agent, model fallbacks
- **Multi-agent configs** — Team configurations with sub-agents work transparently
- **Filesystem operations** — Agents can read/write files relative to the host's working directory
## CLI Flags
```bash
docker agent serve acp <agent-file>|<registry-ref> [flags]
```
| Flag | Default | Description |
| --------------------------------- | ---------------------- | -------------------------------------------------------------------------------------------------------------------- |
| `-s, --session-db <path>` | `~/.cagent/session.db` | Path to the SQLite session database. |
| `--working-dir <path>` | current dir | Working directory the agent runs in. |
| `--env-from-file <file>` | (none) | Load additional environment variables from a `.env` file (repeatable). |
| `--models-gateway <url>` | (none) | Route all provider traffic through a models gateway URL. |
| `--code-mode-tools` | `false` | Expose tools as a single "code" toolset that accepts a JavaScript snippet to run. |
| `--hook-pre-tool-use <cmd>` | (none) | Add a pre-tool-use hook (repeatable). See [Hooks](../../configuration/hooks/index.md). |
| `--hook-post-tool-use <cmd>` | (none) | Add a post-tool-use hook (repeatable). |
| `--hook-session-start <cmd>` | (none) | Add a session-start hook (repeatable). |
| `--hook-session-end <cmd>` | (none) | Add a session-end hook (repeatable). |
| `--hook-on-user-input <cmd>` | (none) | Add an on-user-input hook (repeatable). |
| `--hook-stop <cmd>` | (none) | Add a stop hook, fired when the model finishes responding (repeatable). |
## Integration Example
A host application would spawn docker-agent as a subprocess and communicate via the ACP protocol:
```javascript
// Pseudocode for an IDE extension
const child = spawn("docker", ["agent", "serve", "acp", "./agent.yaml"]);
// Send a message to the agent
child.stdin.write(
JSON.stringify({
jsonrpc: "2.0",
method: "agent/run",
params: { message: "Explain this code" },
}),
);
// Read responses
child.stdout.on("data", (data) => {
const response = JSON.parse(data);
// Handle agent response, tool calls, etc.
});
```
> [!TIP]
> **When to use ACP**
>
> Use ACP when building **IDE integrations**, **editor plugins**, or any tool that wants to embed a docker-agent agent as a subprocess. For HTTP-based integrations, use the [API Server](../api-server/index.md) instead.
> [!NOTE]
> **See also**
>
> For HTTP-based agent access, see the [API Server](../api-server/index.md). For agent-to-agent communication, see [A2A Protocol](../a2a/index.md). For exposing agents as MCP tools, see [MCP Mode](../mcp-mode/index.md).

View File

@@ -0,0 +1,355 @@
---
title: "API Server"
description: "Expose your agents via an HTTP API for programmatic access, web frontends, and integrations."
keywords: docker agent, ai agents, features, api server
weight: 80
---
_Expose your agents via an HTTP API for programmatic access, web frontends, and integrations._
## Overview
The `docker agent serve api` command starts an HTTP server that exposes your agents through a REST-style API with Server-Sent Events (SSE) streaming. Use it to build web UIs, integrate with CI/CD pipelines, or connect agents to other services.
```bash
# Start the API server
$ docker agent serve api agent.yaml
# Custom listen address
$ docker agent serve api agent.yaml --listen 0.0.0.0:8080
# With session persistence
$ docker agent serve api agent.yaml --session-db ./sessions.db
# Auto-refresh from OCI registry every 10 minutes
$ docker agent serve api agentcatalog/coder --pull-interval 10
```
## Endpoints
All endpoints are under the `/api` prefix.
### Agents
| Method | Path | Description |
| ------ | ----------------- | --------------------------------- |
| `GET` | `/api/agents` | List all available agents |
| `GET` | `/api/agents/:id` | Get an agent's full configuration |
### Sessions
| Method | Path | Description |
| -------- | ----------------------------------- | ------------------------------------------------------- |
| `GET` | `/api/sessions` | List all sessions |
| `POST` | `/api/sessions` | Create a new session. Accepts an optional `title` field — when set, it is stored and LLM title generation is skipped. |
| `GET` | `/api/sessions/:id` | Get a session by ID (messages, tokens, permissions) |
| `GET` | `/api/sessions/:id/status` | Lightweight runtime state (streaming, title, agent, tokens). Requires an attached runtime. |
| `GET` | `/api/sessions/:id/snapshot` | Full state in one call (stored fields + runtime state + `last_event_seq`) for gapless resync — see [Reconnecting without gaps](#reconnecting-without-gaps). |
| `GET` | `/api/sessions/:id/events` | Live session event stream (SSE) with sequence numbers and replay. Available for a run attached via [`--listen`](#listen). |
| `DELETE` | `/api/sessions/:id` | Delete a session |
| `PATCH` | `/api/sessions/:id/title` | Update session title |
| `PATCH` | `/api/sessions/:id/permissions` | Update session permissions |
| `POST` | `/api/sessions/:id/fork` | Fork a session at a user message — creates a new session with messages `[0, message_index)` of the parent (see [Session Forking](#session-forking)) |
| `POST` | `/api/sessions/:id/resume` | Resume a paused session (after tool confirmation) |
| `POST` | `/api/sessions/:id/tools/toggle` | Toggle auto-approve (YOLO) mode |
| `POST` | `/api/sessions/:id/elicitation` | Respond to an MCP tool elicitation request |
| `POST` | `/api/sessions/:id/steer` | Inject messages into a running turn (pre-empts current) |
| `POST` | `/api/sessions/:id/followup` | Enqueue messages to run after the current turn finishes (supports an `Idempotency-Key` — see [Idempotent follow-ups](#idempotent-follow-ups)). |
| `GET` | `/api/sessions/:id/models` | List available models for the session's current agent |
### Agent Execution
| Method | Path | Description |
| ------ | ------------------------------------------ | ------------------------------------------------------------------------------------ |
| `POST` | `/api/sessions/:id/agent/:agent` | Run the root agent for a session (SSE stream) |
| `POST` | `/api/sessions/:id/agent/:agent/:name` | Run a specific named agent (SSE stream) |
| `GET` | `/api/agents/:id/:agent_name/tools/count` | Count tools currently available to `:agent_name` (accounts for deferred toolsets). |
**Path parameters:**
- **`:agent`** — The agent identifier, which is the **config filename without the `.yaml` extension**. This must match the filename passed to `docker agent serve api`. For example, if you start the server with `docker agent serve api my-assistant.yaml`, the agent identifier is `my-assistant`. When serving a directory of YAML files, each file becomes a separate agent identified by its filename without the extension.
- **`:name`** _(optional)_ — The name of a specific sub-agent defined in a multi-agent configuration. If omitted, the request targets the `root` agent. For example, in a config that defines agents named `root`, `coder`, and `reviewer`, use `/api/sessions/:id/agent/my-config/coder` to run the `coder` sub-agent directly.
**Examples:**
```bash
# Single-agent config: my-assistant.yaml
# Start: docker agent serve api my-assistant.yaml
# Run the root agent:
curl -N -X POST http://localhost:8080/api/sessions/$SID/agent/my-assistant \
-H "Content-Type: application/json" \
-d '{"messages":[{"role": "user", "content": "Hello!"}]}'
# Multi-agent config: team.yaml (defines agents: root, coder, reviewer)
# Start: docker agent serve api team.yaml
# Run the root agent:
curl -N -X POST http://localhost:8080/api/sessions/$SID/agent/team \
-H "Content-Type: application/json" \
-d '{"messages":[{"role": "user", "content": "Review this PR"}]}'
# Run a specific sub-agent (reviewer):
curl -N -X POST http://localhost:8080/api/sessions/$SID/agent/team/reviewer \
-H "Content-Type: application/json" \
-d '{"messages":[{"role": "user", "content": "Review this PR"}]}'
```
### Health
| Method | Path | Description |
| ------ | ----------- | ----------------------------------------- |
| `GET` | `/api/ping` | Health check — returns `{"status": "ok"}` |
### OAuth
| Method | Path | Description |
| ------ | ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `POST` | `/api/mcp-oauth/callback` | Deliver an OAuth deeplink callback to a pending unmanaged OAuth flow. Success path: `?state=<state>&code=<code>`; authorization-server error path: `?state=<state>&error=<error>&error_description=<desc>`. Returns 400 if `state` is missing or neither `code` nor `error` is provided; 404 if no flow is awaiting that `state`. See [Remote MCP OAuth](../remote-mcp/index.md) for details. |
## Streaming Responses
The agent execution endpoints (`POST /api/sessions/:id/agent/:agent`) return **Server-Sent Events (SSE)**. The request body is a JSON object with a `messages` array and an optional `model` field. Setting `model` applies a persistent per-agent override on the session before the turn starts (subsequent turns reuse it). An empty or omitted `model` leaves the existing override untouched. (Each event is a JSON object representing a runtime event — remember that `:agent` is the config filename without the `.yaml` extension.):
```bash
# Send a message and stream the response
# (assuming the server was started with: docker agent serve api my-agent.yaml)
$ curl -N -X POST http://localhost:8080/api/sessions/$SID/agent/my-agent \
-H "Content-Type: application/json" \
-d '{"messages":[{"role": "user", "content": "Hello!"}]}'
# Same call, but switch the agent's model for this turn (and persist it):
$ curl -N -X POST http://localhost:8080/api/sessions/$SID/agent/my-agent \
-H "Content-Type: application/json" \
-d '{"messages":[{"role":"user","content":"Hello!"}],"model":"openai/gpt-4o"}'
# Response (SSE stream):
data: {"type":"stream_started","session_id":"...","agent":"root"}
data: {"type":"agent_choice","content":"Hello! How","agent":"root"}
data: {"type":"agent_choice","content":" can I help","agent":"root"}
data: {"type":"agent_choice","content":" you today?","agent":"root"}
data: {"type":"stream_stopped","session_id":"...","agent":"root"}
```
Event types include:
- `stream_started` / `stream_stopped` — Agent execution lifecycle
- `agent_choice` — Streamed text content (partial responses)
- `tool_call` — Agent requesting tool execution
- `tool_call_confirmation` — Tool call waiting for user approval
- `tool_call_response` — Tool execution result
- `error` — Error during execution
## Typical Workflow
1. **List agents** — `GET /api/agents` to discover available agents
2. **Create session** — `POST /api/sessions` to start a conversation
3. **Send message** — `POST /api/sessions/:id/agent/:agent` with user messages
4. **Stream response** — Read SSE events as the agent processes
5. **Handle confirmations** — If a tool call needs approval, `POST /api/sessions/:id/resume`
6. **Continue** — Send follow-up messages to the same session
```bash
# 1. List available agents
$ curl http://localhost:8080/api/agents
[{"name":"my-agent","multi":false,"description":"A helpful assistant"}]
# 2. Create a session
$ curl -X POST http://localhost:8080/api/sessions \
-H "Content-Type: application/json" -d '{}'
{"id":"abc-123","title":"","created_at":"..."}
# Create a session with a pre-supplied title (skips LLM title generation)
$ curl -X POST http://localhost:8080/api/sessions \
-H "Content-Type: application/json" -d '{"title":"deploy check"}'
{"id":"def-456","title":"deploy check","created_at":"..."}
# title preserved; LLM title generation skipped
# 3. Run the agent with a message
$ curl -N -X POST http://localhost:8080/api/sessions/abc-123/agent/my-agent \
-H "Content-Type: application/json" \
-d '{"messages":[{"role":"user","content":"What files are in the current directory?"}]}'
```
## CLI Flags
```bash
docker agent serve api <agent-file>|<agents-dir> [flags]
```
| Flag | Default | Description |
| ------------------ | ---------------- | ------------------------------------------------ |
| `-l, --listen` | `127.0.0.1:8080` | Address to listen on |
| `--auth-token` | (none) | Bearer token required for all API requests. Leave empty to disable authentication (safe when listening on loopback interfaces only). Recommended when `--listen` binds to a network-reachable interface. |
| `-s, --session-db` | `session.db` | Path to the SQLite session database |
| `--pull-interval` | `0` (disabled) | Auto-pull OCI reference every N minutes |
| `--fake` | (none) | Replay AI responses from cassette file (testing) |
| `--record` | (none) | Record AI API interactions to cassette file |
| `--mcp-oauth-redirect-uri` | (none) | Public HTTPS URL advertised as the OAuth `redirect_uri` for unmanaged MCP OAuth flows. When set, docker-agent drives PKCE and code exchange in-process and sends the full authorize URL to the client via elicitation. See [Remote MCP](../remote-mcp/index.md) for details. |
> [!TIP]
> **Live profiling (advanced)**
>
> For production diagnostics, set the `CAGENT_PPROF_ADDR` environment variable (or the hidden `--pprof-addr` flag) to a TCP address such as `127.0.0.1:6060`. docker-agent will start a Go pprof HTTP server at `/debug/pprof/`, which you can query with `go tool pprof`. Use a loopback address — a non-loopback binding logs a security warning. This flag is intentionally hidden from `--help`.
> [!TIP]
> **Multi-agent configs**
>
> You can point `docker agent serve api` at a directory containing multiple agent YAML files. Each becomes a separate agent accessible via `/api/agents`. Combine with `--pull-interval` to auto-refresh agents from an OCI registry.
## Session Persistence
Sessions are stored in a SQLite database (default: `session.db` in the current directory). This means:
- Sessions survive server restarts
- Multiple server instances can share a database
- Use `--session-db` to specify a custom path
## Tool Call Approval
By default, tool calls require approval. In the API workflow:
1. Agent makes a tool call → server emits a `tool_call_confirmation` event
2. Client reviews and sends `POST /api/sessions/:id/resume` with the decision
3. Execution continues based on approval/denial
Toggle auto-approve with `POST /api/sessions/:id/tools/toggle` for automated workflows.
## Driving a running TUI with `--listen` {#listen}
The same session API can be exposed by an **interactive run** so an external
process can drive it — send follow-up prompts, observe progress, read the
title — without scraping the terminal. Start a normal run and add `--listen`:
```bash
# Expose this run's control plane on a TCP port...
$ docker agent run agent.yaml --listen 127.0.0.1:8080
# ...or on a unix socket (no port to allocate; access is gated by file
# permissions). npipe:// (Windows) and fd:// are also accepted.
$ docker agent run agent.yaml --listen unix:///tmp/my-run.sock
```
The run keeps its interactive TUI; the control plane runs alongside it. A
follow-up delivered over HTTP is processed exactly as if it had been typed
into the TUI: it starts a turn even when the agent is idle, generates the
session title on the first turn, and streams the resulting events to both the
terminal and every connected API client.
```bash
# Send a follow-up to the attached run (SID is the --session id):
$ curl -X POST http://127.0.0.1:8080/api/sessions/$SID/followup \
-H 'Content-Type: application/json' \
-d '{"messages":[{"content":"Now add tests"}]}'
```
> [!NOTE]
> **Discovering a run**
>
> Each run started with `--listen` writes a discovery record to `<data-dir>/runs/<pid>.json` containing its address and session id, so a supervising process can find a live run by session id, pid, or address.
## Session event stream and reconnection
`GET /api/sessions/:id/events` is a **Server-Sent Events** stream of the
session's runtime events — `stream_started`, `agent_choice`, `tool_call`,
`session_title`, `token_usage`, `stream_stopped`, and so on. Unlike the
per-request stream returned by the agent-execution endpoint, it is
session-scoped and survives across turns, so a client can watch a session for
its whole lifetime. It is available for a run attached via
[`--listen`](#listen).
Each event carries a monotonic **sequence number** in the SSE `id:` field, and
the server buffers recent events. This makes the stream resumable:
- **Resume after a drop** — reconnect with the standard `Last-Event-ID` header
(sent automatically by browser `EventSource` clients) or a `?since=<seq>`
query parameter. Buffered events newer than that point are replayed before
live tailing resumes, so nothing is missed.
- **Gap signal** — if the resume point has already fallen out of the buffer, the
server sends a single `{"type":"gap"}` event (with no id) before the replay.
The client should re-fetch the snapshot to resync, then continue tailing.
- **End of session** — when the session is ended server-side (for example via
`DELETE /api/sessions/:id`) the server sends a terminal
`{"type":"session_exited"}` event and closes the stream; a client that
receives it should stop. A stream that closes **without** `session_exited` is
a dropped connection — including the run process itself exiting — so reconnect
with the last id; if the run is gone the reconnection simply fails.
### Reconnecting without gaps
`GET /api/sessions/:id/snapshot` returns the session's full state in one
response — stored fields (messages, tokens, permissions), live runtime state
(`streaming`, current `agent`), and `last_event_seq`: the sequence number of
the most recent event. Pair it with the event stream for an exact, gapless
resync:
```bash
# 1. Read the full state and the stream position it corresponds to.
$ SEQ=$(curl -s http://127.0.0.1:8080/api/sessions/$SID/snapshot | jq .last_event_seq)
# 2. Tail everything that happens after that point (replaying anything that
# occurred between the two calls).
$ curl -N "http://127.0.0.1:8080/api/sessions/$SID/events?since=$SEQ"
```
This snapshot-then-tail pattern lets a client (or a client that just
restarted) rebuild a session's state and keep it correct without polling.
### Waiting for readiness
`GET /api/sessions/:id/status` reports a session's runtime state. Add
`?wait=<duration>` (e.g. `?wait=10s`) to block until that specific session's
runtime is attached and ready to accept follow-ups, then return its status, or
`503` on timeout. This is session-scoped, unlike `GET /api/ready`, which fires
as soon as any session is ready.
## Session Forking
`POST /api/sessions/:id/fork` creates a new session whose history is a copy of the parent up to (but **excluding**) a specified user message. This lets a client "branch" a conversation — e.g. rewind to an earlier question and try a different prompt — without losing the shared history that came before.
**Request body:**
```json
{ "user_message_index": 1 }
```
`user_message_index` is a **0-based ordinal** that counts only user-role messages in the parent's flat, user-visible message list. The targeted user message is **excluded** from the fork so clients can prefill it into their chat input for the user to edit and resubmit.
**Example:**
```bash
# Fork a session before the second user message (ordinal 1)
$ curl -X POST http://localhost:8080/api/sessions/$SID/fork \
-H 'Content-Type: application/json' \
-d '{"user_message_index": 1}'
# Returns: api.SessionResponse for the new forked session
# New session title: "<parent title> (fork 1)", "(fork 2)", etc.
```
**Validation:**
- Out-of-range ordinals (negative, or at/past the user-message count) return `400 Bad Request`.
- An ordinal that resolves to a user message inside a sub-session returns `400 Bad Request`. A sub-session is a nested session created when a multi-agent config delegates work to a child agent; its messages are embedded within the parent session's message list and cannot be used as a fork boundary.
## Idempotent follow-ups
`POST /api/sessions/:id/followup` accepts an optional `Idempotency-Key`
header, making the request safe to retry after a network timeout. A repeat
with a key already seen for the session is acknowledged without delivering the
follow-up again:
```bash
$ curl -X POST http://127.0.0.1:8080/api/sessions/$SID/followup \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: 7f3a-...' \
-d '{"messages":[{"content":"Ship it"}]}'
# => {"status":"queued_streaming","duplicate":false}
# A retry with the same key => {"status":"duplicate","duplicate":true}
```
The response `status` is `queued_streaming` (a turn is running or starting),
`queued_idle` (delivered to an idle headless session, runs on the next turn),
or `duplicate`.
> [!NOTE]
> **See also**
>
> For interactive use, see the [Terminal UI](../tui/index.md). For agent-to-agent communication, see [A2A Protocol](../a2a/index.md) and [ACP](../acp/index.md). For MCP integration, see [MCP Mode](../mcp-mode/index.md). For an OpenAI-compatible chat-completions API, see the [Chat Server](../chat-server/index.md).

View File

@@ -0,0 +1,231 @@
---
title: "Chat Server"
description: "Expose your agents through an OpenAI-compatible Chat Completions API so any tool that already speaks OpenAI can drive a docker-agent agent."
keywords: docker agent, ai agents, features, chat server
weight: 90
---
_Expose your agents through an OpenAI-compatible Chat Completions API so any tool that already speaks OpenAI can drive a docker-agent agent._
## Overview
The `docker agent serve chat` command starts an HTTP server that exposes one or
more agents through an **OpenAI-compatible Chat Completions API** at
`/v1/chat/completions` and `/v1/models`. Any client that already speaks the
OpenAI protocol — for example
[Open WebUI](https://github.com/open-webui/open-webui), `curl`, the OpenAI
Python SDK, or LangChain — can drive a docker-agent agent without any custom
integration.
```bash
# Single agent — exposed as the model `root`
$ docker agent serve chat agent.yaml
# Multi-agent config — every agent in the team becomes a model
$ docker agent serve chat ./team.yaml
# Pick a specific agent from a multi-agent config
$ docker agent serve chat ./team.yaml --agent reviewer
# Run an agent straight from the registry
$ docker agent serve chat agentcatalog/pirate --listen 127.0.0.1:9090
# Require a Bearer token, sourced from an env var
$ docker agent serve chat agent.yaml --api-key-env CHAT_BEARER_TOKEN
```
> [!TIP]
> **When to use chat server vs. API server**
>
> Use the **chat server** when you want to plug docker-agent into existing OpenAI-compatible tooling (chat UIs, IDE integrations, OpenAI SDK clients). Use the [API server](../api-server/index.md) when you want full control over sessions, agent execution, tool-call confirmations, and streamed runtime events.
## Endpoints
The OpenAI-compatible endpoints live under the `/v1` prefix to match the
OpenAI API surface. The OpenAPI specification is served at the top level so it
can be discovered without authentication.
| Method | Path | Description |
| ------ | ---------------------- | ---------------------------------------------------------------------- |
| `GET` | `/v1/models` | List the agents that this server exposes as models |
| `POST` | `/v1/chat/completions` | Send messages and receive a completion (regular or streaming) |
| `GET` | `/openapi.json` | OpenAPI specification for the chat server |
The model identifier in `POST /v1/chat/completions` is the **agent name**.
For a single-agent config that's typically `root`; for a multi-agent config,
each named agent becomes its own selectable model.
## Quick Start
```bash
# 1. Start the server
$ docker agent serve chat agent.yaml
Listening on 127.0.0.1:8083
OpenAI-compatible chat completions endpoint: http://127.0.0.1:8083/v1/chat/completions
# 2. List exposed agents (models)
$ curl http://127.0.0.1:8083/v1/models
{"object":"list","data":[{"id":"root","object":"model","owned_by":"docker-agent"}]}
# 3. Send a chat request
$ curl http://127.0.0.1:8083/v1/chat/completions \
-H 'Content-Type: application/json' \
-d '{
"model": "root",
"messages": [{"role": "user", "content": "Hello!"}]
}'
```
### Streaming
Set `"stream": true` in the request body to receive a Server-Sent Events
(SSE) stream of OpenAI-format `chat.completion.chunk` deltas:
```bash
$ curl -N http://127.0.0.1:8083/v1/chat/completions \
-H 'Content-Type: application/json' \
-d '{
"model": "root",
"stream": true,
"messages": [{"role": "user", "content": "Stream a poem"}]
}'
```
### Drive it from the OpenAI Python SDK
Because the wire format is OpenAI-compatible, point any OpenAI client at the
chat server's `base_url` and use the agent name as the model:
```python
from openai import OpenAI
client = OpenAI(
base_url="http://127.0.0.1:8083/v1",
api_key="not-needed-when-no-api-key-flag", # required by the SDK, ignored if no auth
)
resp = client.chat.completions.create(
model="root",
messages=[{"role": "user", "content": "Hello!"}],
)
print(resp.choices[0].message.content)
```
## Server-side Conversation Caching
By default the server is **stateless**: every request must contain the full
message history, exactly like OpenAI's API. Enable server-side caching by
setting `--conversations-max` to a positive value, then send a stable
`X-Conversation-Id` header on each request:
```bash
$ docker agent serve chat agent.yaml --conversations-max 100 --conversation-ttl 30m
```
```bash
$ curl http://127.0.0.1:8083/v1/chat/completions \
-H 'Content-Type: application/json' \
-H 'X-Conversation-Id: my-thread-1' \
-d '{
"model": "root",
"messages": [{"role": "user", "content": "Remember my name is Alice"}]
}'
$ curl http://127.0.0.1:8083/v1/chat/completions \
-H 'Content-Type: application/json' \
-H 'X-Conversation-Id: my-thread-1' \
-d '{
"model": "root",
"messages": [{"role": "user", "content": "What is my name?"}]
}'
```
Cached conversations are evicted after `--conversation-ttl` of inactivity, or
when the cache hits `--conversations-max` items (oldest entries are evicted
first).
### Failure-safe caching
When a request fails — for example because the model returns an error or the `--request-timeout` expires — the conversation cache is **not updated**. The server clones the cached session before processing each request and only commits the updated session when the turn completes successfully. This means:
- A failed turn leaves the conversation in the same state it was before the request.
- Clients can safely retry with the same `X-Conversation-Id` after a failure.
- Transient errors do not corrupt the conversation history.
## Authentication
The chat server has **no authentication by default**. To require a Bearer
token, pass `--api-key` (literal value) or `--api-key-env` (name of an
environment variable that holds the value):
```bash
$ docker agent serve chat agent.yaml --api-key-env CHAT_BEARER_TOKEN
```
Clients must then send an `Authorization: Bearer <token>` header on every
request to `/v1/*`. Both `/v1/models` and `/v1/chat/completions` are
protected once a key is set.
> [!WARNING]
> **Public exposure**
>
> The default listen address is `127.0.0.1:8083`. If you bind to a non-loopback address, always set `--api-key` or `--api-key-env` — there is no other authentication layer.
## CORS
CORS is **disabled by default**. To allow a browser-based client to call the
server, set `--cors-origin` to the exact origin (scheme + host + port) that
should be allowed:
```bash
$ docker agent serve chat agent.yaml --cors-origin https://my-ui.example.com
```
## CLI Flags
```bash
docker agent serve chat <agent-file>|<registry-ref> [flags]
```
| Flag | Default | Description |
| ----------------------------- | ------------------ | ----------------------------------------------------------------------------------------------------------------- |
| `-a, --agent <name>` | (all agents) | Name of the agent to expose. If omitted, every agent in the config is exposed as a separate model. |
| `-l, --listen <addr>` | `127.0.0.1:8083` | Address to listen on. |
| `--cors-origin <origin>` | (none) | Allowed CORS origin (e.g. `https://example.com`). Empty disables CORS. |
| `--api-key <token>` | (none) | Required Bearer token clients must present (`Authorization: Bearer <token>`). Empty disables auth. |
| `--api-key-env <name>` | (none) | Read the API key from this environment variable instead of the command line. |
| `--max-request-size <bytes>` | `1048576` (1 MiB) | Maximum request body size. |
| `--request-timeout <dur>` | `5m` | Per-request timeout (covers model + tool calls + streaming). |
| `--conversations-max <n>` | `0` | Cache up to N conversations server-side, keyed by `X-Conversation-Id`. `0` disables — clients must resend history. |
| `--conversation-ttl <dur>` | `30m` | Idle TTL after which a cached conversation is evicted. |
| `--max-idle-runtimes <n>` | `4` | Maximum number of idle runtimes pooled per agent. `0` disables pooling. |
All [runtime configuration flags](../cli/index.md#runtime-configuration-flags)
(`--working-dir`, `--env-from-file`, `--models-gateway`, `--hook-*`, …) are
also accepted.
## Open WebUI Integration
Open WebUI can talk to any OpenAI-compatible endpoint. To plug docker-agent
in:
1. Start the chat server, optionally with auth:
```bash
$ docker agent serve chat agent.yaml \
--listen 127.0.0.1:8083 \
--cors-origin http://localhost:3000 \
--api-key-env OPEN_WEBUI_TOKEN
```
2. In Open WebUI, add an OpenAI-compatible connection:
- **API Base URL:** `http://127.0.0.1:8083/v1`
- **API Key:** the value of `OPEN_WEBUI_TOKEN`
3. Each agent in your config appears as a selectable model.
> [!NOTE]
> **See also**
>
> For the docker-agent–native HTTP API (sessions, tool-call confirmation, runtime events), see the [API Server](../api-server/index.md). For full CLI flag documentation, see the [CLI Reference](../cli/index.md#docker-agent-serve-chat).

View File

@@ -0,0 +1,570 @@
---
title: "CLI Reference"
description: "Complete reference for all docker-agent command-line commands and flags."
keywords: docker agent, ai agents, features, cli reference
weight: 30
aliases:
- /ai/docker-agent/reference/cli/
---
_Complete reference for all docker-agent command-line commands and flags._
> [!TIP]
> **No config needed**
>
> Running `docker agent run` without a config file uses a built-in default agent. Perfect for quick experimentation.
## Commands
### `docker agent run`
Launch the interactive TUI with an agent configuration (`.yaml`, `.yml`, or `.hcl`).
```bash
$ docker agent run [config] [message...] [flags]
```
| Flag | Description |
| --------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| `-a, --agent <name>` | Run a specific agent from the config |
| `--yolo` | Auto-approve all tool calls |
| `--model <ref>` | Override model(s). Use `provider/model` for all agents, or `agent=provider/model` for specific agents. Comma-separate multiple overrides. |
| `--session <id>` | Resume a previous session. Supports relative refs (`-1` = last, `-2` = second to last). An explicit ID that does not exist yet is created with that ID, so a supervisor can own the session ID upfront and reuse it across runs. |
| `-s, --session-db <path>` | Path to the SQLite session database (default: `~/.cagent/session.db`) |
| `--session-read-only` | Open the TUI in read-only mode: conversation history is displayed but no new messages can be sent to the LLM. Cannot be used with `--exec`. |
| `--prompt-file <path>` | Include file contents as additional system context (repeatable) |
| `--attach <path>` | Attach an image file to the initial message |
| `--dry-run` | Initialize the agent without executing anything (useful for validating a config) |
| `--remote <addr>` | Use a remote runtime at the given address instead of running the agent locally |
| `--listen <addr>` | Expose this run's control plane over HTTP so an external process can drive the running TUI (send follow-ups, stream events, read the title). Accepts `host:port` or `unix://`, `npipe://`, `fd://`. See the [API Server](../api-server/index.md#listen). |
| `--lean` | Use a simplified, non-alternate-screen TUI. Unlike the default full-screen TUI, this renders inline in the normal terminal buffer — useful in environments where an alternate screen is unwanted (e.g. inside tmux panes, CI with a tty, or log-friendly pipelines). Displays an ASCII art banner on startup. |
| `--app-name <name>` | Override the application name label shown in the TUI (status bar, window title, "/exit" notifications). |
| `--sidebar` | Control sidebar visibility. Set to `--sidebar=false` to hide the sidebar and disable the Ctrl+B toggle (default: `true`). |
| `--disable-commands <list>` | Hide and disable specific slash commands in the TUI. Accepts a comma-separated list of command names (leading slash optional, case-insensitive). E.g. `--disable-commands="/cost,/eval,/model"`. |
| `--theme <name>` | Preselect a TUI theme by name (overrides the theme from user config; ignored outside the interactive TUI) |
| `--on-event <type>=<cmd>` | Run a shell command when an event of the given type fires (`*=<cmd>` matches any event). Repeatable. |
| `--json` | Output results as newline-delimited JSON (use with `--exec`) |
| `--hide-tool-calls` | Hide tool calls in the output |
| `--hide-tool-results` | Hide tool call results in the output |
| `--sandbox` | Run the agent inside a Docker sandbox (see [Sandbox](../../configuration/sandbox/index.md)) |
| `--template <image>` | Template image for the sandbox (default: `docker/sandbox-templates:docker-agent`) |
| `--sbx` | Prefer the `sbx` CLI backend when available (default `true`; set `--sbx=false` to force `docker sandbox`) |
| `--no-kit` | Disable the [auto-kit](../../configuration/sandbox/index.md#auto-kit): do not stage skills or prompt files into the sandbox |
| `--agent-picker [refs]` | Show a full-screen interactive picker before launching, letting you browse and select an agent. Accepts an optional comma-separated list of agent references to show (defaults to `default,coder`). Arrow keys navigate; `?` toggles the YAML preview panel; Enter confirms. Not available in `--exec` or non-TTY modes. |
| `-w, --worktree [name]` | Run the agent in a fresh git worktree of the working directory, isolating its changes from your checkout. Optionally name it (`--worktree=my-feature`); otherwise a name is generated. Requires the working directory to be inside a git repository. Every tool (the shell included) runs inside the worktree. Combine with `--working-dir` to branch from another repository, and with `--session` to resume into the same worktree later. Cannot be combined with `--remote` or `--sandbox`. When the session ends, a clean worktree is removed automatically; one with work prompts to keep or remove (never in `--exec`). |
| `--worktree-base <ref>` | Branch the `--worktree` from `<ref>` (a branch, tag, commit, or remote-tracking ref like `origin/main`) instead of the current `HEAD`. A remote-tracking ref is fetched first so the worktree starts from the latest remote state. Requires `--worktree`; cannot be combined with `--worktree-pr`, `--remote`, or `--sandbox`. |
| `--worktree-pr <number\|url>` | Run the agent in a git worktree checked out on an existing GitHub pull request (PR number, `#123`, or PR URL). Continues the PR's branch so commits push back to it. Requires the [GitHub CLI](https://cli.github.com/) (`gh`). Cannot be combined with `--worktree`, `--remote`, or `--sandbox`. |
| `--working-dir <path>` | Set the working directory for the session (applies to tools and relative paths) |
| `--env-from-file <path>` | Load environment variables from file (repeatable) |
| `--code-mode-tools` | Provide a single tool to call other tools via JavaScript (forces code-mode tools globally) |
| `--models-gateway <addr>` | Route model traffic through a gateway. Also reads `DOCKER_AGENT_MODELS_GATEWAY` (legacy `CAGENT_MODELS_GATEWAY`) env var. |
| `--hook-pre-tool-use <cmd>` | Add a pre-tool-use hook command (repeatable). See [Hooks](../../configuration/hooks/index.md). |
| `--hook-post-tool-use <cmd>` | Add a post-tool-use hook command (repeatable) |
| `--hook-session-start <cmd>` | Add a session-start hook command (repeatable) |
| `--hook-session-end <cmd>` | Add a session-end hook command (repeatable) |
| `--hook-on-user-input <cmd>` | Add an on-user-input hook command (repeatable) |
| `--hook-stop <cmd>` | Add a stop hook command, fired when the model finishes responding (repeatable) |
| `--fake <path>` | Replay AI responses from a cassette file (for testing). Mutually exclusive with `--record`. |
| `--fake-stream [ms]` | When replaying with `--fake`, simulate streaming with a delay between chunks (defaults to 15ms when given without a value). |
| `--record [path]` | Record AI API interactions to a cassette file (auto-generates filename if no path given) |
| `-d, --debug` | Enable debug logging |
| `--log-file <path>` | Custom debug log location |
| `-o, --otel` | Enable OpenTelemetry observability: traces, metrics, and logs. Requires `OTEL_EXPORTER_OTLP_ENDPOINT` to export to a collector. |
```bash
# Examples
$ docker agent run agent.yaml
$ docker agent run agent.yaml "Fix the bug in auth.go"
$ docker agent run agent.yaml -a developer --yolo
$ docker agent run agent.yaml --model anthropic/claude-sonnet-4-5
$ docker agent run agent.yaml --model "dev=openai/gpt-4o,reviewer=anthropic/claude-sonnet-4-5"
$ docker agent run agent.yaml --session -1 # resume last session
$ docker agent run agent.yaml --session -1 --session-read-only # review last session without sending messages
$ docker agent run agent.yaml --prompt-file ./context.md # include file as context
# Add hooks from the command line
$ docker agent run agent.yaml --hook-session-start "./scripts/setup-env.sh"
$ docker agent run agent.yaml --hook-pre-tool-use "./scripts/validate.sh" --hook-post-tool-use "./scripts/log.sh"
# Queue multiple messages (processed in sequence)
$ docker agent run agent.yaml "question 1" "question 2" "question 3"
# Customize TUI display
$ docker agent run agent.yaml --app-name "My Project"
$ docker agent run agent.yaml --sidebar=false
$ docker agent run agent.yaml --disable-commands="/cost,/eval,/model"
# Browse and pick an agent interactively
$ docker agent run --agent-picker
$ docker agent run --agent-picker=agentcatalog/coder,agentcatalog/researcher
```
> [!TIP]
> **Lean, inline TUI**
>
> Pass `--lean` to get a lightweight TUI that renders inline in your terminal (no alternate screen). It displays an ASCII art banner on startup and supports the same slash commands and streaming output as the full TUI, making it handy inside tmux, scripts, or any context where a full-screen takeover is unwanted.
> [!TIP]
> **Isolate a run in a git worktree**
>
> When the working directory is inside a git repository, `--worktree` creates a fresh [git worktree](https://git-scm.com/docs/git-worktree) and points the session at it, so the agent's edits land on a separate branch and never touch your checkout. Every tool — the shell included — runs inside the worktree. The worktree is stored under `<data-dir>/worktrees/<name>` on a branch named `worktree-<name>`.
```bash
# Run in an isolated worktree with a generated name (e.g. "focused_turing")
$ docker agent run agent.yaml --worktree
$ docker agent run agent.yaml -w "Refactor the auth package"
# Give the worktree (and its branch) an explicit name
$ docker agent run agent.yaml --worktree=auth-refactor
# Branch the worktree from another ref instead of the current HEAD.
# A remote-tracking ref is fetched first, so the worktree starts from the
# latest remote state.
$ docker agent run agent.yaml --worktree=auth-refactor --worktree-base origin/main
# Resume a worktree run later: the session remembers its worktree, so you
# don't pass --worktree again (which would fail — the worktree already exists).
$ docker agent run agent.yaml --worktree=auth-refactor # first run, creates it
$ docker agent run agent.yaml --session -1 # resumes into the same worktree
# Check out an existing GitHub pull request to continue it (requires gh)
$ docker agent run agent.yaml --worktree-pr 123
$ docker agent run agent.yaml --worktree-pr https://github.com/owner/repo/pull/123
```
With `--worktree-pr`, the PR's head branch is checked out tracking its remote (via the [GitHub CLI](https://cli.github.com/)), so commits made during the run push straight back to the pull request. The worktree is stored under `<data-dir>/worktrees/pr-<number>`.
When the interactive session ends, the worktree is cleaned up based on its state:
- **Clean** (no uncommitted changes, untracked files, or new commits): the worktree and its branch are removed automatically.
- **Has work** (uncommitted changes, untracked files, or new commits): you're prompted to keep or remove it. Keeping preserves the directory and branch so you can return later; removing discards the worktree, its branch, and all that work.
- **Non-interactive runs** (`--exec`): the worktree is never cleaned up — it's left in place for inspection.
A worktree is only ever removed if `--worktree` created it for this run; a pre-existing worktree is never touched.
A kept worktree can be resumed: pass `--session` (a relative ref like `-1`, or the session id) and the run reattaches to the same worktree directory and branch automatically. Don't re-pass `--worktree` on resume — that would try to create a new worktree and fail because it already exists.
### `docker agent run --exec`
Run an agent in non-interactive (headless) mode. No TUI — output goes to stdout.
```bash
$ docker agent run --exec [config] [message...] [flags]
```
```bash
# One-shot task
$ docker agent run --exec agent.yaml "Create a Dockerfile for a Python Flask app"
# With auto-approve
$ docker agent run --exec agent.yaml --yolo "Set up CI/CD pipeline"
# Multi-turn conversation
$ docker agent run --exec agent.yaml "question 1" "question 2" "question 3"
```
### `docker agent new`
Interactively generate a new agent configuration file.
```bash
$ docker agent new [flags]
# Examples
$ docker agent new
$ docker agent new --model openai/gpt-5
$ docker agent new --model dmr/ai/gemma3-qat:12B --max-iterations 15
```
### `docker agent models`
List models available for use with `--model`. By default only shows models for providers you have credentials for. Aliases: `docker agent models list`, `docker agent models ls`.
```bash
$ docker agent models [flags]
```
| Flag | Default | Description |
| ---------------------- | ------- | ---------------------------------------------------------------------------------- |
| `-p, --provider <id>` | (none) | Filter models by provider name (e.g. `openai`, `anthropic`, `dmr`, `ollama`, …). |
| `--format <fmt>` | `table` | Output format: `table` or `json`. |
| `-a, --all` | `false` | Include models from all providers, not just those you have credentials for. |
```bash
# Examples
$ docker agent models # only providers you can use
$ docker agent models --all # every provider the catalog knows about
$ docker agent models --provider openai
$ docker agent models --format json | jq
```
### `docker agent serve api`
Start the HTTP API server for programmatic access. The argument can be a single agent file, a registry reference, or a directory — when given a directory, every `.yaml`/`.yml`/`.hcl` file in it is exposed as a separate entry under `/api/agents`.
```bash
$ docker agent serve api <agent-file>|<agents-dir>|<registry-ref> [flags]
```
| Flag | Default | Description |
| -------------------------- | ------------------ | ---------------------------------------------------------------------------------------------------------- |
| `-l, --listen <addr>` | `127.0.0.1:8080` | Address to listen on. |
| `--auth-token <token>` | (none) | Bearer token required for all API requests. When set, every request must include `Authorization: Bearer <token>`. Leave empty to disable authentication (safe when listening on loopback interfaces only). |
| `-s, --session-db <path>` | `session.db` | Path to the SQLite session database (relative paths resolve against the working directory). |
| `--pull-interval <minutes>`| `0` | Periodically re-pull OCI/URL references and refresh the agent definition. `0` disables auto-pull. |
| `--fake <path>` | (none) | Replay AI responses from a cassette file (for testing). Mutually exclusive with `--record`. |
| `--record [path]` | (none) | Record AI API interactions to a cassette file. |
| `--mcp-oauth-redirect-uri <url>` | (none) | OAuth redirect URI for the unmanaged MCP OAuth flow in server mode. When set, the runtime drives PKCE and code exchange in-process and sends the full authorize URL to the client via elicitation. See [Remote MCP](../remote-mcp/index.md) for details. |
> **Diagnostics:** Set `CAGENT_PPROF_ADDR=127.0.0.1:6060` (or `--pprof-addr`, a hidden flag) to start a live Go pprof server at `/debug/pprof/`. Use a loopback address; a non-loopback binding logs a security warning.
All [runtime configuration flags](#runtime-configuration-flags) (`--working-dir`, `--env-from-file`, `--models-gateway`, `--hook-*`, …) are also accepted.
```bash
# Examples
$ docker agent serve api agent.yaml
$ docker agent serve api agent.yaml --listen :8080
$ docker agent serve api ./agents/ # directory of agent YAML/HCL configs
$ docker agent serve api ociReference --pull-interval 10 # auto-refresh
```
See [API Server](../api-server/index.md) for the full HTTP API reference.
### `docker agent serve mcp`
Expose agents as MCP tools for use in Claude Desktop, Claude Code, or other MCP clients. Defaults to stdio transport; use `--http` to start a streaming HTTP server instead.
```bash
$ docker agent serve mcp <config> [flags]
```
| Flag | Default | Description |
| ---------------------- | ------------------ | ------------------------------------------------------------------------------------------------- |
| `-a, --agent <name>` | (all agents) | Name of the agent to expose. If omitted, every agent in the config is exposed as a separate tool. |
| `--tool-name <name>` | (agent name) | Override the MCP tool identifier clients call; only valid when exposing a single agent. |
| `--http` | `false` | Use streaming HTTP transport instead of stdio. |
| `-l, --listen <addr>` | `127.0.0.1:8081` | Address to listen on (only used with `--http`). |
| `--mcp-keepalive <dur>`| `0` (disabled) | Interval between MCP keep-alive pings (e.g. `30s`). |
| `--attach [target]` | (none) | Attach to a running TUI run by pid, address, or session id; given without a value, selects the most recent run. |
All [runtime configuration flags](#runtime-configuration-flags) are also accepted.
```bash
# Examples
$ docker agent serve mcp agent.yaml # stdio transport
$ docker agent serve mcp agent.yaml --http --listen 127.0.0.1:9090 # streaming HTTP
$ docker agent serve mcp agent.yaml --working-dir /path/to/project
$ docker agent serve mcp agentcatalog/coder
```
See [MCP Mode](../mcp-mode/index.md) for detailed setup.
### `docker agent serve a2a`
Start an A2A (Agent-to-Agent) protocol server.
```bash
$ docker agent serve a2a <config> [flags]
```
| Flag | Default | Description |
| ---------------------- | ------------------ | ------------------------------------------------------------------------------------------ |
| `-a, --agent <name>` | (team default) | Name of the agent to run. Defaults to the team's first agent if not specified. |
| `-l, --listen <addr>` | `127.0.0.1:8082` | Address to listen on. |
All [runtime configuration flags](#runtime-configuration-flags) are also accepted.
```bash
# Examples
$ docker agent serve a2a agent.yaml
$ docker agent serve a2a agent.yaml --listen 127.0.0.1:9000
$ docker agent serve a2a agentcatalog/pirate
```
### `docker agent serve acp`
Start an ACP (Agent Client Protocol) server over stdio. This allows external clients to interact with your agents using the ACP protocol.
```bash
$ docker agent serve acp <config> [flags]
```
| Flag | Default | Description |
| ------------------------- | --------------------------- | ------------------------------------------------- |
| `-s, --session-db <path>` | `~/.cagent/session.db` | Path to the SQLite session database. |
All [runtime configuration flags](#runtime-configuration-flags) are also accepted.
```bash
# Examples
$ docker agent serve acp agent.yaml
$ docker agent serve acp ./team.yaml
$ docker agent serve acp agentcatalog/pirate
```
See [ACP](../acp/index.md) for details on the Agent Client Protocol.
### `docker agent serve chat`
Start an HTTP server that exposes one or more agents through an **OpenAI-compatible Chat Completions API** at `/v1/chat/completions` and `/v1/models`. This lets any tool that already speaks the OpenAI protocol — for example [Open WebUI](https://github.com/open-webui/open-webui), `curl`, the OpenAI Python SDK, or LangChain — drive a docker-agent agent without any custom integration.
```bash
$ docker agent serve chat <config> [flags]
```
| Flag | Default | Description |
| ----------------------------- | ------------------ | ----------------------------------------------------------------------------------------------------------------- |
| `-a, --agent <name>` | (all agents) | Name of the agent to expose. If omitted, every agent in the config is exposed as a separate model. |
| `-l, --listen <addr>` | `127.0.0.1:8083` | Address to listen on. |
| `--cors-origin <origin>` | (none) | Allowed CORS origin (e.g. `https://example.com`). Empty disables CORS. |
| `--api-key <token>` | (none) | Required Bearer token clients must present (`Authorization: Bearer <token>`). Empty disables auth. |
| `--api-key-env <name>` | (none) | Read the API key from this environment variable instead of the command line. |
| `--max-request-size <bytes>` | `1048576` (1 MiB) | Maximum request body size. |
| `--request-timeout <dur>` | `5m` | Per-request timeout (covers model + tool calls + streaming). |
| `--conversations-max <n>` | `0` | Cache up to N conversations server-side, keyed by `X-Conversation-Id`. `0` disables — clients must resend history. |
| `--conversation-ttl <dur>` | `30m` | Idle TTL after which a cached conversation is evicted. |
| `--max-idle-runtimes <n>` | `4` | Maximum number of idle runtimes pooled per agent. `0` disables pooling. |
```bash
# Examples
$ docker agent serve chat agent.yaml
$ docker agent serve chat ./team.yaml --agent reviewer
$ docker agent serve chat agentcatalog/pirate --listen 127.0.0.1:9090
$ docker agent serve chat agent.yaml --api-key-env CHAT_BEARER_TOKEN
# Drive it from any OpenAI-compatible client
$ curl http://127.0.0.1:8083/v1/chat/completions \
-H 'Content-Type: application/json' \
-d '{"model": "root", "messages": [{"role": "user", "content": "hello"}]}'
```
See [Chat Server](../chat-server/index.md) for the full feature reference.
### `docker agent share push` / `docker agent share pull`
Share agents via OCI registries.
```bash
# Push an agent
$ docker agent share push ./agent.yaml docker.io/username/my-agent:latest
# Pull an agent
$ docker agent share pull docker.io/username/my-agent:latest
# Force pull, overwriting the local copy
$ docker agent share pull docker.io/username/my-agent:latest --force
```
| Flag | Applies to | Description |
| ---------- | ---------- | ---------------------------------------------------------- |
| `--force` | `pull` | Force pull even if the configuration already exists locally |
See [Agent Distribution](../../concepts/distribution/index.md) for full registry workflow details.
### `docker agent eval`
Run agent evaluations against a directory of recorded sessions.
```bash
$ docker agent eval <agent-file>|<registry-ref> [<eval-dir>|./evals] [flags]
```
| Flag | Default | Description |
| ------------------- | ------------------------------------ | -------------------------------------------------------------------------- |
| `-c, --concurrency` | num CPUs | Number of concurrent evaluation runs |
| `--judge-model` | `anthropic/claude-opus-4-5-20251101` | Model for LLM-as-a-judge relevance scoring (format: `provider/model`) |
| `--output <dir>` | `<eval-dir>/results` | Directory for results, logs, and session databases |
| `--only <pattern>` | (all) | Only run evals with file names matching these patterns (repeatable) |
| `--base-image` | (default) | Custom base Docker image for eval containers |
| `--keep-containers` | `false` | Keep containers after evaluation (don't remove with `--rm`) |
| `-e, --env` | (none) | Environment variables to pass to container (`KEY` or `KEY=VALUE`, repeatable) |
| `--repeat <n>` | `1` | Number of times to repeat each evaluation (useful for computing baselines) |
All [runtime configuration flags](#runtime-configuration-flags) are also accepted.
```bash
# Examples
$ docker agent eval agent.yaml # use ./evals
$ docker agent eval agent.yaml ./my-evals # custom directory
$ docker agent eval agent.yaml -c 8 # 8 concurrent evaluations
$ docker agent eval agent.yaml --keep-containers # keep containers for debugging
$ docker agent eval agent.yaml --only "auth*" # only run matching evals
$ docker agent eval agent.yaml --repeat 5 # repeat each eval 5 times
```
See [Evaluation](../evaluation/index.md) for details on creating eval sessions and interpreting results.
### `docker agent version`
Print the version and commit hash for your `docker-agent` install.
```bash
$ docker agent version
docker agent version v1.54.0
Commit: 1737035c
```
### `docker agent alias`
Manage agent aliases for quick access.
```bash
# List aliases
$ docker agent alias ls
# List aliases as JSON
$ docker agent alias list --json
# Add an alias
$ docker agent alias add pirate /path/to/pirate.yaml
$ docker agent alias add other ociReference
# Add an alias with runtime options
$ docker agent alias add yolo-coder agentcatalog/coder --yolo
$ docker agent alias add fast-coder agentcatalog/coder --model openai/gpt-4o-mini
$ docker agent alias add safe-coder agentcatalog/coder --sandbox
$ docker agent alias add turbo agentcatalog/coder --yolo --model anthropic/claude-sonnet-4-5
# Use an alias
$ docker agent run pirate
$ docker agent run yolo-coder
```
**Alias Options:** Aliases can include runtime options that apply automatically when used:
- `--yolo` — Auto-approve all tool calls when running the alias
- `--model <ref>` — Override the model for the alias
- `--hide-tool-results` — Hide tool call results in the TUI when running the alias
- `--sandbox` — Always run the alias inside a [Docker sandbox](../../configuration/sandbox/index.md)
When listing aliases, options are shown in brackets:
```bash
$ docker agent alias ls
Registered aliases (3):
fast-coder → agentcatalog/coder [model=openai/gpt-4o-mini]
turbo → agentcatalog/coder [yolo, model=anthropic/claude-sonnet-4-5]
yolo-coder → agentcatalog/coder [yolo]
Run an alias with: docker agent run <alias>
```
Pass `--json` to output aliases as a JSON array instead of the formatted table. Each entry includes the alias `name` and its options:
```bash
$ docker agent alias list --json
[
{
"name": "fast-coder",
"path": "agentcatalog/coder",
"model": "openai/gpt-4o-mini"
},
{
"name": "turbo",
"path": "agentcatalog/coder",
"yolo": true,
"model": "anthropic/claude-sonnet-4-5"
},
{
"name": "yolo-coder",
"path": "agentcatalog/coder",
"yolo": true
}
]
```
JSON output is sorted by name and omits false/zero-valued fields. This is useful for scripting and automation.
> [!TIP]
> **Override alias options**
>
> Command-line flags override alias options. For example, `docker agent run yolo-coder --yolo=false` disables yolo mode even though the alias has it enabled.
> [!TIP]
> **Set a default agent**
>
> Create a `default` alias to customize what `docker agent` starts with no arguments:
>
> ```console
> $ docker agent alias add default /my/default/agent.yaml
> ```
>
> Then simply run `docker agent` — it will launch that agent automatically.
### `docker agent sandbox`
Manage settings shared by every [`--sandbox`](../../configuration/sandbox/index.md) run — today, the persistent network allowlist that turns a `Blocked by network policy` 403 into a one-line, durable fix:
```bash
# Allow a host on every subsequent --sandbox run.
$ docker agent sandbox allow api.example.com
# Or several at once.
$ docker agent sandbox allow api.example.com registry.npmjs.org:443
# See what's persisted in ~/.config/cagent/config.yaml.
$ docker agent sandbox list
# Drop a host you no longer need.
$ docker agent sandbox deny api.example.com
```
Entries are unioned with the gateway, the kit-resolved tool install hosts, and any `runtime.network_allowlist` declared by the agent. The launch summary lists every source separately so you can see which holes were punched by which layer.
## Global Flags
These flags are available on every `docker agent` command:
| Flag | Description |
| ------------------------- | -------------------------------------------------------------------------------------- |
| `-d, --debug` | Enable debug logging (default location: `~/.cagent/cagent.debug.log`) |
| `--log-file <path>` | Custom debug log location (only used with `--debug`) |
| `-o, --otel` | Enable OpenTelemetry observability: traces, metrics, and logs. Requires `OTEL_EXPORTER_OTLP_ENDPOINT` to export to a collector. |
| `--cache-dir <path>` | Override the cache directory (default: `~/Library/Caches/cagent` on macOS) |
| `--config-dir <path>` | Override the config directory (default: `~/.config/cagent`) |
| `--data-dir <path>` | Override the data directory (default: `~/.cagent`) |
| `--help` | Show help for any command |
### OpenTelemetry environment variables
When `--otel` is enabled, the standard [OTel SDK env vars](https://opentelemetry.io/docs/specs/otel/configuration/sdk-environment-variables/) are honored (`OTEL_EXPORTER_OTLP_ENDPOINT`, `OTEL_RESOURCE_ATTRIBUTES`, etc.). Two additional docker-agent-specific variables control GenAI instrumentation:
| Variable | Default | Description |
| -------- | ------- | ----------- |
| `OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT` | `false` | Set to `true` to capture prompt text, model responses, tool arguments, and tool results as span attributes. Off by default because these fields may contain PII. |
| `OTEL_SEMCONV_STABILITY_OPT_IN` | (dual-emit) | Set to `gen_ai_latest_experimental` to emit only the spec-defined `gen_ai.*` keys from the [GenAI semantic conventions](https://opentelemetry.io/docs/specs/semconv/gen-ai/). The default dual-emit mode emits both `gen_ai.*` and legacy keys so existing dashboards continue working. |
## Runtime Configuration Flags
These flags are accepted by every command that loads an agent (`run`, `run --exec`, `new`, `eval`, `serve api`, `serve mcp`, `serve a2a`, `serve acp`, `serve chat`). They are listed once here to avoid repetition in the per-command tables above.
| Flag | Description |
| ------------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
| `--working-dir <path>` | Set the working directory for the session (applies to tools and relative paths). |
| `--env-from-file <path>` | Load environment variables from file (repeatable). |
| `--code-mode-tools` | Provide a single tool to call other tools via JavaScript (forces code-mode tools globally). |
| `--models-gateway <addr>` | Route model traffic through a gateway. Reads `DOCKER_AGENT_MODELS_GATEWAY` (legacy `CAGENT_MODELS_GATEWAY`) env var. |
| `--hook-pre-tool-use <cmd>` | Add a pre-tool-use hook command (repeatable). See [Hooks](../../configuration/hooks/index.md). |
| `--hook-post-tool-use <cmd>` | Add a post-tool-use hook command (repeatable). |
| `--hook-session-start <cmd>` | Add a session-start hook command (repeatable). |
| `--hook-session-end <cmd>` | Add a session-end hook command (repeatable). |
| `--hook-on-user-input <cmd>` | Add an on-user-input hook command (repeatable). |
| `--hook-stop <cmd>` | Add a stop hook command, fired when the model finishes responding (repeatable). |
## Agent References
Commands that accept a config support multiple reference types:
| Type | Example |
| ------------- | ------------------------------------------- |
| Local file | `./agent.yaml` |
| OCI registry | `docker.io/username/agent:latest` |
| Agent catalog | `agentcatalog/pirate` |
| Alias | `pirate` (after `docker agent alias add`) |
| Default | (no argument) — uses built-in default agent |
> [!NOTE]
> **Debugging**
>
> Having issues? See [Troubleshooting](../../community/troubleshooting/index.md) for debug mode, log analysis, and common solutions.

View File

@@ -0,0 +1,238 @@
---
title: "Evaluation"
description: "Measure agent quality with automated evaluations — tool call accuracy, response relevance, output size, and more."
keywords: docker agent, ai agents, features, evaluation
weight: 100
aliases:
- /ai/docker-agent/evals/
---
_Measure agent quality with automated evaluations — tool call accuracy, response relevance, output size, and more._
## Overview
The `docker agent eval` command runs your agent against a set of recorded sessions and scores the results. Each eval session captures a user question, the expected tool calls, and criteria the response must satisfy. docker-agent replays the question, compares the agent's behavior to expectations, and produces a report.
> [!NOTE]
> **Docker required**
>
> Evaluations run inside Docker containers for isolation. Each eval gets a clean environment with optional setup scripts. Docker Desktop (or Docker Engine) must be running.
## Quick Start
```bash
# Run evaluations for an agent
$ docker agent eval agent.yaml
# Specify a custom evals directory
$ docker agent eval agent.yaml ./my-evals
# Run with 8 concurrent evaluations
$ docker agent eval agent.yaml -c 8
# Only run evals matching a pattern
$ docker agent eval agent.yaml --only "auth*"
# Repeat each eval 5 times to compute a baseline
$ docker agent eval agent.yaml --repeat 5
# Repeat a specific eval 5 times
$ docker agent eval agent.yaml --only "auth*" --repeat 5
```
## Eval Directory Structure
By default, docker-agent looks for eval sessions in an `evals/` directory next to your agent config:
```bash
my-agent/
├── agent.yaml
└── evals/
├── 41b179a2-....json # Eval session 1
├── 5d83e247-....json # Eval session 2
└── results/ # Output (auto-created)
├── adjective-noun-1234.json
├── adjective-noun-1234.log
├── adjective-noun-1234.db
└── adjective-noun-1234-sessions.json
```
## Eval Session Format
Each eval file is a JSON session that captures a complete conversation. The key fields for evaluation are the user message, the expected tool calls (recorded from a real session), and optional eval criteria:
```json
{
"id": "41b179a2-ed19-4ae2-a45d-95775aaa90f7",
"title": "Counting Files in Local Folder",
"messages": [
{
"message": {
"agentFilename": "./agent.yaml",
"message": {
"role": "user",
"content": "How many files in the local folder?"
}
}
},
{
"message": {
"agentName": "root",
"message": {
"role": "assistant",
"tool_calls": [
{
"id": "call_abc123",
"type": "function",
"function": {
"name": "list_directory",
"arguments": "{\"path\":\"./\"}"
}
}
]
}
}
},
{
"message": {
"agentName": "root",
"message": {
"role": "assistant",
"content": "There are 2 files in the local folder..."
}
}
}
],
"evals": {
"relevance": [
"The response mentions exactly 2 files",
"The response lists README.md and agent.yaml"
],
"size": "S",
"working_dir": "my-project",
"setup": "echo 'hello' > test.txt"
}
}
```
## Eval Criteria
The `evals` object inside each session controls what gets scored:
| Field | Type | Description |
| ------------- | -------- | ----------------------------------------------------------------------------------------- |
| `relevance` | string[] | Statements that must be true about the agent's response. Scored by an LLM judge. |
| `size` | string | Expected response size: `S`, `M`, `L`, or `XL`. Compared against actual output length. |
| `working_dir` | string | Subdirectory under `evals/working_dirs/` to mount as the container's working directory. |
| `setup` | string | Shell script to run in the container before the agent executes (e.g., create test files). |
## Scoring Metrics
docker-agent evaluates agents across three dimensions:
| Metric | How It's Measured |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------- |
| **Tool Calls (F1)** | F1 score between the expected tool call sequence (from the recorded session) and the actual tool calls made by the agent. |
| **Relevance** | An LLM judge (configurable via `--judge-model`) evaluates whether each relevance statement is satisfied by the response. |
| **Size** | Whether the response length matches the expected size category (S/M/L/XL). |
## Creating Eval Sessions
The easiest way to create eval sessions is from real conversations:
1. Run your agent interactively: `docker agent run agent.yaml`
2. Have a conversation that tests the behavior you care about
3. Use the `/eval` slash command in the TUI to save the session as an eval file
4. Edit the generated JSON to add `evals` criteria (relevance, size, etc.)
> [!TIP]
> Start with tool call scoring (automatic from recorded sessions), then add relevance criteria for the responses you care most about.
## CLI Flags
```bash
$ docker agent eval <agent-file>|<registry-ref> [<eval-dir>|./evals]
```
| Flag | Default | Description |
| ------------------- | --------------------------- | ----------------------------------------------------------------- |
| `-c, --concurrency` | num CPUs | Number of concurrent evaluation runs |
| `--judge-model` | `anthropic/claude-opus-4-5-20251101` | Model for LLM-as-a-judge relevance scoring |
| `--output` | `<eval-dir>/results` | Directory for results, logs, and session databases |
| `--only` | (all) | Only run evals with file names matching these patterns |
| `--base-image` | (default) | Custom base Docker image for eval containers (see [Custom Base Images](#custom-base-images)) |
| `--keep-containers` | `false` | Keep containers after evaluation (don't remove with `--rm`) |
| `-e, --env` | (none) | Environment variables to pass to container (`KEY` or `KEY=VALUE`) |
| `--repeat` | `1` | Number of times to repeat each evaluation (useful for computing baselines) |
### Custom Base Images
When `--base-image` is set, the eval harness builds a derived image on top of your base image at evaluation time. Two things happen automatically:
1. **The docker-agent binary is injected** — it is copied from `docker/docker-agent:edge` into the derived image at build time, so you don't need to include it in your base image.
2. **The entrypoint is overridden** — docker-agent replaces your base image's entrypoint with its own `/run.sh` wrapper.
Your base image therefore only needs to provide the runtime environment: language runtimes, installed dependencies, test fixtures, the appropriate working directory, and so on. Any `ENTRYPOINT` or `CMD` defined in your base image is ignored.
## Output
After a run completes, docker-agent produces:
- **Console summary** — Pass/fail status per eval with metric breakdowns
- **JSON results** — Full structured results for programmatic analysis
- **SQLite database** — Complete sessions for detailed investigation and debugging
- **Sessions JSON** — Exported session data for analysis
- **Log file** — Debug-level log of the entire evaluation run
> [!TIP]
> **Debugging Failed Evals**
>
> Use `--keep-containers` to preserve containers after evaluation. You can then inspect them with `docker exec` to understand why an eval failed. The session database (`.db` file) contains the full conversation history for each eval.
```bash
$ docker agent eval demo.yaml ./evals
✓ Counting Files in Local Folder
✓ tool calls ✓ relevance 2/2
✓ Checking the Content of README.md File
✓ tool calls ✓ relevance 1/1
✅ Tool Calls: 100.0% avg F1 (2 evals)
✅ Relevance: 3/3 passed (100.0%)
Total Cost: $0.012345
Total Time: 12s
Sessions DB: ./evals/results/happy-panda-1234.db
Sessions JSON: ./evals/results/happy-panda-1234-sessions.json
Log: ./evals/results/happy-panda-1234.log
```
## Example
Here's a minimal evaluation setup:
```yaml
# agent.yaml
agents:
root:
model: openai/gpt-4o
description: Test agent
instruction: You know how to read/write and list files.
toolsets:
- type: filesystem
```
```bash
# Create evals from interactive sessions
$ docker agent run agent.yaml
# ... have conversations, then use /eval to save them
# Run the evaluations
$ docker agent eval agent.yaml ./evals
```
> [!NOTE]
> **See also**
>
> Use `/eval` in the [TUI](../tui/index.md) to create eval sessions from conversations. See the [CLI Reference](../cli/index.md) for all `docker agent eval` flags. Example eval configs are in [examples/eval](https://github.com/docker/docker-agent/tree/main/examples/eval) on GitHub.

View File

@@ -0,0 +1,200 @@
---
title: "Coding Harnesses"
description: "Delegate coding tasks to external AI coding CLIs (Claude Code, Codex, opencode) as sub-agents."
keywords: docker agent, ai agents, features, coding harnesses
weight: 50
---
_Delegate coding tasks to external AI coding CLIs (Claude Code, Codex, opencode) as sub-agents._
## Overview
A **harness** agent delegates its work to an external coding CLI — `claude` (Claude Code), `codex` (OpenAI Codex), `opencode`, or `pi` — instead of calling a model API directly. The external CLI drives the coding loop while Docker Agent provides orchestration, hooks, permissions, and distribution.
This pattern gives you the best of both worlds:
- **External CLI strengths** — deep IDE integration, specialized coding workflows, CLI-native tool access
- **Docker Agent strengths** — multi-agent coordination, hook-based auditing and policy enforcement, permission controls, OCI distribution, and the full agent config schema
> [!NOTE]
> **When to use harnesses**
>
> Use a harness when you want a Claude Code / Codex / opencode session to act as a sub-agent inside a larger Docker Agent workflow — for example, an orchestrator that plans work and delegates coding tasks to specialized harness agents.
## Prerequisites
The external CLI must be installed and available on `PATH` before starting Docker Agent:
| Harness type | Required binary | Install |
| --- | --- | --- |
| `claude-code` | `claude` | [docs.anthropic.com/en/docs/claude-code](https://docs.anthropic.com/en/docs/claude-code) |
| `codex` | `codex` | [github.com/openai/codex](https://github.com/openai/codex) |
| `opencode` | `opencode` | [opencode.ai](https://opencode.ai) |
| `pi` | `pi` | Refer to the `pi` CLI documentation for installation instructions. |
Docker Agent will report an error at session start if the required binary is not found.
## Configuration
Add a `harness:` block to any agent definition to make it harness-backed:
```yaml
agents:
coder:
description: A Claude Code harness agent
harness:
type: claude-code
```
Harness agents do **not** need a `model:` field — the external CLI manages its own model selection.
### Field Reference
| Field | Applies to | Type | Description |
| --- | --- | --- | --- |
| `type` | all | string | **Required.** One of `claude-code`, `codex`, `opencode`, `pi` |
| `model` | all | string | Optional model override forwarded to the CLI |
| `effort` | `claude-code` | string | Reasoning effort: `low` \| `medium` \| `high` \| `max` — forwarded as `--effort` |
| `agent` | `opencode` | string | opencode agent profile name |
| `thinking` | `opencode` | boolean | Enable extended thinking — forwarded as `--thinking` |
### Claude Code
```yaml
agents:
coder:
description: Claude Code coding agent
harness:
type: claude-code
effort: high # low | medium | high | max
```
### Codex
```yaml
agents:
coder:
description: Codex coding agent
harness:
type: codex
model: o4-mini # optional model override
```
### opencode
```yaml
agents:
coder:
description: opencode coding agent
harness:
type: opencode
agent: my-profile # optional agent profile
thinking: true # enable extended thinking
```
## What Does NOT Work
Harness agents bypass the Docker Agent model pipeline entirely. As a result:
- **Docker Agent toolsets are inactive.** The external CLI provides its own tools — filesystem, shell, etc. Any `toolsets:` defined on a harness agent are silently ignored.
- **`model:` routing is unavailable.** The harness CLI manages model selection; Docker Agent's `models:` configuration and routing rules do not apply to harness agents.
- **Token usage tracking depends on the external CLI.** Docker Agent records usage when the CLI reports it (Claude Code and Codex both report usage data). If the CLI does not emit usage data, the session will show zero token usage.
> [!WARNING]
> **No Docker Agent toolsets inside a harness**
>
> Do not configure `toolsets:` on a harness agent — they are silently ignored. If you need Docker Agent toolsets alongside external coding capabilities, use a standard sub-agent with `transfer_task` rather than a harness.
## Hook Behavior
Hooks work normally on harness agents, including `before_llm_call` and `after_llm_call`. `before_llm_call` runs before the prompt is forwarded to the external CLI and can block or rewrite the run; `after_llm_call` fires after the CLI returns its final response.
The `model_id` field in hook payloads is set to the harness label (e.g. `claude-code`) rather than a canonical `provider/model` string. This applies to `before_llm_call`, `after_llm_call`, and any other event that carries `model_id`.
See [Hooks](../../configuration/hooks/index.md) for the full hook reference.
## Recipe: Orchestrator + Harness Sub-Agents (Sequential)
An orchestrator plans the work and delegates to specialized harness agents one at a time. Each coding agent runs in its own sub-session and reports results back.
```yaml
# examples/coding_harnesses.yaml
models:
claude:
provider: anthropic
model: claude-sonnet-4-5
agents:
root:
model: claude
description: Orchestrator that plans and delegates coding tasks
instruction: |
You are a project orchestrator. Break down coding requests into
focused tasks and delegate each task to the most appropriate
coding agent. Collect results and synthesize a final summary.
sub_agents:
- claude-coder
- codex-coder
claude-coder:
description: Claude Code specialist for complex refactors
harness:
type: claude-code
effort: high
codex-coder:
description: Codex specialist for code generation
harness:
type: codex
```
The root agent calls `transfer_task` to send work to a harness sub-agent, waits for the result, and continues. See the [full example on GitHub](https://github.com/docker/docker-agent/blob/main/examples/coding_harnesses.yaml).
## Recipe: Parallel Harness Dispatch
Combine the `background_agents` toolset with harness sub-agents to dispatch multiple coding tasks simultaneously:
```yaml
# examples/coding_harness_background_agents.yaml
models:
claude:
provider: anthropic
model: claude-sonnet-4-5
agents:
root:
model: claude
description: Orchestrator that fans out coding tasks in parallel
instruction: |
Use background agents to run multiple coding tasks at once.
Dispatch all tasks, then collect results when each finishes.
sub_agents:
- claude-coder
- codex-coder
toolsets:
- type: background_agents
claude-coder:
description: Frontend specialist (Claude Code)
harness:
type: claude-code
effort: medium
codex-coder:
description: Backend specialist (Codex)
harness:
type: codex
```
The orchestrator calls `run_background_agent` for each task, monitors progress with `list_background_agents`, and collects results with `view_background_agent`. See the [full example on GitHub](https://github.com/docker/docker-agent/blob/main/examples/coding_harness_background_agents.yaml).
For the general background agents reference, see [Background Agents](../../tools/background-agents/index.md).
## See Also
- [Multi-Agent Systems](../../concepts/multi-agent/index.md) — orchestration patterns
- [Background Agents](../../tools/background-agents/index.md) — parallel task dispatch
- [Hooks](../../configuration/hooks/index.md) — auditing and policy enforcement
- [Agent Configuration](../../configuration/agents/index.md) — full agent schema reference

View File

@@ -0,0 +1,126 @@
---
title: "MCP Mode"
description: "Expose your docker-agent agents as MCP tools for use in Claude Desktop, Claude Code, and other MCP-compatible applications."
keywords: docker agent, ai agents, features, mcp mode
weight: 40
---
_Expose your docker-agent agents as MCP tools for use in Claude Desktop, Claude Code, and other MCP-compatible applications._
## Why MCP Mode?
The `docker agent serve mcp` command makes your agents available to any application that supports the [Model Context Protocol](https://modelcontextprotocol.io/). This means you can:
- Use custom agents directly within **Claude Desktop** or **Claude Code**
- Share specialized agents across different applications
- Build reusable agent teams consumable from any MCP client
- Integrate domain-specific agents into existing workflows
> [!NOTE]
> **What is MCP?**
>
> The [Model Context Protocol](https://modelcontextprotocol.io/) is an open standard for connecting AI tools. See also [Remote MCP Servers](../remote-mcp/index.md) for connecting to cloud services.
## Basic Usage
```bash
# Expose a local config (stdio transport, the default)
$ docker agent serve mcp ./agent.yaml
# Expose from a registry
$ docker agent serve mcp agentcatalog/pirate
# Set the working directory
$ docker agent serve mcp ./agent.yaml --working-dir /path/to/project
```
## Transports
By default, `serve mcp` uses the stdio transport — ideal for clients that spawn the server as a subprocess (Claude Desktop, Claude Code, Cursor, …).
To expose the MCP server over streaming HTTP instead, pass `--http`:
```bash
# Streaming HTTP transport on the default 127.0.0.1:8081
$ docker agent serve mcp ./agent.yaml --http
# Override the listen address / port
$ docker agent serve mcp ./agent.yaml --http --listen 0.0.0.0:9090
```
| Flag | Default | Description |
| ---------------------- | ------------------ | ------------------------------------------------------------------------------------------------------------ |
| `--http` | `false` | Use streaming HTTP transport instead of stdio. |
| `-l`, `--listen` | `127.0.0.1:8081` | Address to listen on when `--http` is enabled. |
| `-a`, `--agent` | all agents | Expose a single named agent instead of every agent in the config. |
| `--tool-name` | (none) | Override the MCP tool identifier clients call (defaults to agent name); only valid when exposing one agent. |
| `--mcp-keepalive` | `0` | Interval between MCP keep-alive pings (e.g. `30s`); `0` disables keep-alive. |
Runtime configuration flags such as `--working-dir`, `--env-from-file`, `--models-gateway`, and hook flags are also available — see the [CLI reference](../cli/index.md).
## Using with Claude Desktop
Add a configuration to your Claude Desktop MCP settings file:
- **macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json`
- **Windows:** `%APPDATA%\Claude\claude_desktop_config.json`
```json
{
"mcpServers": {
"myagent": {
"command": "/usr/local/bin/docker",
"args": [
"agent",
"serve",
"mcp",
"agentcatalog/coder",
"--working-dir",
"/home/user/projects"
],
"env": {
"ANTHROPIC_API_KEY": "your_key_here",
"OPENAI_API_KEY": "your_key_here"
}
}
}
}
```
Restart Claude Desktop after updating the configuration.
## Using with Claude Code
```bash
$ claude mcp add --transport stdio myagent \
--env OPENAI_API_KEY=$OPENAI_API_KEY \
--env ANTHROPIC_API_KEY=$ANTHROPIC_API_KEY \
-- docker agent serve mcp agentcatalog/pirate --working-dir $(pwd)
```
## Multi-Agent in MCP Mode
When you expose a multi-agent configuration via MCP, each agent becomes a separate tool in the MCP client:
```yaml
agents:
root:
model: anthropic/claude-sonnet-4-5
description: Main coordinator
sub_agents: [designer, engineer]
designer:
model: openai/gpt-5-mini
description: UI/UX design specialist
engineer:
model: anthropic/claude-sonnet-4-5
description: Software engineer
```
All three agents (`root`, `designer`, `engineer`) appear as separate tools in Claude Desktop or Claude Code.
## Troubleshooting
- **Agents not appearing:** Verify the `docker-agent` binary path and restart the MCP client
- **Permission errors:** Ensure `docker-agent` has execute permissions (`chmod +x`)
- **Missing API keys:** Pass all required keys in the `env` section
- **Working directory issues:** Verify the `--working-dir` path exists and is accessible

View File

@@ -0,0 +1,279 @@
---
title: "Remote MCP Servers"
description: "Connect docker-agent to cloud services via remote MCP servers with built-in OAuth authentication."
keywords: docker agent, ai agents, features, remote mcp servers
weight: 120
---
_Connect docker-agent to cloud services via remote MCP servers with built-in OAuth authentication._
## Overview
Docker Agent supports connecting to remote MCP servers over **Streamable HTTP** and **SSE** (Server-Sent Events) transports. Streamable HTTP is the current recommended transport for most hosted MCP servers. Many popular services offer MCP endpoints with OAuth — docker-agent handles the authentication flow automatically.
```yaml
toolsets:
- type: mcp
remote:
url: "https://mcp.linear.app/mcp"
transport_type: "streamable"
```
> [!TIP]
> **OAuth flow**
>
> When you connect to a remote MCP server that requires OAuth, docker-agent opens your browser automatically for authentication. Tokens are cached for subsequent sessions.
> [!TIP]
> **Cancelling the authorization dialog**
>
> If you dismiss the OAuth authorization dialog, the request is cancelled cleanly — no repeated prompts appear. The agent will report that authorization was declined. To try again, simply re-enable the server or repeat the request that triggered the flow.
## Configuration
```yaml
toolsets:
- type: mcp
remote:
url: "https://mcp.example.com/mcp"
transport_type: "streamable" # or "sse" for legacy servers
headers:
Authorization: "Bearer token" # optional: static auth
# Optional: use only for trusted internal/private MCP or OAuth endpoints.
allow_private_ips: true
```
For full configuration details, see the [Tool Config](../../configuration/tools/index.md) page.
Set `allow_private_ips: true` on a remote MCP toolset only when the MCP server or its OAuth registration/token endpoints intentionally resolve to private, loopback, or link-local addresses. The default blocks those OAuth helper requests to reduce SSRF risk.
> [!NOTE]
> **Headers forwarded during OAuth discovery**
>
> Configured `headers` are forwarded to OAuth protected-resource-metadata discovery requests directed at the MCP server's own host — not to third-party authorization servers. This allows services like Grafana Cloud that require a routing header (e.g. `X-Grafana-URL`) on the discovery request to scope the OAuth flow correctly. Headers are never sent to a different host than the one in `remote.url`.
> [!NOTE]
> **Automatic reconnection after idle timeouts**
>
> Remote MCP connections (Streamable HTTP / SSE) automatically reconnect after the server closes an idle connection — no configuration needed. Services like Notion and Linear close idle connections periodically; docker-agent detects the clean close and reconnects with exponential backoff. To tune reconnect behaviour or disable reconnection entirely, use the [`lifecycle` block](../../configuration/tools/index.md#toolset-lifecycle).
> [!NOTE]
> **Automatic recovery from revoked or rotated OAuth tokens**
>
> If a remote MCP server rejects the cached token with a `401 invalid_token` error (for example, because the token was revoked or rotated server-side), docker-agent handles the failure automatically:
>
> - **Silent refresh:** when a refresh token is available, docker-agent silently exchanges it for a new access token and replays the request — no user interaction required.
> - **Re-authentication prompt:** when the refresh token is absent or has also expired, the toolset transitions to a "needs re-auth" state and surfaces an OAuth prompt on your next message (exactly like the first-time flow).
>
> Either way, the agent never burns 5 reconnect attempts on an auth failure — it fails fast and either refreshes silently or defers to interactive re-auth. If you want to trigger re-auth immediately without waiting for the next message, run `/toolset-restart <name>` from the TUI.
### OAuth for servers without Dynamic Client Registration
Most remote MCP servers that require OAuth support [Dynamic Client Registration (RFC 7591)](https://datatracker.ietf.org/doc/html/rfc7591) — no configuration is needed, docker-agent handles the flow for you.
For servers that do **not** support DCR, provide explicit OAuth credentials with the `oauth:` block:
```yaml
toolsets:
- type: mcp
remote:
url: "https://mcp.example.com/mcp"
transport_type: "streamable"
oauth:
clientId: "my-app-client-id"
clientSecret: "my-app-client-secret" # optional (public clients may omit)
callbackPort: 8765 # optional; picks a free port otherwise
scopes: # optional; server-specific
- read
- write
```
| Field | Type | Required | Description |
| -------------- | --------------- | -------- | ------------------------------------------------------------------------------------------------ |
| `clientId` | string | ✓ | OAuth client ID registered with the remote MCP server. |
| `clientSecret` | string | ✗ | OAuth client secret. Omit for public clients using PKCE. |
| `callbackPort` | integer | ✗ | Local port to receive the OAuth redirect. If omitted, docker-agent picks a random free port. |
| `scopes` | array[string] | ✗ | Scopes to request during the authorization step. Values are server-specific. |
| `callbackRedirectURL` | string | ✗ | Custom OAuth redirect URI. Useful when the auth server requires HTTPS or a pre-registered URL. The literal placeholder `${callbackPort}` is replaced with the actual local callback port. See below. |
Secrets should be stored in a credential helper or environment variable rather than committed — see [Secrets](../../guides/secrets/index.md) for interpolation patterns.
### Custom redirect URI (`callbackRedirectURL`)
Some authorization servers require the OAuth `redirect_uri` to be HTTPS or to match a URL that was pre-registered during app creation — neither of which plays nicely with a locally-bound loopback address such as `http://127.0.0.1:8765/callback`.
To work around this, set `callbackRedirectURL` to a public URL that redirects back to the local callback server. The literal placeholder `${callbackPort}` is substituted with the actual port the local callback server is listening on (either `callbackPort` when set, or the randomly-assigned port otherwise).
```yaml
toolsets:
- type: mcp
remote:
url: "https://mcp.example.com/mcp"
transport_type: "streamable"
oauth:
clientId: "my-app-client-id"
callbackPort: 8765
# Advertise this URL to the authorization server. The external
# service at redirect.example.com is expected to 302-redirect the
# browser to http://127.0.0.1:8765/callback preserving the query
# string (code, state, …).
callbackRedirectURL: "https://redirect.example.com/cb?port=${callbackPort}"
```
The local callback server still listens on the loopback interface on `callbackPort`; only the `redirect_uri` advertised to the authorization server changes.
**Validation rules:**
- The URL must be absolute (scheme + host) once `${callbackPort}` has been substituted.
- Only `http` and `https` schemes are accepted.
- `http` is only allowed when the host is a loopback address (`127.0.0.1`, `::1`, `localhost`); any other host must use `https` to avoid exposing the authorization `code` on the wire (RFC 8252 §7.3).
### Unmanaged OAuth flow (server mode)
When running `docker-agent serve api` (no local browser, no callback server), the runtime delegates the OAuth dance to the connected client via an MCP elicitation. There are two sub-behaviors, selected by the `--mcp-oauth-redirect-uri` flag:
- **`--mcp-oauth-redirect-uri=<URL>` set** (recommended for hosts like Docker Desktop): the runtime generates `state` + PKCE + (optional) Dynamic Client Registration in-process, builds the full authorize URL, and emits an elicitation whose `Meta` includes:
| Key | Value |
| ---------------------------- | ---------------------------------------------------------------- |
| `docker-agent/type` | `"oauth_flow"` |
| `docker-agent/server_url` | The MCP server URL (for display / favicon) |
| `docker-agent/authorize_url` | The full URL the client should open in the user's browser |
| `docker-agent/state` | The `state` value the client must echo back when replying |
| `auth_server` | Issuer of the authorization server |
| `auth_server_metadata` | RFC 8414 authorization-server metadata document |
| `resource_metadata` | RFC 9728 protected-resource metadata document |
The client opens the browser at the URL provided in the `docker-agent/authorize_url` meta field, receives the OAuth callback at whatever endpoint the configured `redirect_uri` resolves to (typically a host-controlled bouncer that 302s into a deeplink), and replies to the elicitation with `accept` and `Content = {"code": "...", "state": "..."}`. The runtime verifies the `state`, exchanges the `code` at the token endpoint (using the same `redirect_uri` for RFC 6749 §4.1.3 binding), stores the token, and replays the original MCP request with `Authorization: Bearer ...`.
- **Flag not set** (client-driven): the runtime emits the elicitation meta below and expects the client to drive the OAuth flow itself (PKCE, DCR, token exchange) and reply with `Content = {"access_token": "...", "refresh_token": "...", ...}`:
| Key | Value |
| ---------------------------- | ---------------------------------------------------------------- |
| `docker-agent/type` | `"oauth_flow"` |
| `docker-agent/server_url` | The MCP server URL (for display / favicon) |
| `auth_server` | Issuer of the authorization server |
| `auth_server_metadata` | RFC 8414 authorization-server metadata document |
| `resource_metadata` | RFC 9728 protected-resource metadata document |
The client-driven `{access_token, ...}` reply shape is still accepted on the `--mcp-oauth-redirect-uri` path too: a client that prefers to do the exchange itself can ignore the `docker-agent/authorize_url`/`docker-agent/state` keys.
A per-toolset `callbackRedirectURL` (in the YAML) overrides the runtime-wide `--mcp-oauth-redirect-uri` for that toolset.
> [!WARNING]
> **Security note**
>
> The `POST /api/mcp-oauth/callback` route is open by default (no auth required) when `--auth-token` is unset. State values are 128-bit opaque tokens, so brute-force is infeasible, but a state value that leaks (e.g. via debug logs or a compromised host) could be exploited by an attacker to inject a code. Set `--auth-token` when `docker agent serve api` listens on a network-reachable interface. When set, `--auth-token` enforces Bearer-token authentication on all API routes including this callback endpoint.
## Project Management &amp; Collaboration
| Service | URL | Transport | Description |
| ---------- | ---------------------------------- | --------- | ------------------------------------- |
| Asana | `https://mcp.asana.com/sse` | sse | Task and project management |
| Atlassian | `https://mcp.atlassian.com/v1/mcp/authv2` | streamable | Jira, Confluence integration |
| Linear | `https://mcp.linear.app/mcp` | streamable | Issue tracking and project management |
| Monday.com | `https://mcp.monday.com/sse` | sse | Work management platform |
| Intercom | `https://mcp.intercom.com/sse` | sse | Customer communication platform |
## Development &amp; Infrastructure
| Service | URL | Transport | Description |
| ------------------------ | ---------------------------------------------- | ---------- | --------------------------------- |
| GitHub | `https://api.githubcopilot.com/mcp` | sse | Version control and collaboration |
| Buildkite | `https://mcp.buildkite.com/mcp` | streamable | CI/CD platform |
| Netlify | `https://netlify-mcp.netlify.app/mcp` | streamable | Web hosting and deployment |
| Vercel | `https://mcp.vercel.com/` | sse | Web deployment platform |
| Cloudflare Bindings | `https://bindings.mcp.cloudflare.com/sse` | sse | Edge computing resources |
| Cloudflare Observability | `https://observability.mcp.cloudflare.com/sse` | sse | Monitoring and analytics |
| Grafbase | `https://api.grafbase.com/mcp` | streamable | GraphQL backend platform |
| Neon | `https://mcp.neon.tech/sse` | sse | Serverless Postgres database |
| Prisma | `https://mcp.prisma.io/mcp` | streamable | Database ORM and toolkit |
| Sentry | `https://mcp.sentry.dev/sse` | sse | Error tracking and monitoring |
## Content &amp; Media
| Service | URL | Transport | Description |
| ---------- | ------------------------------------------------- | ---------- | --------------------------------- |
| Canva | `https://mcp.canva.com/mcp` | streamable | Design and graphics platform |
| Miro | `https://mcp.miro.com/` | streamable | Collaborative whiteboard platform (Enterprise plan required; see [official docs](https://developers.miro.com/docs/miro-mcp)) |
| Cloudinary | `https://asset-management.mcp.cloudinary.com/sse` | sse | Media management and optimization |
| InVideo | `https://mcp.invideo.io/sse` | sse | Video creation platform |
| Webflow | `https://mcp.webflow.com/sse` | sse | Website builder and CMS |
| Wix | `https://mcp.wix.com/sse` | sse | Website builder platform |
| Notion | `https://mcp.notion.com/mcp` | streamable | Documentation and knowledge base |
## Communication &amp; Voice
| Service | URL | Transport | Description |
| ----------- | ----------------------------------- | ---------- | --------------------------- |
| Fireflies | `https://api.fireflies.ai/mcp` | streamable | Meeting transcription |
| Listenetic | `https://mcp.listenetic.com/v1/mcp` | streamable | Audio intelligence platform |
| Carbonvoice | `https://mcp.carbonvoice.app` | sse | Voice communication tools |
| Telnyx | `https://api.telnyx.com/v2/mcp` | streamable | Communications platform |
| Dialer | `https://getdialer.app/sse` | sse | Phone communication tools |
## Storage &amp; File Management
| Service | URL | Transport | Description |
| ------- | ----------------------------------- | --------- | ------------------------ |
| Box | `https://mcp.box.com` | sse | Cloud content management |
| Egnyte | `https://mcp-server.egnyte.com/sse` | sse | Enterprise file sharing |
## Business &amp; Finance
| Service | URL | Transport | Description |
| ------------- | ----------------------------------------- | ---------- | -------------------------- |
| PayPal | `https://mcp.paypal.com/sse` | sse | Payment processing |
| Plaid | `https://api.dashboard.plaid.com/mcp/sse` | sse | Financial data integration |
| Square | `https://mcp.squareup.com/sse` | sse | Payment processing |
| Close | `https://mcp.close.com/mcp` | streamable | CRM platform |
| Dodo Payments | `https://mcp.dodopayments.com/sse` | sse | Payment processing |
## Analytics &amp; Data
| Service | URL | Transport | Description |
| ----------- | --------------------------------------- | ---------- | ------------------------------ |
| ThoughtSpot | `https://agent.thoughtspot.app/mcp` | streamable | Analytics and BI platform |
| Meta Ads | `https://mcp.pipeboard.co/meta-ads-mcp` | streamable | Facebook advertising analytics |
## Utilities &amp; Tools
| Service | URL | Transport | Description |
| ------------- | ---------------------------------- | ---------- | ------------------------------- |
| Apify | `https://mcp.apify.com` | sse | Web scraping and automation |
| SimpleScraper | `https://mcp.simplescraper.io/mcp` | streamable | Web scraping tool |
| GlobalPing | `https://mcp.globalping.dev/sse` | sse | Network diagnostics |
| Jam | `https://mcp.jam.dev/mcp` | streamable | Bug reporting and collaboration |
## Example: Multi-Service Agent
Combine multiple remote MCP servers in a single agent:
```yaml
agents:
root:
model: anthropic/claude-sonnet-4-5
instruction: |
You help manage projects and deployments.
toolsets:
- type: mcp
remote:
url: "https://mcp.linear.app/mcp"
transport_type: "streamable"
instruction: Use Linear for issue tracking.
- type: mcp
remote:
url: "https://api.githubcopilot.com/mcp"
transport_type: "sse"
instruction: Use GitHub for code and PRs.
- type: mcp
remote:
url: "https://mcp.vercel.com/"
transport_type: "sse"
instruction: Use Vercel for deployments.
```
> [!NOTE]
> **Growing list**
>
> This list is updated as more services add MCP support. If a service you use isn't listed, check their documentation — many providers are adding MCP endpoints regularly.

View File

@@ -0,0 +1,375 @@
---
title: "Skills"
description: "Skills provide specialized instructions that agents can load on demand when a task matches a skill's description."
keywords: docker agent, ai agents, features, skills
weight: 110
---
_Skills provide specialized instructions that agents can load on demand when a task matches a skill's description._
## How Skills Work
1. docker-agent scans standard directories for `SKILL.md` files
2. Skill metadata (name, description) is injected into the agent's system prompt
3. When a user request matches a skill, the agent reads the full instructions
4. The agent follows the skill's detailed instructions to complete the task
## Enabling Skills
```yaml
agents:
root:
model: openai/gpt-4o
instruction: You are a helpful assistant.
skills: true
toolsets:
- type: filesystem # required for reading skill files
```
> [!TIP]
> Skills are perfect for encoding team-specific workflows (PR review, deployment, coding standards) that apply across projects.
## Filtering Skills
The `skills` field also accepts a list, letting you restrict the agent to a specific subset of skills instead of exposing every discovered one. List items are classified automatically:
- `"local"` or any `http://` / `https://` URL → a **source** to load skills from
- any other string → the **name** of a skill to include
When only names are given, local sources are used by default.
```yaml
agents:
# Load every discovered local skill (same as `skills: true`).
full:
skills: true
# Load local skills, but only expose "commit" and "poem".
scoped:
skills:
- commit
- poem
# Combine an explicit source with a name filter.
remote_filtered:
skills:
- https://skills.example.com
- commit
# Disable skills entirely.
none:
skills: false
```
A name that doesn't match any discovered skill is logged as a warning at startup but is otherwise ignored.
## Inline Skills
Instead of (or alongside) loading skills from files and URLs, you can define skills directly in the agent config. An inline skill is a mapping item in the `skills` list, freely mixed with the string items above:
```yaml
agents:
root:
model: openai/gpt-4o
instruction: You are a helpful assistant.
skills:
- name: changelog
description: Write a concise changelog entry from a diff or description.
instructions: |
Produce a single changelog entry in Keep a Changelog style.
Pick the right category (Added, Changed, Fixed, Removed) and write
one imperative sentence summarising the user-visible change.
# A fork-mode inline skill runs in an isolated sub-agent.
- name: triage
description: Triage a bug report in an isolated context.
context: fork
instructions: |
Restate the problem, list likely root causes most-probable-first,
and propose the smallest reproduction and next concrete action.
# Inline skills mix freely with sources and name filters.
- local
toolsets:
- type: filesystem
```
Inline skills carry their body in the config itself, so they need no `SKILL.md` file and require no filesystem source. They are **always exposed** — the name filter only applies to file- and URL-based skills. Because inline skills travel inside the agent YAML, they also work in `--sandbox` mode without any kit staging, and they can be shared with the agent via `share push`.
### Inline Skill Fields
| Field | Required | Description |
| --------------- | -------- | -------------------------------------------------------------------------- |
| `name` | Yes | Skill identifier used by `read_skill` / `run_skill` and the `/<name>` command |
| `description` | Yes | Short description shown to the agent for skill matching |
| `instructions` | Yes | The skill body (what a `SKILL.md` would contain below its frontmatter) |
| `context` | No | Set to `fork` to run the skill as an isolated sub-agent |
| `model` | No | Override the model used while running a fork-mode skill |
| `allowed_tools` | No | For a fork-mode skill, restricts the sub-session to the parent tools whose names match an entry (glob or exact). See [Scoping a fork skill's tools](#scoping-a-fork-skills-tools). |
| `toolsets` | No | For a fork-mode skill, names of top-level [`toolsets`](../../configuration/overview/index.md#reusable-toolsets-toolsets) to expose in the sub-session on top of the inherited tools. |
> [!NOTE]
> **Inline vs. file-based skills**
>
> Inline skills support the subset of the SKILL.md format that fits in YAML. They cannot bundle supporting files (no `read_skill_file`) or use `` !`command` `` expansion. For skills that need bundled resources or executable helpers, use a `SKILL.md` directory instead.
## SKILL.md Format
<!-- yaml-lint:skip -->
```yaml
---
name: create-dockerfile
description: Create optimized Dockerfiles for applications
license: Apache-2.0
metadata:
author: my-org
version: "1.0"
---
# Creating Dockerfiles
When asked to create a Dockerfile:
1. Analyze the application type and language
2. Use multi-stage builds for compiled languages
3. Minimize image size by using slim base images
4. Follow security best practices (non-root user, etc.)
```
### Frontmatter Fields
| Field | Required | Description |
| ---------------- | -------- | --------------------------------------------------------------------------- |
| `name` | Yes | Unique skill identifier |
| `description` | Yes | Short description shown to the agent for skill matching |
| `context` | No | Set to `fork` to run the skill as an isolated sub-agent (see below) |
| `model` | No | Override the model used while running the skill as a sub-agent (fork only) |
| `allowed-tools` | No | For a fork-mode skill, restricts the sub-session to the parent tools whose names match an entry (YAML list or comma-separated string). See [Scoping a fork skill's tools](#scoping-a-fork-skills-tools). |
| `toolsets` | No | For a fork-mode skill, names of top-level [`toolsets`](../../configuration/overview/index.md#reusable-toolsets-toolsets) to expose in the sub-session (YAML list or comma-separated string). |
| `license` | No | License identifier (e.g. `Apache-2.0`) |
| `compatibility` | No | Free-text compatibility notes |
| `metadata` | No | Arbitrary key-value pairs (e.g. `author`, `version`) |
## Running a Skill as a Sub-Agent
By default, when an agent invokes a skill it reads the instructions inline into its own conversation. For complex, multi-step skills this can consume a large portion of the agent's context window and pollute the parent conversation with intermediate tool calls.
Adding `context: fork` to the SKILL.md frontmatter tells the agent to run the skill in an **isolated sub-agent** instead:
<!-- yaml-lint:skip -->
```yaml
---
name: bump-go-dependencies
description: Update Go module dependencies one by one
context: fork
---
# Bump Dependencies
1. List outdated deps
2. Update each one, run tests, commit or revert
3. Produce a summary table
```
When the agent encounters a task that matches a `context: fork` skill, it uses the `run_skill` tool instead of `read_skill`. This:
- **Spawns a child session** with the skill content as the system prompt and the caller's task as the user message
- **Isolates the context window** — the sub-agent has its own conversation history, so lengthy tool-call chains don't eat into the parent's token budget
- **Folds the result** — only the sub-agent's final answer is returned to the parent as the tool result
- **Inherits the parent's model and tools** — the sub-agent can use all tools available to the parent agent (scope this with `allowed_tools` / `toolsets`, see [Scoping a fork skill's tools](#scoping-a-fork-skills-tools))
> [!TIP]
> **When to use context: fork**
>
> Use `context: fork` for skills that involve many steps, heavy tool usage, or that should not clutter the main conversation — for example dependency bumping, large refactors, or code generation pipelines.
### Overriding the model for a fork skill
Fork skills can declare a `model` field in their frontmatter to use a
different model than the parent agent for the duration of the sub-session.
This is useful when a skill is best handled by a faster, cheaper, or more
specialised model — for example a powerful reasoning model for refactors,
or a fast model for routine bookkeeping work. The override only applies
while the skill is running; the parent agent keeps its own model.
The `model` value accepts either a named model from the agent config or
an inline `provider/model` reference (and the same comma-separated alloy
syntax as the rest of the agent config):
<!-- yaml-lint:skip -->
```yaml
---
name: bump-go-dependencies
description: Update Go module dependencies one by one
context: fork
model: openai/gpt-4o-mini
---
# Bump Dependencies
1. ...
```
If the model reference cannot be resolved (unknown name, missing
credentials, runtime not configured for model switching, …) the skill
falls back to the agent's currently-active model (its configured
default, or any override the user previously set via the model picker)
and a warning is logged.
When the skill completes, the agent's previous model is restored — but
only if no one else changed the model in the meantime. If the user
switches the model via the TUI model picker while the fork skill is
running, their choice is preserved (the deferred restore becomes a
no-op).
### Scoping a fork skill's tools
By default a fork skill inherits the parent agent's entire tool set. Two
optional fields let you scope what the sub-session can use. Both apply
**only to fork-mode skills** and work the same whether the skill is
inline or loaded from a `SKILL.md` file.
`allowed_tools` (frontmatter: `allowed-tools`) is an **allow-list** over
the inherited tools: only tools whose names match an entry are kept,
everything else is hidden from the sub-session. Entries support glob
patterns (e.g. `read_*`) and otherwise match exactly. This is the
Claude-Code-compatible `allowed-tools` field, now enforced for fork
skills rather than merely recorded.
`toolsets` references reusable [top-level toolsets](../../configuration/overview/index.md#reusable-toolsets-toolsets)
by name. The referenced toolsets are exposed in the sub-session **in
addition to** the inherited tools, and they bypass the `allowed_tools`
filter (the skill explicitly asked for them).
```yaml
toolsets:
web:
type: fetch
agents:
root:
model: openai/gpt-4o
instruction: You are a helpful assistant.
toolsets:
- type: filesystem
- type: shell
skills:
# Inherits the parent tools but is restricted to read-only filesystem
# access while it runs — shell and write tools are hidden.
- name: audit
description: Review the repository layout without modifying anything.
context: fork
allowed_tools:
- read_file
- list_directory
- directory_tree
instructions: Inspect the repository structure and summarise it.
# Brings in the top-level `web` toolset on top of the parent's tools.
- name: research
description: Research a topic using web fetches in an isolated context.
context: fork
toolsets:
- web
instructions: Research the requested topic and summarise with links.
```
The equivalent in a `SKILL.md` file uses frontmatter lists:
<!-- yaml-lint:skip -->
```yaml
---
name: research
description: Research a topic using web fetches
context: fork
allowed-tools:
- fetch
toolsets:
- web
---
```
> [!NOTE]
> **Fork only**
>
> Both fields are rejected by config validation when set on a non-fork skill, and a `toolsets` entry that doesn't resolve to a top-level toolset is a load-time error.
## Search Paths
Skills are discovered from these locations (later overrides earlier):
### Global
| Path | Search Type |
| ------------------- | --------------------------------------- |
| `~/.codex/skills/` | Recursive (searches all subdirectories) |
| `~/.claude/skills/` | Flat (immediate children only) |
| `~/.agents/skills/` | Recursive (searches all subdirectories) |
### Project (from git root to current directory)
| Path | Search Type |
| ----------------- | ------------------------------------------ |
| `.claude/skills/` | Flat (cwd only) |
| `.agents/skills/` | Flat (each directory from git root to cwd) |
## Invoking Skills
Skills can be invoked in multiple ways:
- **Automatic:** The agent detects when your request matches a skill's description and loads it automatically
- **Explicit:** Reference the skill name in your prompt: "Use the create-dockerfile skill to..."
- **Slash command:** Use `/{skill-name}` to invoke a skill directly
```bash
# In the TUI, invoke skill directly:
/create-dockerfile
# Or mention it in your message:
"Create a dockerfile for my Python app (use the create-dockerfile skill)"
```
## Precedence
When multiple skills share the same name:
1. Global skills load first
2. Project skills load next, from git root toward current directory
3. Skills closer to the current directory override those further away
## Skills in Sandbox Mode
When you run an agent with [`--sandbox`](../../configuration/sandbox/index.md), the sandbox VM has its own filesystem with no access to your host's skill directories. docker-agent handles this transparently via the [auto-kit](../../configuration/sandbox/index.md#auto-kit): every discovered local skill is staged into a per-agent kit on the host, run through best-effort secret redaction (see the [auto-kit](../../configuration/sandbox/index.md#secret-redaction) docs), and bind-mounted read-only into the sandbox so the agent sees the same skills inside the VM as on the host. No configuration is required — use `--no-kit` only if you explicitly want to run the sandbox without any host skills.
## Creating a Skill
```bash
# Create the skill directory
$ mkdir -p ~/.agents/skills/create-dockerfile
# Write the SKILL.md file
$ cat > ~/.agents/skills/create-dockerfile/SKILL.md << 'EOF'
---
name: create-dockerfile
description: Create optimized Dockerfiles for applications
---
# Creating Dockerfiles
When asked to create a Dockerfile:
1. Analyze the application type and language
2. Use multi-stage builds for compiled languages
3. Use slim base images to minimize size
4. Run as non-root user for security
EOF
```
The skill will automatically be available to any agent with skills enabled (`skills: true`, or a list that targets its name — see [Filtering Skills](#filtering-skills)).
> [!NOTE]
> **See also**
>
> Skills are enabled in the [Agent Config](../../configuration/agents/index.md) with the `skills` property (boolean or list). For tool-based capabilities, see [Tools](../../concepts/tools/index.md).
>
> Example configs: [`examples/skills_inline.yaml`](https://github.com/docker/docker-agent/blob/main/examples/skills_inline.yaml) (inline skill definition), [`examples/skills_fork_toolsets.yaml`](https://github.com/docker/docker-agent/blob/main/examples/skills_fork_toolsets.yaml) (scoping a fork skill's tools), [`examples/skills_filter.yaml`](https://github.com/docker/docker-agent/blob/main/examples/skills_filter.yaml) (filtering which skills load).

View File

@@ -0,0 +1,111 @@
---
title: "Snapshots"
description: "Shadow-git snapshots capture your workspace at turn boundaries so you can review what an agent changed and undo it without touching your real git history."
keywords: docker agent, ai agents, features, snapshots
weight: 20
---
_Shadow-git snapshots capture your workspace at turn boundaries so you can review what an agent changed and undo it without touching your real git history._
## Overview
When snapshots are enabled, docker-agent records the state of your working
directory as the agent runs. Each checkpoint is stored in a **shadow git
repository** kept under the docker-agent data directory — completely separate
from your project's own `.git`. This lets you:
- **Review** exactly which files an agent touched on a given turn.
- **Undo** the most recent change with `/undo`.
- **Reset** the workspace back to any earlier checkpoint — or all the way to
its pre-agent state.
Snapshots only ever touch files on disk. They never write commits to your
repository, never move your `HEAD`, and never appear in `git log` or
`git status`.
> [!NOTE]
> **Git repositories only**
>
> The snapshot machinery is a no-op outside a git worktree. It needs a git
> repository to scope which files belong to your project and to mirror the
> repository's ignore rules.
## Enabling Snapshots
The simplest way is to enable snapshots globally for every agent in your user
config:
```yaml
# ~/.config/cagent/config.yaml
settings:
snapshot: true
```
Omit `snapshot` or set it to `false` to leave automatic snapshots off.
You can also wire the [`snapshot` built-in hook](../../configuration/hooks/index.md#available-built-ins)
into a single agent instead of enabling it globally:
```yaml
hooks:
turn_start:
- type: builtin
command: snapshot
turn_end:
- type: builtin
command: snapshot
session_end:
- type: builtin
command: snapshot
```
Manually configured snapshot hooks always run, even when `settings.snapshot`
is unset. See [`examples/snapshot_hooks.yaml`](https://github.com/docker/docker-agent/blob/main/examples/snapshot_hooks.yaml)
for a complete configuration.
## Using Snapshots in the TUI
When snapshots are enabled, the [TUI](../tui/index.md)
exposes two slash commands:
- **`/undo`** restores files from the most recent snapshot (one step back).
- **`/snapshots`** opens a dialog listing every captured snapshot and the
number of files in each. Use <kbd>↑</kbd>/<kbd>↓</kbd> (or
<kbd>j</kbd>/<kbd>k</kbd>) to highlight an entry, then press <kbd>r</kbd> to
reset the workspace to that point. Pick `<original>` to revert every snapshot
and bring the workspace back to its pre-agent state. <kbd>Esc</kbd> closes the
dialog without changing anything.
Neither command removes messages from the session transcript — they only touch
files on disk. Both commands (and their command-palette entries) are hidden
when snapshots are turned off.
## How It Works
- **Shadow repository.** The first time a snapshot is taken for a worktree,
docker-agent initializes a separate shadow git directory under the data
directory (`~/.cagent/snapshot/...` by default), keyed by a hash of the
worktree path. The shadow repo stores tree objects only — it never writes
commits and never touches your source repository's `.git`.
- **Ignore rules are mirrored.** Before each capture, the source repository's
`.gitignore` and `info/exclude` rules are mirrored into the shadow repo so
ignored files never appear in snapshots.
- **Large files are skipped.** Newly-added files larger than 2 MiB are excluded
from snapshots to keep the shadow repo small.
- **Checkpoints only on change.** A checkpoint is recorded only when files
actually changed, so a final no-op model response does not hide the last
meaningful snapshot.
- **Scoped to the working directory.** Snapshot operations are scoped to the
agent's working directory within the worktree, so a sub-directory agent only
captures and restores files under that directory.
- **Garbage collection.** Wiring `snapshot` into `session_end` (or enabling it
globally) runs `git gc` against the shadow repo so old, unreferenced objects
are pruned over time.
## See Also
- [Hooks](../../configuration/hooks/index.md) — the `snapshot`
built-in and the events it can run on.
- [Terminal UI](../tui/index.md) — the `/undo` and
`/snapshots` commands.
- [`examples/snapshot_hooks.yaml`](https://github.com/docker/docker-agent/blob/main/examples/snapshot_hooks.yaml) — a complete snapshot hook configuration.

View File

@@ -0,0 +1,416 @@
---
title: "Terminal UI (TUI)"
description: "docker-agent's default interface is a rich, interactive terminal UI with file attachments, themes, session management, and more."
keywords: docker agent, ai agents, features, terminal ui (tui)
linkTitle: "Terminal UI"
weight: 10
---
_docker-agent's default interface is a rich, interactive terminal UI with file attachments, themes, session management, and more._
![docker-agent TUI in action showing an interactive agent session](../../demo.gif)
## Launching the TUI
```bash
# Launch with a config
$ docker agent run agent.yaml
# Start with an initial message
$ docker agent run agent.yaml "Help me refactor this code"
# Auto-approve all tool calls
$ docker agent run agent.yaml --yolo
# Enable debug logging
$ docker agent run agent.yaml --debug
# Override the application name shown in the status bar and window title
$ docker agent run agent.yaml --app-name "My Project"
# Preselect a color theme
$ docker agent run agent.yaml --theme dracula
# Hide the sidebar (cannot be re-enabled via Ctrl+B)
$ docker agent run agent.yaml --sidebar=false
# Disable specific slash commands
$ docker agent run agent.yaml --disable-commands="/cost,/eval,/model"
# Open in read-only mode to review a past session without sending new messages
$ docker agent run agent.yaml --session -1 --session-read-only
# Use the lean TUI for this run
$ docker agent run agent.yaml --lean
```
### Lean TUI
The lean TUI uses a simplified terminal interface with minimal chrome. To make it the default for interactive runs, set `lean` in your user config:
```yaml
# ~/.config/cagent/config.yaml
settings:
lean: true
```
Omit `lean` or set it to `false` to keep the full TUI as the default. You can still use `--lean` for a single run, or `--lean=false` to use the full TUI when `settings.lean` is enabled.
## Slash Commands
Type `/` during a session to see available commands, or press <kbd>Ctrl</kbd>+<kbd>K</kbd> for the command palette:
| Command | Description |
| ------------------ | ------------------------------------------------------------------------------------ |
| `/new` | Start a new conversation |
| `/clear` | Clear the current conversation (keep session, drop messages) |
| `/compact` | Summarize and compact the conversation history |
| `/fork` | Fork the current session into a new branch |
| `/copy` | Copy the entire conversation to clipboard |
| `/copy-last` | Copy only the last assistant message to clipboard |
| `/undo` | Restore file changes from the latest snapshot (only when snapshots are enabled) |
| `/snapshots` | List captured snapshots (only when snapshots are enabled) |
| `/export` | Export the session as HTML |
| `/sessions` | Browse and load past sessions |
| `/model` | Change the model for the current agent |
| `/effort` | Set the current model's reasoning-effort level (`/effort <none\|minimal\|low\|medium\|high\|xhigh\|max>`, reasoning models only) |
| `/theme` | Change the color theme |
| `/yolo` | Toggle automatic tool call approval |
| `/title` | Set or regenerate session title |
| `/attach` | Attach a file to your message |
| `/shell` | Open a shell |
| `/star` | Star/unstar the current session |
| `/cost` | Show cost breakdown for this session |
| `/eval` | Create an evaluation report |
| `/pause` | Pause/resume the runtime loop. While the agent is mid-request, the resize handle shows "Pausing…" until the in-flight request completes; once the loop is blocked the indicator changes to "⏸ Paused". Run `/pause` again to resume. |
| `/tools` | Show every toolset (with lifecycle state) and the tools they expose |
| `/skills` | List skills available to the current agent |
| `/toolset-restart` | Force a supervisor-driven reconnect of the named toolset (`/toolset-restart <name>`) |
| `/permissions` | Inspect and edit tool permission rules |
| `/split-diff` | Toggle split-diff view for file edits |
| `/speak` | Voice input via system speech-to-text (macOS only) |
| `/exit` | Exit the application (aliases: `/quit`, `/q`) |
Slash commands (both built-in and named) execute immediately when entered. Regular chat messages are queued and processed in order. This means you can invoke a slash command to interrupt or direct the agent even while it is mid-response.
### Agents Panel
The sidebar's **Agents** section lists every agent in the team. The current agent is shown as a focus **card** (rendered in place at its position in the list) with its name, a wrapped description, its full `provider/model`, and a thinking line. Every other agent is shown as a compact **two-line row** — line 1 is the shortcut/spinner, the agent name (in its accent color), and a right-aligned thinking **gauge**; line 2 is the indented full `provider/model` — so a large team stays scannable while still showing each model. Agents are separated by a blank line so the two-line rows stay visually distinct. The effort **gauge** is the only visual language for thinking; the focus card and the Agent Inspector spell out the exact level alongside it. Left-click any agent to switch to it.
#### Agent inspector
Open a read-only **Agent Inspector** to inspect any agent's full configuration combined with its live state. The instruction/system prompt is deliberately omitted; everything else the agent declares is shown:
- **Right-click any agent** (card or row) to open the inspector without switching to it.
- **<kbd>Ctrl</kbd>+left-click any agent** does the same — a fallback for terminals that don't forward right-clicks.
- **Left-click** always switches to the agent.
The title is rendered in the agent's accent color. Sections appear in this order, and any empty section is omitted:
- **Description** — the agent's wrapped description.
- **Live state** — a `● current agent` line when the inspected agent is the one currently running.
- **Model / Fallback / Thinking** — the `provider/model`, any fallback models, and the gauge + value thinking line (omitted for models with no selectable thinking, e.g. harness-backed agents).
- **Sub-agents (N) / Handoffs (N) / Skills (N)** — compact, inline, comma-separated lists wrapped to the dialog width.
- **Limits** — the configured per-agent limits that are set, e.g. `Limits: max-iter 50 · history 40 · max-tool-calls 5`.
- **Options** — the enabled option flags, e.g. `Options: add-date · add-environment-info · redact-secrets`.
- **Toolsets (N)** — one line per toolset with a status marker, its name, kind, and tool count, followed by the indented tool names.
- **Commands (N)** — the slash commands the agent defines, each with its description.
Each toolset carries a single-width status marker reflecting its **live** lifecycle: `●` started (serving), `○` stopped (not yet started), or `⚠` error. The tools listed under a toolset are the **live** tool names when it has started; for a toolset that has not started, the inspector instead shows its declared `tools:` allow-list prefixed with `declared:` (and shows nothing when the toolset declares no allow-list and therefore serves every tool). This lets you see both what an agent is configured with and what is actually running, even before the agent has been used.
The dialog scrolls when the content is long; press <kbd>Esc</kbd> to close it. Remote runtimes (which hold no local team config) degrade gracefully — the config-derived sections are simply omitted.
Model identifiers on line 2 are truncated **from the left** (e.g. `…claude-sonnet-4-6`) only when they overflow, so the informative tail (variant/version) is preserved. As the sidebar narrows the model keeps its own line, and near the minimum width line 1's gauge collapses to a single cell to keep the name readable.
The thinking state of each model is shown with a gauge + value on the card and a gauge or badge on the row (no `✻` glyph):
| Model state | Card line | Row badge |
| ---------------------- | ------------------------------ | ---------------------- |
| Effort level | `thinking ▰▰▰▰▱▱ high` | `▰▰▰▰▱▱` (effort gauge) |
| Adaptive budget | `thinking auto adaptive` | `auto` |
| Token budget | `thinking ◉ 8.2K tokens` | `◉ 8.2K` |
| Disabled (capable) | `thinking ▱▱▱▱▱▱ off` (dimmed) | `▱▱▱▱▱▱` (empty gauge) |
| Not reasoning-capable | _(omitted)_ | _(omitted)_ |
The **effort gauge** is a fixed-width six-cell indicator (`▰` filled, `▱` empty) so the badge column stays aligned. It maps the six selectable levels one-to-one onto filled-cell counts — `minimal` → `▰▱▱▱▱▱`, `low` → `▰▰▱▱▱▱`, `medium` → `▰▰▰▱▱▱`, `high` → `▰▰▰▰▱▱`, `xhigh` → `▰▰▰▰▰▱`, `max` → `▰▰▰▰▰▰` — so the cell count alone is lossless, with a low→high color ramp as a secondary cue. A capable-but-disabled model shows a dim empty gauge (`▱▱▱▱▱▱` `off`), adaptive budgets show `auto`, and token budgets keep `◉ <count>`. The same gauge + value renders on the focus card, the Agent Inspector, and the row.
Harness-backed agents (e.g. `claude-code`) show the harness type as their model and no thinking gauge. Press **Shift+Tab** to cycle the current model's thinking-effort level; a `✻ Thinking: <level>` toast confirms the change (useful when the sidebar is hidden).
### Thinking and Tool Details
Reasoning/thinking blocks are collapsed by default and carry a `Thinking` header badge. When collapsed, the TUI shows a short preview and compact tool summaries. Expand a block to see the full thinking content and the real tool renderers, including detailed tool output such as file edit diffs.
To start new sessions with thinking/tool blocks expanded by default, set `expand_thinking` in your user config:
```yaml
# ~/.config/cagent/config.yaml
settings:
expand_thinking: true
```
Set it to `false` or omit it to keep the default collapsed behavior.
### Snapshots, `/undo`, and `/snapshots`
Enable shadow-git snapshots globally in `~/.config/cagent/config.yaml`:
```yaml
settings:
snapshot: true
```
When enabled, docker-agent records filesystem snapshots at turn boundaries. The TUI exposes two slash commands that operate on those snapshots:
- **`/undo`** restores files from the most recent snapshot (one step back).
- **`/snapshots`** opens a dialog showing how many snapshots have been captured and the number of files in each one. Use <kbd>↑</kbd>/<kbd>↓</kbd> (or <kbd>j</kbd>/<kbd>k</kbd>) to highlight an entry, then press <kbd>r</kbd> to reset the workspace to that point. Pick `<original>` to revert every snapshot and bring the workspace back to its pre-agent state. <kbd>Esc</kbd> closes the dialog without changing anything.
Neither command removes messages from the session transcript — they only touch files on disk. Both commands (and the matching command-palette entries) are hidden when snapshots are turned off. Omit `snapshot` or set it to `false` to leave automatic snapshots off; agents can still configure snapshot hooks manually.
See [Snapshots](../snapshots/index.md) for how the shadow-git machinery works and how to wire it per-agent.
## File Attachments
Attach file contents to your messages using the `@` trigger:
1. Type `@` to open the file completion menu
2. Start typing to filter files (respects `.gitignore`)
3. Select a file to insert the reference
```bash
# In the chat input:
Explain what the code in @pkg/agent/agent.go does
```
The agent receives the full file contents in a structured `<attachments>` block, while the UI shows just the reference.
## Runtime Model Switching
Change the AI model during a session with `/model` or <kbd>Ctrl</kbd>+<kbd>M</kbd>:
1. Press <kbd>Ctrl</kbd>+<kbd>M</kbd> or type `/model`
2. Select from config models or type a custom `provider/model`
3. The model switch is saved with the session and restored on reload
When a models gateway is configured (`--models-gateway`) and it exposes an OpenAI-style `/v1/models` endpoint, the picker lists the models actually served by the gateway (merged with the models defined in the agent config). When the gateway doesn't expose `/v1/models`, the picker falls back to the regular catalog.
> [!TIP]
> Use model switching to try a more capable model for complex tasks, or a cheaper one for simple queries — without modifying your YAML config.
## Editable Messages
Edit any previous user message to branch the conversation. Click on a past message to modify it — the agent will re-process from that point, while the original session history is preserved. This is great for exploring alternative approaches without losing your work.
## Error Recovery
When an agent turn fails (fatal model error, hook block, loop detection, tool-setup failure), the TUI displays the error in the message stream and persists it to the session store. Errors survive a reload and are shown exactly where they occurred, making them visible in shared or remote sessions.
Each error message includes a clickable **↻ retry** button. Clicking it resumes the conversation from the point of failure — without retyping your last message. This lets you recover from transient failures (rate limits, network blips, model API errors) in one click.
## Session Management
docker-agent automatically saves your sessions. Use `/sessions` to browse past conversations:
- **Browse** past sessions with search and filtering
- **Star** important sessions with `/star`
- **Branch** conversations by editing any previous user message — preserving the original session history
- **Resume** sessions with `docker agent run config.yaml --session <id>`
- **Relative refs**: `--session -1` for the last session, `-2` for the one before
### Session Title Editing
Customize session titles to make them more meaningful and easier to find. By default, docker-agent auto-generates titles based on your first message, but you can override or regenerate them at any time.
**Using the `/title` command:**
```bash
/title # Regenerate title using AI (based on recent messages)
/title My Custom Title # Set a specific title
```
**Using the sidebar:**
1. Click the pencil icon (✎) next to the session title in the sidebar
2. Type your new title
3. Press <kbd>Enter</kbd> to save, or <kbd>Escape</kbd> to cancel
> [!NOTE]
> Manually set titles are preserved and won’t be overwritten by auto-generation. Title changes are persisted immediately to the session.
## Keyboard Shortcuts
| Shortcut | Action |
| ---------- | ----------------------------------------------- |
| Ctrl+K | Open command palette |
| Ctrl+M | Switch model |
| Ctrl+R | Reverse history search (search previous inputs) |
| Ctrl+G | Cancel reverse history search |
| Ctrl+S | Cycle to next agent in the team |
| Shift+Tab | Cycle the current model's thinking-effort level (shows a `✻ Thinking: <level>` toast) |
| Ctrl+1 – 9 | Switch directly to agent _N_ in the team list |
| Ctrl+T | Open a new tab (additional agent session) |
| Ctrl+W | Close the current tab |
| Ctrl+N | Next tab |
| Ctrl+P | Previous tab |
| Ctrl+B | Toggle the sidebar (full-UI mode only; disabled when --sidebar=false) |
| Ctrl+Y | Toggle YOLO mode (auto-approve tool calls) |
| Ctrl+O | Toggle hide tool results |
| Ctrl+Z | Suspend TUI to background (resume with `fg`) |
| Ctrl+X | Clear queued messages |
| Escape | Cancel current operation |
| Enter | Send message (or newline with Shift+Enter) |
| Up/Down | Navigate message history |
Press <kbd>Ctrl</kbd>+<kbd>H</kbd> to view the complete list of all available keyboard shortcuts.
### Custom Keybindings
You can remap the shortcuts above by adding a `keybindings` list to the `settings` block of your `~/.config/cagent/config.yaml`. Each entry maps an action to one or more key combinations in [Bubbles key format](https://github.com/charmbracelet/bubbles) (for example `ctrl+q`, `alt+enter`, `f2`). Unlisted actions keep their defaults.
This is the recommended way to replace the `Ctrl+J` newline fallback, which conflicts with common editor/terminal shortcuts (for example inside VS Code).
```yaml
settings:
keybindings:
# Insert a newline with Alt+Enter instead of Ctrl+J. Shift+Enter still
# works automatically on terminals that report it.
- action: "editor_newline"
keys: ["alt+enter"]
# Allow several keys for one action.
- action: "commands"
keys: ["f2", "ctrl+k"]
- action: "quit"
keys: ["ctrl+q"]
```
**Valid actions:**
| Action | Default | Description |
| -------------------------- | ------------ | -------------------------------------- |
| `editor_send` | `enter` | Send the current message |
| `editor_newline` | `ctrl+j` | Insert a newline in the input |
| `quit` | `ctrl+c` | Quit (opens the exit confirmation) |
| `switch_focus` | `tab` | Switch focus between panels |
| `commands` | `ctrl+k` | Open the command palette |
| `help` | `ctrl+h` | Show the help dialog |
| `toggle_yolo` | `ctrl+y` | Toggle YOLO mode |
| `toggle_hide_tool_results` | `ctrl+o` | Toggle hiding tool results |
| `cycle_agent` | `ctrl+s` | Cycle to the next agent |
| `model_picker` | `ctrl+m` | Open the model picker |
| `clear_queue` | `ctrl+x` | Clear queued messages |
| `suspend` | `ctrl+z` | Suspend the TUI |
| `toggle_sidebar` | `ctrl+b` | Toggle the sidebar |
| `edit_external` | `ctrl+g` | Edit input in an external editor |
| `history_search` | `ctrl+r` | Incremental history search |
`Shift+Enter` for newline is detected from your terminal's capabilities and is always available where supported, independent of `editor_newline`.
Invalid entries are ignored with a warning (visible with `--debug`) so a bad config never breaks the TUI: unknown actions, empty or malformed keys, and keys that would collide with another action are dropped while every other binding keeps working.
## History Search
Press <kbd>Ctrl</kbd>+<kbd>R</kbd> to enter incremental history search mode. Start typing to filter through your previous inputs. Press <kbd>Enter</kbd> to select a match, or <kbd>Escape</kbd> to cancel.
## Theming
Customize the TUI appearance with built-in or custom themes:
```bash
# Switch themes interactively
/theme
```
### Built-in Themes
`default`, `catppuccin-latte`, `catppuccin-mocha`, `dracula`, `gruvbox-dark`, `gruvbox-light`, `nord`, `one-dark`, `solarized-dark`, `tokyo-night`
### Custom Themes
Create theme files in `~/.cagent/themes/` as YAML. Theme files are **partial overrides** — you only need to specify the colors you want to change. Any omitted keys fall back to the built-in default theme values.
```yaml
# ~/.cagent/themes/my-theme.yaml
name: "My Custom Theme"
colors:
# Backgrounds
background: "#1a1a2e"
background_alt: "#16213e"
# Text colors
text_bright: "#ffffff"
text_primary: "#e8e8e8"
text_secondary: "#b0b0b0"
text_muted: "#707070"
# Accent colors
accent: "#4fc3f7"
brand: "#1d96f3"
# Status colors
success: "#4caf50"
error: "#f44336"
warning: "#ff9800"
info: "#00bcd4"
# Optional: Customize syntax highlighting colors
chroma:
comment: "#6a9955"
keyword: "#569cd6"
literal_string: "#ce9178"
# Optional: Customize markdown rendering colors
markdown:
heading: "#4fc3f7"
link: "#569cd6"
code: "#ce9178"
```
### Applying Themes
**In user config** (`~/.config/cagent/config.yaml`):
```yaml
settings:
theme: my-theme # References ~/.cagent/themes/my-theme.yaml
```
**At launch:** Pass `--theme <name>` to `docker agent run` to preselect a theme for that session. This overrides `settings.theme` in your config but is not saved. Invalid theme names print an error at startup listing the available options. Has no effect in `--exec` mode.
**At runtime:** Use the `/theme` command to open the theme picker and select from available themes. Your selection is saved globally in `~/.config/cagent/config.yaml` under `settings.theme` and persists across sessions.
> [!TIP]
> **Hot Reload**
>
> Custom themes auto-reload when you save changes to the file — no restart needed. This makes it easy to tweak colors in real-time.
> [!WARNING]
> **Partial overrides**
>
> All user themes are applied on top of the `default` theme. If you want to customize a built-in theme (e.g., `dracula`), copy its full YAML from the [built-in themes on GitHub](https://github.com/docker/docker-agent/tree/main/pkg/tui/styles/themes) into `~/.cagent/themes/` and edit the copy. Otherwise, omitted values will use `default` colors, not the original theme's colors.
## Tool Permissions
When an agent calls a tool, docker-agent shows a confirmation dialog by default. You can:
- **Approve once** — Allow this specific call
- **Always allow** — Permanently approve this tool/command for the session
- **Deny** — Reject the tool call
**Granular permissions:** The permission system supports pattern-based matching. When you “Always allow” a specific tool command, only that exact pattern is auto-approved — other commands from the same tool still require confirmation. This lets you auto-approve safe, read-only operations while maintaining control over destructive ones.
> [!TIP]
> **YOLO mode**
>
> Use `--yolo` or the `/yolo` command to auto-approve all tool calls. You can also toggle this mid-session. For aliases, set `--yolo` when creating the alias: `docker agent alias add fast agentcatalog/coder --yolo`.
## Notifications
The TUI displays transient notification banners for agent warnings, errors, and other runtime events. Notifications auto-dismiss after a short delay unless the mouse is hovering over them — hovering pauses the timer so you have time to read the message.
| Interaction | Behaviour |
| ----------- | --------- |
| Hover | Pauses auto-dismiss; the notification stays visible until the mouse moves away |
| Click | Copies the notification text to the clipboard |
| × (close) | Dismisses immediately; the glyph turns red when hovered |
Hint text in the top-left corner of the notification border shows the available actions at a glance.

View File

@@ -0,0 +1,5 @@
---
title: "Getting Started"
description: "Install Docker Agent and build your first agent."
weight: 10
---

View File

@@ -0,0 +1,148 @@
---
title: "Installation"
description: "Get docker-agent running on your system in minutes."
keywords: docker agent, ai agents, getting started, installation
weight: 20
---
_Get docker-agent running on your system in minutes._
## Prerequisites
- An API key for at least one AI provider (OpenAI, Anthropic, Google, etc.)
- **Optional:** [Docker Desktop](https://www.docker.com/products/docker-desktop/) — for running containerized MCP tools and Docker Model Runner
## Docker Desktop (Pre-installed)
Starting with [Docker Desktop 4.63](https://docs.docker.com/desktop/release-notes/#4630), **docker-agent is already available**. No separate installation needed — just open a terminal and run:
```bash
$ docker agent version
```
> [!TIP]
> Docker Desktop bundles docker-agent and keeps it up to date. This is the easiest way to get started, especially if you want to use Docker MCP tools and Docker Model Runner.
## Homebrew (macOS / Linux)
Install docker-agent using [Homebrew](https://brew.sh/):
```bash
# Install
$ brew install docker-agent
# Verify
$ docker-agent version
```
You can also install docker-agent as a docker CLI plugin, by copying `docker-agent` binary in `~/.docker/cli-plugins`. You can then run `docker agent version`.
## Download Binary Releases
Download [prebuilt binary releases](https://github.com/docker/docker-agent/releases) for Windows, macOS, and Linux from the GitHub Releases page.
### macOS / Linux
```bash
# Download the latest release
OS=$(uname -s | tr '[:upper:]' '[:lower:]')
ARCH=$(uname -m); case "$ARCH" in x86_64) ARCH=amd64;; aarch64) ARCH=arm64;; esac
curl -L "https://github.com/docker/docker-agent/releases/latest/download/docker-agent-${OS}-${ARCH}" -o docker-agent
chmod +x docker-agent
sudo mv docker-agent /usr/local/bin/
docker-agent version
# or alternatively, instead of moving to /usr/local/bin:
mkdir -p ~/.docker/cli-plugins
sudo mv docker-agent ~/.docker/cli-plugins
docker agent version
```
### Windows
Download `docker-agent-windows-amd64.exe` from the [releases page](https://github.com/docker/docker-agent/releases), rename it to `docker-agent.exe` and add it to your PATH. Alternatively you can move it to `~/.docker/cli-plugins`
## Optional Self-Updates
When docker-agent is installed from a standalone GitHub release binary, you can opt in to automatic self-updates by setting `DOCKER_AGENT_AUTO_UPDATE` to a truthy value (`1`, `true`, `yes`, or `on`):
```bash
# Enable for one command
DOCKER_AGENT_AUTO_UPDATE=1 docker agent run
# Or enable for the current shell session
export DOCKER_AGENT_AUTO_UPDATE=1
docker agent run
```
With self-updates enabled, docker-agent checks the latest GitHub release before normal commands run. If a newer release exists and your session is interactive, docker-agent asks whether you want to install it or keep running your current version. When the answer is yes (or the session is non-interactive, such as CI or piped input, in which case the update proceeds automatically), it downloads the asset for your OS and architecture, verifies the release-provided SHA-256 digest/checksum, replaces the current binary, and restarts the command with the same arguments.
Self-updates are fail-safe: if checking, downloading, verifying, installing, or restarting fails, docker-agent keeps running the current binary. Version/help/completion commands and Docker CLI plugin metadata handshakes do not trigger self-updates.
> [!NOTE]
> **Package-manager installs**
>
> Docker Desktop and Homebrew already manage docker-agent updates. Prefer those update mechanisms when you installed docker-agent that way. Self-updates are mainly intended for standalone release binaries.
## Build from Source
For the latest features, or to contribute, build from source:
### Prerequisites
- [Go 1.26](https://go.dev/dl/) or higher
- [Task](https://taskfile.dev/installation/) (build tool)
- [golangci-lint](https://golangci-lint.run/docs/welcome/install/local/) (for linting)
```bash
# Clone the repository
git clone https://github.com/docker/docker-agent.git
cd docker-agent
# Build the binary
task build
# The binary is at ./bin/docker-agent
./bin/docker-agent --help
```
> [!TIP]
> **Building on Windows**
>
> On Windows, use `task build-local` instead of `task build`. This builds the binary inside a Docker container using Docker Buildx, which avoids issues with Windows-specific toolchain setup and CGo cross-compilation. The output goes to the `./dist` directory.
## Set Up API Keys
docker-agent needs API keys for the model providers you want to use. Set them as environment variables:
```bash
# Pick one (or more) depending on your provider
export OPENAI_API_KEY="sk-..." # OpenAI
export ANTHROPIC_API_KEY="sk-ant-..." # Anthropic
export GOOGLE_API_KEY="AI..." # Google Gemini (or GEMINI_API_KEY)
export MISTRAL_API_KEY="..." # Mistral
export OPENROUTER_API_KEY="..." # OpenRouter
```
See [Configuration Overview](../../configuration/overview/index.md#environment-variables) for the full list of supported providers and environment variables.
> [!NOTE]
> You only need the key(s) for the provider(s) you configure in your agent YAML. If you use Docker Model Runner (DMR), no API key is needed — models run locally.
## Verify Installation
```bash
# Check the version
$ docker agent version
# Run the default agent
$ docker agent run
# Or try a built-in example
$ docker agent run agentcatalog/pirate
```
## What's Next?
- [**Quick Start**](../quickstart/index.md) — create and run your first agent in under 5 minutes.
- [**Troubleshooting**](../../community/troubleshooting/index.md) — something not working? Debug mode, common issues, and solutions.

View File

@@ -0,0 +1,73 @@
---
title: "Introduction"
description: "Docker Agent is a multi-agent runtime that lets you build, run, and share AI agents with a YAML or HCL config — no glue code required."
keywords: docker agent, ai agents, getting started, introduction
weight: 10
---
_Docker Agent is a multi-agent runtime that lets you build, run, and share AI agents with a YAML or HCL config — no glue code required._
## What is Docker Agent?
Docker Agent is an open-source tool from Docker that orchestrates AI
agents with specialized capabilities and tools. Instead of writing
code to wire up LLMs, tools, and workflows, you **declare** your
agents in YAML or HCL — their model, personality, tools, and how they
collaborate — and Docker Agent handles the rest.
- **Multi-Agent Architecture** — build hierarchical teams of agents that specialize in different tasks and delegate work to each other.
- **Rich Tool Ecosystem** — built-in tools for files, shell, memory, and todos. Extend with any MCP server from [Docker's MCP catalog](https://hub.docker.com/u/mcp).
- **Multi-Model Support** — OpenAI, Anthropic, Google Gemini, AWS Bedrock, Docker Model Runner, and reusable provider definitions with shared defaults.
- **Package & Share** — push agents to OCI registries and pull them anywhere, just like Docker images.
- **Multiple Interfaces** — interactive TUI, headless CLI, HTTP API server, MCP mode, and A2A protocol support.
- **Security-First Design** — tool confirmation prompts, containerized MCP tools via Docker, client isolation, and resource scoping.
## Why Docker Agent?
After spending years building AI agents using various frameworks, the
Docker team kept asking the same questions:
- **How do we make building agents less of a hassle?** — Most agents
use the same building blocks. Docker Agent provides them out of the
box.
- **Can we reuse those building blocks?** — Declarative YAML or HCL
configs mean you can mix and match agents, models, and tools without
rewriting code.
- **How can we share agents easily?** — Push agents to any OCI
registry and run them anywhere with a single command.
Docker Agent is built in the open so the community can make use of
this work and contribute to its future.
## How it works
At its core, Docker Agent follows a simple loop:
1. **You define agents** in YAML or HCL — their model, instructions, tools, and sub-agents.
2. **You run an agent** via the TUI, CLI, or API.
3. **The agent processes your request** — calling tools, delegating to sub-agents, and reasoning step by step.
4. **Results stream back in real-time** via an event-driven architecture.
```yaml
# A minimal agent definition
agents:
root:
model: openai/gpt-5
description: A helpful assistant
instruction: You are a helpful assistant.
toolsets:
- type: think
```
```bash
# Run it
$ docker agent run agent.yaml
```
> [!TIP]
> Jump straight to the [Quick Start](../quickstart/index.md) if you want to build your first agent right away.
## What's next?
- [**Installation**](../installation/index.md) — install Docker Agent on macOS, Linux, or Windows.
- [**Quick Start**](../quickstart/index.md) — build your first agent in under 5 minutes.

View File

@@ -0,0 +1,144 @@
---
title: "Quick Start"
description: "Get up and running with Docker Agent in under 5 minutes. Pick whichever path suits you best."
keywords: docker agent, ai agents, getting started, quick start
weight: 30
aliases:
- /ai/docker-agent/tutorial/
---
_Get up and running with Docker Agent in under 5 minutes. Pick whichever path suits you best._
## Option A: Run the Default Agent
The fastest way to try Docker Agent — no config file needed:
```bash
# Launch the default agent with the interactive TUI
$ docker agent run
```
This starts a general-purpose assistant with sensible defaults. Just start chatting.
## Option B: Run a Pre-Built Agent from the Registry
Try a ready-made agent from the [agent catalog](https://hub.docker.com/u/agentcatalog) — no YAML needed:
```bash
# Run a pirate-themed assistant
$ docker agent run agentcatalog/pirate
# Run a coding agent
$ docker agent run agentcatalog/coder
```
## Option C: Generate a Config Interactively
Use the `docker agent new` command to scaffold a config file through prompts:
```bash
# Interactive wizard
$ docker agent new
# Or specify options directly
$ docker agent new --model openai/gpt-5
# Override iteration limits
$ docker agent new --model dmr/ai/gemma3-qat:12B --max-iterations 15
```
This generates an `agent.yaml` in the current directory. Then run it:
```bash
$ docker agent run agent.yaml
```
## Option D: Write Your Own Config
Create an `agent.yaml` by hand for full control. Here's a minimal example:
```yaml
agents:
root:
model: anthropic/claude-sonnet-4-5
description: A helpful coding assistant
instruction: |
You are an expert software developer. Help users write
clean, efficient code. Explain your reasoning.
toolsets:
- type: filesystem
- type: shell
- type: think
```
This gives your agent:
- **Claude Sonnet 4.5** as the underlying model
- **Filesystem access** to read and write files
- **Shell access** to run commands
- **Think tool** for step-by-step reasoning
```bash
# Launch the interactive terminal UI
$ docker agent run agent.yaml
```
> [!TIP]
> **Prefer HCL?**
>
> You can write the same config as `agent.hcl` using labeled blocks and heredocs. See [HCL Configuration](../../configuration/hcl/index.md).
## Try It Out
Once your agent is running, try asking it to:
- _"List the files in the current directory"_
- _"Create a Python script that fetches weather data"_
- _"Explain what the code in main.go does"_
> [!TIP]
> Add `--yolo` to auto-approve all tool calls: `docker agent run agent.yaml --yolo`
## Non-Interactive Mode
Use `docker agent run --exec` for one-shot tasks:
```bash
# Ask a single question
$ docker agent run --exec agent.yaml "Create a Dockerfile for a Node.js app"
# Pipe input
$ cat error.log | docker agent run --exec agent.yaml "What's wrong in this log?"
```
## Add More Power
Give your agent persistent memory and web search:
```yaml
agents:
root:
model: anthropic/claude-sonnet-4-5
description: Research assistant with memory
instruction: |
You are a research assistant. Search the web for information,
remember important findings, and provide thorough analysis.
toolsets:
- type: think
- type: memory
path: ./research.db
- type: mcp
ref: docker:duckduckgo
```
> [!NOTE]
> **Docker MCP Tools**
>
> The `ref: docker:duckduckgo` syntax runs the DuckDuckGo MCP server in a Docker container. This is the recommended way to use MCP tools — secure, isolated, and easy to configure. Requires Docker Desktop.
## What's Next?
- [**Understand Agents**](../../concepts/agents/index.md) — learn how agents work and what you can configure.
- [**Multi-Agent Systems**](../../concepts/multi-agent/index.md) — build teams of collaborating agents.
- [**Configuration Reference**](../../configuration/overview/index.md) — full reference for all YAML and HCL options.
- [**Troubleshooting**](../../community/troubleshooting/index.md) — something not working? Debug tips and common fixes.

View File

@@ -0,0 +1,5 @@
---
title: "Guides"
description: "Practical guides for building and running agents."
weight: 70
---

View File

@@ -0,0 +1,615 @@
---
title: "Go SDK"
description: "Use docker-agent as a Go library to embed AI agents in your applications."
keywords: docker agent, ai agents, guides, go sdk
weight: 40
---
_Use docker-agent as a Go library to embed AI agents in your applications._
## Overview
docker-agent can be used as a Go library, allowing you to build AI agents directly into your Go applications. This gives you full programmatic control over agent creation, tool integration, and execution.
> [!NOTE]
> **Import Path**
>
> ```go
> import "github.com/docker/docker-agent/pkg/..."
> ```
## Core Packages
| Package | Purpose |
| ---------------------- | ---------------------------------------- |
| `pkg/agent` | Agent creation and configuration |
| `pkg/runtime` | Agent execution and event streaming |
| `pkg/session` | Conversation state management |
| `pkg/team` | Multi-agent team composition |
| `pkg/tools` | Tool interface and utilities |
| `pkg/tools/builtin` | Built-in tools (shell, filesystem, etc.) |
| `pkg/model/provider/*` | Model provider clients |
| `pkg/config/latest` | Configuration types |
| `pkg/environment` | Environment and secrets |
| `pkg/embeddedchat` | Headless chat session for embedding the agent runtime in a custom UI |
| `pkg/tui/components/toolconfirm` | Tool-confirmation policy: `Decision` enum, `BuildPermissionPattern`, key bindings, and rejection-reason presets. Share this instead of copying the permission-pattern logic. |
| `pkg/tui/service` | `StaticSessionState` — a `SessionStateReader` with conservative fixed values, for rendering message/tool views outside the full TUI app. Replaces hand-rolled nine-method stubs. |
| `pkg/tui/animation` | `Stopper` / `StopView` — animation lifecycle contract. Call `StopAnimation` on views removed from the UI to prevent leaked tick subscriptions. |
| `pkg/tui/components/transcript` | Embedded transcript view with read-only `Messages()` accessor for observing conversation structure in host tests and persistence layers. |
## Embedding TUI Components
When building custom UIs on top of docker-agent's TUI primitives, four packages define the contracts that keep the runtime and the UI in sync:
- **`pkg/tui/components/toolconfirm`** — import this package for the permission-decision policy rather than copying the pattern-building logic. The `Decision` enum, `BuildPermissionPattern` helper, and rejection-reason presets are the canonical source of truth: whatever pattern is shown to the user in the confirmation dialog is exactly the pattern granted to the runtime.
- **`pkg/tui/service`** — use `StaticSessionState` as a stub `SessionStateReader` when rendering individual message or tool views outside the full TUI app. It returns conservative fixed values for all nine interface methods, eliminating the need for hand-rolled stubs.
- **`pkg/tui/animation`** — implement `animation.Stopper` on any view that owns a tick-based animation. Call `StopAnimation` whenever a view is removed from the UI hierarchy to prevent leaked `time.Tick` subscriptions from firing against a dead view.
- **`pkg/tui/components/transcript`** — embed the transcript view for displaying conversation history. Use the `Messages()` method to read the current slice of transcript messages (treat as read-only — mutations desync renders). This is useful for host-side tests asserting on chat history, and for persistence layers that need to snapshot conversation state.
## Headless Embedded Chat (`pkg/embeddedchat`)
`pkg/embeddedchat` is a thin wrapper around the docker-agent runtime that lets you drive an agent from your own UI instead of running docker-agent's Bubble Tea application. It handles runtime construction, event projection, and conversation state, exposing a simple `Send` / `Confirm` / `Restart` / `Close` API.
### Creating a session
```go
import (
"context"
"fmt"
"strings"
dagentcfg "github.com/docker/docker-agent/pkg/config"
dagentruntime "github.com/docker/docker-agent/pkg/runtime"
"github.com/docker/docker-agent/pkg/embeddedchat"
)
chat, err := embeddedchat.New(ctx, embeddedchat.Config{
// AgentSource can be a file path, raw YAML bytes, or an OCI reference.
AgentSource: dagentcfg.NewBytesSource("agent", []byte(agentYAML)),
})
if err != nil {
return err
}
defer chat.Close()
```
### Sending a message and reading events
`Send` appends the user message to the conversation and returns a channel of `Event` values. Drain the channel until it closes.
```go
events, err := chat.Send(ctx, "Hello! What can you do?")
if err != nil {
return err
}
var response strings.Builder
for ev := range events {
switch {
case ev.Text != "":
response.WriteString(ev.Text)
case ev.Tool != nil && ev.Tool.NeedsConfirmation:
// Approve the pending tool call (use ResumeApproveSession to allow all).
if err := chat.Confirm(ctx, dagentruntime.ResumeApprove()); err != nil {
return err
}
case ev.Tool != nil && ev.Tool.Finished:
fmt.Printf("[tool %s finished]\n", ev.Tool.Def.Name)
case ev.Err != nil:
fmt.Printf("error: %v\n", ev.Err)
case ev.Done:
fmt.Println("\n[turn complete]")
}
}
fmt.Print(response.String())
```
### Restarting the conversation
To start a fresh conversation without recreating the runtime:
```go
if err := chat.Restart(); err != nil {
return err
}
```
### Event types
| Field | When set |
| -------------- | ------------------------------------------------------------------------ |
| `Text` | Assistant text delta; accumulate into a string for the full reply. |
| `Tool` | A tool call started, needs confirmation, or finished. |
| `Tool.NeedsConfirmation` | Runtime is blocked until `Confirm` is called. |
| `Tool.Finished` | Tool call completed; `Tool.IsError` is true if it errored. |
| `Err` | A user-facing runtime error; no further content events follow. |
| `Done` | Clean end of turn; no more events. |
| `RuntimeEvent` | The original `runtime.Event` for callers that need the full stream. |
For advanced use (custom elicitation, raw event inspection), call `chat.Runtime()` to access the underlying `runtime.Runtime` directly.
## Optional Provider Build Tags
By default docker-agent includes all four cloud providers (OpenAI, Anthropic, Google, Amazon Bedrock). When embedding docker-agent in your own binary you can compile out unneeded providers — together with their transitive SDK dependencies — to reduce binary size.
Each provider is gated by a negative build tag prefixed `docker_agent_` to avoid collisions with your own project's tags:
| Build tag | Provider dropped | Major dependency removed |
| ---------------------------- | ------------------------ | ------------------------------------------------- |
| `docker_agent_no_openai` | OpenAI | `github.com/openai/openai-go` |
| `docker_agent_no_anthropic` | Anthropic | `github.com/anthropics/anthropic-sdk-go` (partial — see note) |
| `docker_agent_no_google` | Google / Vertex AI | `google.golang.org/genai`, Vertex auth stack, and indirectly the Anthropic and OpenAI SDKs via Vertex Model Garden |
| `docker_agent_no_bedrock` | Amazon Bedrock | `github.com/aws/aws-sdk-go-v2` stack (the largest provider dependency tree) |
To build without Bedrock and OpenAI:
```bash
go build -tags 'docker_agent_no_bedrock docker_agent_no_openai' ./...
```
Requesting a model whose provider was compiled out fails at construction time with a clear `"not compiled into this build"` error. The `dmr` (Docker Model Runner) provider and the rule-based router are always compiled in.
> [!WARNING]
> **Anthropic + Google dependency**
>
> The Google provider's Vertex Model Garden support also imports the Anthropic SDK, so the Anthropic dependency is only fully removed when _both_ `docker_agent_no_anthropic` and `docker_agent_no_google` are set.
## RAG Toolset (opt-out)
The RAG toolset (`type: rag`) is included in `NewDefaultToolsetRegistry()` (from `pkg/teamloader/toolsets`) and `loaderdefaults.Opts()` (from `pkg/teamloader/defaults`, using the conventional import alias `loaderdefaults`).
The underlying tree-sitter code parser uses cgo, but build-tag guards in `pkg/rag/treesitter` mean importing the package is safe regardless of `CGO_ENABLED`: with `CGO_ENABLED=0` the parser stub compiles in and returns a runtime error on first use rather than failing at compile time.
If you want to exclude the RAG toolset from your binary entirely — surfacing a load-time warning on the agent rather than a deferred runtime error from the `!cgo` stub — remove it from the registry before passing it to `teamloader.Load`:
```go
import (
"github.com/docker/docker-agent/pkg/teamloader"
loadertoolsets "github.com/docker/docker-agent/pkg/teamloader/toolsets"
)
// Opt out of the RAG toolset; a config that declares type: rag attaches
// a load-time warning to the agent instead of failing at document processing.
creators := loadertoolsets.DefaultToolsetCreators()
delete(creators, "rag")
registry := teamloader.NewToolsetRegistry(creators)
```
Pass the custom registry via `teamloader.WithToolsetRegistry(registry)` when calling `teamloader.Load`. Note that `teamloader.Load()` does not return an error for unknown toolset types — the failure is recorded as a load-time warning and can be retrieved with `agent.DrainWarnings()`; it is also surfaced via logging and TUI notifications.
## Registering Custom Built-in Themes
When embedding docker-agent, you can contribute your own built-in themes via `styles.RegisterBuiltinThemes`. Registered themes integrate seamlessly with the existing theme picker, `/theme` command, and `settings.theme` config key — they behave exactly like docker-agent's own bundled themes.
```go
import (
"embed"
"github.com/docker/docker-agent/pkg/tui/styles"
)
//go:embed themes/*.yaml
var brandThemes embed.FS
// Call at startup, before applying any persisted theme:
if err := styles.RegisterBuiltinThemes(brandThemes); err != nil {
return err
}
```
Each theme file lives at `themes/<name>.yaml` inside the embedded filesystem and is a **partial override** — only the colors you want to change are required; everything else falls back to `DefaultTheme()`.
```yaml
# themes/brand.yaml
name: Brand
colors:
accent: "#FF6A00"
background: "#1A0F0A"
```
If `name:` is omitted, docker-agent uses the filename stem as the display name in the theme picker (e.g. `brand` from `themes/brand.yaml`).
To replace docker-agent's default theme entirely, ship the file as `themes/default.yaml` — it masks the bundled default while inheriting any colors you don't set.
**Semantics:**
- Registered sources take precedence over bundled themes; a registered ref overrides a bundled theme of the same name.
- Among multiple registered sources, last-registered wins on a collision.
- `RegisterBuiltinThemes` validates eagerly (nil fs, missing `themes/` dir) so errors surface at registration time, not at picker time.
## MCP OAuth Token Persistence
By default, MCP OAuth tokens are stored in-memory only and are not persisted across process restarts. The CLI registers a keyring-backed store automatically at startup; when embedding docker-agent as a library you must do this yourself if you want tokens to survive restarts.
Call `keyringstore.Register()` **before** any MCP toolset is initialised to enable the OS keyring-backed token store:
```go
import "github.com/docker/docker-agent/pkg/tools/mcp/keyringstore"
func main() {
// Must be called before teamloader.Load() on configs with remote MCP
// toolsets; calling it after the store is created panics.
keyringstore.Register()
// ... rest of your startup code
}
```
> [!WARNING]
> **Call order matters**
>
> If `keyringstore.Register()` is called after the default token store has already been lazily initialised, docker-agent panics. The store is initialised when any remote MCP toolset is constructed — which happens inside `teamloader.Load()`. Always call `keyringstore.Register()` before calling `teamloader.Load()` on a config that includes remote MCP toolsets.
If you do not need persistent OAuth tokens (for example, in short-lived batch jobs or tests), omit the call and tokens will be kept in-memory for the process lifetime.
## Basic Example
Create a simple agent and run it:
```go
package main
import (
"context"
"fmt"
"log"
"os/signal"
"syscall"
"github.com/docker/docker-agent/pkg/agent"
"github.com/docker/docker-agent/pkg/config/latest"
"github.com/docker/docker-agent/pkg/environment"
"github.com/docker/docker-agent/pkg/model/provider/openai"
"github.com/docker/docker-agent/pkg/runtime"
"github.com/docker/docker-agent/pkg/session"
"github.com/docker/docker-agent/pkg/team"
)
func main() {
ctx, cancel := signal.NotifyContext(context.Background(),
syscall.SIGINT, syscall.SIGTERM)
defer cancel()
if err := run(ctx); err != nil {
log.Fatal(err)
}
}
func run(ctx context.Context) error {
// Create model provider
llm, err := openai.NewClient(
ctx,
&latest.ModelConfig{
Provider: "openai",
Model: "gpt-4o",
},
environment.NewDefaultProvider(),
)
if err != nil {
return err
}
// Create agent
assistant := agent.New(
"root",
"You are a helpful assistant.",
agent.WithModel(llm),
agent.WithDescription("A helpful assistant"),
)
// Create team and runtime
t := team.New(team.WithAgents(assistant))
rt, err := runtime.New(t)
if err != nil {
return err
}
// Run with a user message
sess := session.New(
session.WithUserMessage("What is 2 + 2?"),
)
messages, err := rt.Run(ctx, sess)
if err != nil {
return err
}
// Print the response
fmt.Println(messages[len(messages)-1].Message.Content)
return nil
}
```
## Custom Tools
Define custom tools for your agent:
```go
package main
import (
"context"
"encoding/json"
"fmt"
"github.com/docker/docker-agent/pkg/tools"
)
// Define the tool's input schema
type AddNumbersArgs struct {
A int `json:"a"`
B int `json:"b"`
}
// Implement the tool handler
func addNumbers(_ context.Context, toolCall tools.ToolCall) (*tools.ToolCallResult, error) {
var args AddNumbersArgs
if err := json.Unmarshal([]byte(toolCall.Function.Arguments), &args); err != nil {
return nil, err
}
result := args.A + args.B
return tools.ResultSuccess(fmt.Sprintf("%d", result)), nil
}
func main() {
// Create the tool definition
addTool := tools.Tool{
Name: "add",
Category: "math",
Description: "Add two numbers together",
Parameters: tools.MustSchemaFor[AddNumbersArgs](),
Handler: addNumbers,
}
// Use with an agent
calculator := agent.New(
"root",
"You are a calculator. Use the add tool for arithmetic.",
agent.WithModel(llm),
agent.WithTools(addTool),
)
// ...
}
```
## Streaming Responses
Process events as they happen:
```go
func runStreaming(ctx context.Context, rt runtime.Runtime, sess *session.Session) error {
events := rt.RunStream(ctx, sess)
for event := range events {
switch e := event.(type) {
case *runtime.StreamStartedEvent:
fmt.Println("Stream started")
case *runtime.AgentChoiceEvent:
// Print response chunks as they arrive
fmt.Print(e.Content)
case *runtime.ToolCallEvent:
fmt.Printf("\n[Tool call: %s]\n", e.ToolCall.Function.Name)
case *runtime.ToolCallConfirmationEvent:
// Auto-approve tool calls
rt.Resume(ctx, runtime.ResumeRequest{
Type: runtime.ResumeTypeApproveSession,
})
case *runtime.ToolCallResponseEvent:
fmt.Printf("[Tool response: %s]\n", e.Response)
case *runtime.StreamStoppedEvent:
fmt.Println("\nStream stopped")
case *runtime.ErrorEvent:
return fmt.Errorf("error: %s", e.Error)
}
}
return nil
}
```
## Multi-Agent Teams
Create agents that delegate to sub-agents:
```go
package main
import (
"github.com/docker/docker-agent/pkg/agent"
"github.com/docker/docker-agent/pkg/team"
"github.com/docker/docker-agent/pkg/tools/builtin"
)
func createTeam(llm provider.Provider) *team.Team {
// Create a child agent
researcher := agent.New(
"researcher",
"You research topics thoroughly.",
agent.WithModel(llm),
agent.WithDescription("Research specialist"),
)
// Create root agent with sub-agents
coordinator := agent.New(
"root",
"You coordinate research tasks.",
agent.WithModel(llm),
agent.WithDescription("Team coordinator"),
agent.WithSubAgents(researcher),
agent.WithToolSets(builtin.NewTransferTaskTool()),
)
return team.New(team.WithAgents(coordinator, researcher))
}
```
## Built-in Tools
Use docker-agent's built-in tools:
```go
import (
"github.com/docker/docker-agent/pkg/config"
"github.com/docker/docker-agent/pkg/tools/builtin"
)
func createAgentWithBuiltinTools(llm provider.Provider) *agent.Agent {
// Runtime config for tools that need it
rtConfig := &config.RuntimeConfig{
Config: config.Config{
WorkingDir: "/path/to/workdir",
},
}
return agent.New(
"root",
"You are a developer assistant.",
agent.WithModel(llm),
agent.WithToolSets(
// Shell tool for running commands
builtin.NewShellTool(os.Environ(), rtConfig),
// Filesystem tools
builtin.NewFilesystemTool(rtConfig.Config.WorkingDir),
// Think tool for reasoning
builtin.NewThinkTool(),
// Todo tool for task tracking
builtin.NewTodoTool(),
),
)
}
```
## HTTP Middleware / Transport Wrappers
Use `options.WithHTTPTransportWrapper` to inject HTTP middleware into the transport chain of all provider clients built by docker-agent. This is useful for request tracing, injecting custom headers, collecting metrics, or any other cross-cutting concern at the HTTP layer.
```go
import (
"net/http"
"github.com/docker/docker-agent/pkg/model/provider/options"
)
type headerTransport struct {
base http.RoundTripper
}
func (t *headerTransport) RoundTrip(req *http.Request) (*http.Response, error) {
req = req.Clone(req.Context())
req.Header.Set("X-Request-Source", "my-app")
return t.base.RoundTrip(req)
}
// Example: add a custom header to every outbound LLM request
wrapper := options.WithHTTPTransportWrapper(
func(base http.RoundTripper) http.RoundTripper {
return &headerTransport{base: base}
},
)
client, err := openai.NewClient(ctx, &latest.ModelConfig{
Provider: "openai",
Model: "gpt-4o",
}, env, wrapper)
```
The wrapper receives the already-instrumented transport (OpenTelemetry, SSE decompression, Desktop proxy support) as its `base` argument, so wrapping it preserves all built-in behaviour.
**Supported providers:** Anthropic, OpenAI, Gemini (GeminiAPI backend), Bedrock. Works in both direct and gateway/proxy mode.
> [!WARNING]
> **Vertex AI not supported**
>
> Vertex AI uses an ADC-managed HTTP client that docker-agent cannot intercept. When a transport wrapper is set, docker-agent falls back to the GeminiAPI backend instead of Vertex AI — a debug message is logged.
In **gateway mode** the wrapper is called on every LLM request because gateway clients are rebuilt each call for short-lived auth tokens. In **direct mode** it is called once at client construction. Rate-limit responses (HTTP 429) are classified as non-retryable by the runtime and cause the model chain to skip to the next fallback, so wrappers that track per-request outcomes will observe these as failures rather than retried calls.
Returning `nil` from your wrapper function is not allowed; docker-agent logs a warning and keeps the original transport instead.
## Using Different Providers
```go
import (
"github.com/docker/docker-agent/pkg/model/provider/anthropic"
"github.com/docker/docker-agent/pkg/model/provider/gemini"
"github.com/docker/docker-agent/pkg/model/provider/openai"
)
// OpenAI
openaiClient, _ := openai.NewClient(ctx, &latest.ModelConfig{
Provider: "openai",
Model: "gpt-4o",
}, env)
// Anthropic
anthropicClient, _ := anthropic.NewClient(ctx, &latest.ModelConfig{
Provider: "anthropic",
Model: "claude-sonnet-4-5",
}, env)
// Google Gemini
geminiClient, _ := gemini.NewClient(ctx, &latest.ModelConfig{
Provider: "google",
Model: "gemini-3.5-flash",
}, env)
```
## Session Options
```go
import "github.com/docker/docker-agent/pkg/session"
sess := session.New(
// Set a title for the session
session.WithTitle("Code Review Task"),
// Add user message
session.WithUserMessage("Review this code for bugs"),
// Limit iterations
session.WithMaxIterations(20),
)
```
## Error Handling
```go
messages, err := rt.Run(ctx, sess)
if err != nil {
if errors.Is(err, context.Canceled) {
// User cancelled
log.Println("Operation cancelled")
return nil
}
if errors.Is(err, context.DeadlineExceeded) {
// Timeout
log.Println("Operation timed out")
return nil
}
// Other error
return fmt.Errorf("runtime error: %w", err)
}
// Check for errors in the event stream
for event := range rt.RunStream(ctx, sess) {
if errEvent, ok := event.(*runtime.ErrorEvent); ok {
return fmt.Errorf("stream error: %s", errEvent.Error)
}
}
```
## Complete Example
See the [examples/golibrary](https://github.com/docker/docker-agent/tree/main/examples/golibrary) directory for complete working examples:
- `simple/` — Basic agent with no tools
- `tool/` — Custom tool implementation
- `stream/` — Streaming event handling
- `multi/` — Multi-agent with sub-agents
- `builtintool/` — Using built-in tools

View File

@@ -0,0 +1,271 @@
---
title: "Managing Secrets"
description: "How to securely provide API keys and credentials to docker-agent using environment variables, env files, Docker Compose secrets, macOS Keychain, pass, and 1Password references."
keywords: docker agent, ai agents, guides, managing secrets
weight: 30
---
_How to securely provide API keys and credentials to docker-agent._
## Overview
docker-agent needs API keys to talk to model providers (OpenAI, Anthropic, etc.) and MCP tool servers (GitHub, Slack, etc.). These keys are **never stored in config files**. Instead, docker-agent resolves them at runtime through a chain of secret providers, checked in order (see `pkg/environment/default.go`):
| Priority | Provider | Description |
| --- | --- | --- |
| 1 | [Environment variables](#environment-variables) | `export OPENAI_API_KEY=sk-...` |
| 2 | [Docker Compose secrets](#docker-compose-secrets) | Files in `/run/secrets/` |
| 3 | [Credential helper](#credential-helper) | Custom command declared in `~/.config/cagent/config.yaml` under `credential_helper:` |
| 4 | [Docker Desktop](#docker-desktop) | Secrets stored by the Docker Desktop backend (no setup on a Desktop install) |
| 5 | [`pass` password manager](#pass-password-manager) | `pass insert OPENAI_API_KEY` |
| 6 | [macOS Keychain](#macos-keychain) | `security add-generic-password` |
The first provider that has a value wins. You can mix and match — for example, use environment variables for one key and Keychain for another.
Whatever provider returns the value, if that value looks like a [1Password secret reference](#1password-references) (it starts with `op://`), docker-agent resolves it through the `op` CLI before handing it to a model provider or tool.
When docker-agent runs inside a Docker sandbox (detected via `SANDBOX_VM_ID`), a sandbox token provider is prepended to the chain so that `DOCKER_TOKEN` is read from a continuously-refreshed file instead of a stale environment variable.
## Environment Variables
The simplest approach. Set variables in your shell before running docker-agent:
```bash
export OPENAI_API_KEY=sk-...
export ANTHROPIC_API_KEY=sk-ant-...
docker agent run agent.yaml
```
Common variables:
| Variable | Provider |
| --- | --- |
| `OPENAI_API_KEY` | OpenAI |
| `ANTHROPIC_API_KEY` | Anthropic |
| `GOOGLE_API_KEY` | Google Gemini |
| `MISTRAL_API_KEY` | Mistral |
| `OPENROUTER_API_KEY` | OpenRouter |
| `XAI_API_KEY` | xAI |
| `NEBIUS_API_KEY` | Nebius |
MCP tools may require additional variables. For example, the GitHub MCP server needs `GITHUB_PERSONAL_ACCESS_TOKEN`. These are passed to tools via the `env` field in your config:
```yaml
toolsets:
- type: mcp
ref: docker:github-official
env:
GITHUB_PERSONAL_ACCESS_TOKEN: $GITHUB_PERSONAL_ACCESS_TOKEN
```
## Env Files
For convenience, you can store secrets in a `.env` file and pass it to docker-agent with `--env-from-file`:
```bash
# .env
OPENAI_API_KEY=sk-...
ANTHROPIC_API_KEY=sk-ant-...
GITHUB_PERSONAL_ACCESS_TOKEN=ghp_...
```
```bash
docker agent run agent.yaml --env-from-file .env
```
The file format supports:
- `KEY=VALUE` pairs, one per line
- Comments starting with `#`
- Quoted values: `KEY="value with spaces"`
- Blank lines are ignored
> [!IMPORTANT]
> Add `.env` to your `.gitignore` to avoid committing secrets to version control.
## Docker Compose Secrets
When running docker-agent in a container with Docker Compose, you can use [Compose secrets](https://docs.docker.com/compose/how-tos/use-secrets/) to inject credentials securely. Compose mounts secrets as files under `/run/secrets/`, and docker-agent reads from this location automatically.
### From a file
Store each secret in its own file, then reference it in `compose.yaml`:
```bash
echo -n "sk-ant-your-key-here" > .anthropic_api_key
```
```yaml
# compose.yaml
services:
agent:
image: docker/docker-agent
command: run --exec /app/agent.yaml "Hello!"
secrets:
- ANTHROPIC_API_KEY
volumes:
- ./agent.yaml:/app/agent.yaml:ro
secrets:
ANTHROPIC_API_KEY:
file: ./.anthropic_api_key
```
Docker Compose mounts the file as `/run/secrets/ANTHROPIC_API_KEY`. docker-agent picks it up with no extra configuration.
### From a host environment variable
In CI/CD pipelines, secrets are often injected as environment variables. Compose can forward these to `/run/secrets/`:
```yaml
secrets:
ANTHROPIC_API_KEY:
environment: "ANTHROPIC_API_KEY"
```
### Multiple secrets
```yaml
services:
agent:
image: docker/docker-agent
command: run --exec /app/agent.yaml "Summarize my GitHub issues"
secrets:
- ANTHROPIC_API_KEY
- GITHUB_PERSONAL_ACCESS_TOKEN
volumes:
- ./agent.yaml:/app/agent.yaml:ro
secrets:
ANTHROPIC_API_KEY:
file: ./.anthropic_api_key
GITHUB_PERSONAL_ACCESS_TOKEN:
file: ./.github_token
```
### Why use Compose secrets over environment variables?
| Aspect | Environment Variables | Compose Secrets |
| --- | --- | --- |
| Storage | In memory, visible via `docker inspect` | Mounted as tmpfs files under `/run/secrets/` |
| Visibility | Shown in process list and inspect output | Not exposed in `docker inspect` |
| Best for | Development | Production and CI/CD |
## Credential Helper
docker-agent can shell out to an external credential helper you define in your user config. This is useful when your organisation already has a secrets daemon you want to reuse (HashiCorp Vault, 1Password CLI, `bitwarden-cli`, etc.).
Declare the helper in `~/.config/cagent/config.yaml`:
```yaml
# ~/.config/cagent/config.yaml
credential_helper:
command: op
args: ["read", "op://Personal/docker-agent"]
```
The command is invoked with the variable name appended as the final argument, and must print the secret value to stdout.
## Docker Desktop
On machines where Docker Desktop is installed, docker-agent queries Docker Desktop's backend for secrets stored against your signed-in Docker account. This is transparent — no extra configuration — and it is how signed-in Docker users get provider API keys without setting any environment variables.
## `pass` Password Manager
docker-agent integrates with [`pass`](https://www.passwordstore.org/), the standard Unix password manager. Secrets are stored as GPG-encrypted files in `~/.password-store/`.
### Store a secret
```bash
pass insert ANTHROPIC_API_KEY
```
The entry name must match the environment variable name that docker-agent expects.
### Verify it works
```bash
pass show ANTHROPIC_API_KEY
```
Once `pass` is set up, docker-agent resolves secrets from it automatically.
## macOS Keychain
On macOS, docker-agent can read secrets from the system Keychain. This is useful for local development — you store the key once and it's available across all your projects.
### Store a secret
```bash
security add-generic-password -a "$USER" -s ANTHROPIC_API_KEY -w "sk-ant-your-key-here"
```
The `-s` (service name) must match the environment variable name that docker-agent expects.
### Verify it works
```bash
security find-generic-password -s ANTHROPIC_API_KEY -w
```
### Delete a secret
```bash
security delete-generic-password -s ANTHROPIC_API_KEY
```
Once stored, docker-agent finds the secret automatically — no flags or config needed.
## 1Password References
Any secret value resolved through the chain above can be a **1Password secret reference** instead of the literal secret. If the value starts with `op://`, docker-agent resolves it by invoking the [1Password CLI](https://developer.1password.com/docs/cli/) (`op read <reference>`) and uses the result.
This works with every provider — most commonly an environment variable or env file:
```bash
export OPENAI_API_KEY="op://Personal/OpenAI/api-key"
docker agent run agent.yaml
```
References follow the `op://<vault>/<item>/<field>` format. Make sure the `op` CLI is installed and you are signed in (`op signin`) so that non-interactive reads succeed.
> [!WARNING]
> **Behaviour when resolution fails**
>
> If the value starts with `op://` but the `op` CLI is not installed, or the reference cannot be read (not signed in, wrong path, locked vault), docker-agent logs a warning and uses an **empty value** — it never forwards the raw `op://` reference to a model provider or tool. Resolved references (and deterministic failures) are cached for the lifetime of the run; transient failures such as a cancelled lookup are not cached, so a later attempt can retry.
## Choosing a Method
| Method | Best for | Setup effort |
| --- | --- | --- |
| Environment variables | Quick local development, scripts | Low |
| Env files | Team projects, multiple keys | Low |
| Docker Compose secrets | Containerized deployments, CI/CD | Medium |
| `pass` | Linux/macOS, GPG-based workflows | Medium |
| macOS Keychain | macOS local development | Low |
| 1Password references (`op://`) | Teams already using 1Password | Low |
You can combine methods. For example, store long-lived provider keys in macOS Keychain and pass project-specific MCP tokens via env files.
## Preventing Secret Leaks
Provider keys live in the secret store and are passed to docker-agent through the chain above — the agent itself never receives them as input. But the **content of a conversation** can still leak credentials: a user pasting a token, a tool returning a config file with embedded keys, a transcript dumped into a prompt.
For that defense-in-depth case, set `redact_secrets: true` on an agent. It scrubs detected secrets out of:
- the arguments of every outgoing tool call (before the tool sees them),
- every outgoing chat message (before the model provider sees them), and
- every tool's output (before it reaches event consumers, the persisted session file, the `post_tool_use` hook input, or the next LLM call).
```yaml
agents:
root:
model: openai/gpt-5
description: A helpful assistant
instruction: You are a helpful assistant.
redact_secrets: true
toolsets:
- type: shell
```
The ruleset covers GitHub PATs, AWS / GCP / Azure credentials, Stripe / Slack / GitLab / Hugging Face tokens, JWTs, PEM-encoded private keys, Docker Hub PATs, and many others. Each detected span is replaced with the literal `[REDACTED]`. See the [Redacting Secrets](../../configuration/agents/index.md#redacting-secrets) section in the agent configuration reference for the full picture and important caveats about false negatives.

View File

@@ -0,0 +1,360 @@
---
title: "Thinking / Reasoning"
description: "Control how much a model reasons before responding. Works across OpenAI, Anthropic, Google Gemini, AWS Bedrock, and Docker Model Runner."
keywords: docker agent, ai agents, guides, thinking / reasoning
weight: 20
---
_Control how much a model reasons before responding. Works across OpenAI, Anthropic, Google Gemini, AWS Bedrock, and Docker Model Runner._
## What Is Thinking?
Several modern models support an extended reasoning phase that happens before they produce visible output. During this phase the model plans, evaluates options, and works through the problem — internally, not shown in the response by default. This typically improves accuracy on complex tasks like coding, math, and multi-step planning, at the cost of higher token usage and latency.
docker-agent exposes this through a single `thinking_budget` field on any named model. The value format differs slightly by provider, but the semantics are the same: higher effort means more thorough reasoning.
> [!NOTE]
> **Think tool vs. thinking budget**
>
> The [think tool](../../tools/think/index.md) is a scratchpad for models that lack native reasoning. If your model supports `thinking_budget`, you do not need the think tool.
## Quick Reference
| Provider | Format | Values | Default |
| ------------------- | ---------- | --------------------------------------------------------------------------------------- | ------------------ |
| OpenAI | string | `minimal`, `low`, `medium`, `high`, `none`; `xhigh` on gpt-5.2+ only | `medium` (API default) |
| Anthropic | int or str | 1024–32768 tokens, or `minimal`–`max`, `adaptive`, `adaptive/<effort>`, `none` | off |
| Gemini 2.5 | int | `0` (off), `-1` (dynamic), or token count (max 24576 / 32768) | `-1` (dynamic) |
| Gemini 3 | string | `minimal`, `low`, `medium`, `high` | API default (model-dependent) |
| AWS Bedrock | int or str | 1024–32768 tokens (`minimal`–`max` mapped to tokens); `adaptive`, `adaptive/<effort>` for Opus 4.6+ (rejected by older Claude models) | off |
| Docker Model Runner | int or str | token count, `minimal`–`max` (mapped to tokens), `adaptive` (unlimited), `none` | engine default |
String values are case-insensitive. The full set of accepted strings is `none`, `minimal`, `low`, `medium`, `high`, `xhigh`, `max`, `adaptive`, and `adaptive/<effort>` — but each provider only honors the subset listed above. Unsupported values either fail at request time (OpenAI) or are mapped/ignored as described per provider below.
> `thinking_budget` is only applied by the providers listed above. Other OpenAI-compatible providers (xAI, Mistral, Ollama, …) currently ignore it — see [xAI and Mistral](#xai-grok-and-mistral).
## OpenAI
OpenAI reasoning models (o-series, gpt-5, gpt-5-mini) use a string effort level that maps to their `reasoning_effort` API parameter. The `xhigh` level is only accepted by gpt-5.2 and later minor versions; o-series and earlier gpt-5 releases top out at `high`.
```yaml
models:
gpt-thinker:
provider: openai
model: gpt-5-mini
thinking_budget: high # minimal | low | medium | high | xhigh
```
**Effort levels:**
| Level | Description |
| --------- | -------------------------------------------------------- |
| `none` | Don't request extra reasoning (alias for `0`); the API's own default still applies. |
| `minimal` | Fastest; lightest reasoning pass. |
| `low` | Quick reasoning for straightforward tasks. |
| `medium` | Balanced default. |
| `high` | More thorough; recommended for complex tasks. |
| `xhigh` | Near-maximum effort; slower but most accurate. |
These effort levels (`minimal`–`xhigh`) are the **only** values accepted for OpenAI. Token counts, `max`, `adaptive`, and `adaptive/<effort>` are rejected with a configuration error at request time. The `xhigh` level is only supported by gpt-5.2 and later minor versions (e.g. gpt-5.2, gpt-5.4-mini); o-series and earlier gpt-5 releases top out at `high`. Older models (o1, o3-mini) only accept `low`/`medium`/`high` — sending an unsupported level returns an API error.
> [!WARNING]
> **Tokens and max_tokens**
>
> OpenAI reasoning models always reason internally — even with `thinking_budget: none` there are hidden reasoning tokens that count against `max_tokens`. docker-agent automatically raises the output-token floor for its internal low-effort calls (e.g. title generation) so hidden reasoning cannot starve visible text output.
## Anthropic
Anthropic Claude supports two thinking modes: a **token budget** (older models) and **adaptive / effort-based** thinking (newer models).
### Token budget (Claude 4 and earlier)
Set an explicit number of thinking tokens (1024–32768). This must be less than `max_tokens`:
```yaml
models:
claude-thinker:
provider: anthropic
model: claude-sonnet-4-5
thinking_budget: 16384 # tokens reserved for internal reasoning
```
docker-agent auto-adjusts `max_tokens` when you set a thinking budget but leave `max_tokens` at its default. If you set `max_tokens` explicitly, it must be greater than `thinking_budget`.
### Adaptive thinking (Opus 4.6+ and Sonnet 4.6)
Newer Claude models support adaptive thinking, where the model decides how much to think. **Claude Opus 4.6, 4.7, 4.8, and Sonnet 4.6 only support adaptive thinking** — they reject token-based budgets. Use `adaptive`, `adaptive/<effort>`, or a bare effort level — on Anthropic, a bare effort level like `high` is shorthand for adaptive thinking at that effort:
```yaml
models:
claude-adaptive:
provider: anthropic
model: claude-opus-4-6
thinking_budget: adaptive # model decides effort (defaults to high)
claude-adaptive-low:
provider: anthropic
model: claude-opus-4-6
thinking_budget: low # same as adaptive/low
claude-adaptive-max:
provider: anthropic
model: claude-opus-4-6
thinking_budget: adaptive/max # adaptive/low | adaptive/medium | adaptive/high | adaptive/xhigh | adaptive/max
```
**Adaptive effort levels and per-model support:**
| Level | Opus 4.5 | Sonnet 4.5 / Haiku | Sonnet 4.6 | Opus 4.6 | Opus 4.7 / 4.8 | Fable 5 | Mythos 5 | Mythos preview |
| --------- | :------: | :----------------: | :--------: | :------: | :------------: | :-----: | :------: | :------------: |
| `low` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| `medium` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| `high` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| `xhigh` | — | — | — | — | ✓ | ✓ | ✓ | — |
| `max` | — | — | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
`minimal` is treated as `low` (bare form only). `high` is the default when `adaptive` is used without an effort level.
> [!WARNING]
> **Effort strings require adaptive-capable models**
>
> Every string effort value on Anthropic is sent as adaptive thinking (`output_config.effort`), which only newer Claude models (Opus 4.6+, Sonnet 4.6) accept. For older models like Sonnet 4.5, use an integer token budget instead. Conversely, models that _only_ support adaptive thinking (Opus 4.6, 4.7, 4.8, Sonnet 4.6) automatically have token budgets coerced to `adaptive` (a warning is logged).
### Disabling thinking
```yaml
thinking_budget: none # or 0
```
### Interleaved thinking
Interleaved thinking lets the model reason between tool calls — useful for complex agentic tasks. docker-agent auto-enables it whenever a thinking budget is configured on a Claude model, so you only need to set it explicitly to turn it off:
```yaml
models:
claude-interleaved:
provider: anthropic
model: claude-sonnet-4-5
thinking_budget: 16384
# interleaved_thinking is auto-enabled; disable it explicitly if needed:
provider_opts:
interleaved_thinking: false
```
> [!NOTE]
> **Temperature and top_p**
>
> When extended thinking is enabled, Anthropic requires `temperature=1.0`. docker-agent automatically suppresses any `temperature` or `top_p` settings you have configured — they are silently ignored while thinking is active.
### Thinking display
Newer Claude models (Opus 4.7+, Fable 5) hide thinking content by default at the API level. To keep reasoning visible, docker-agent requests `summarized` thinking whenever adaptive/effort-based thinking is used without an explicit `thinking_display`. Use `thinking_display` in `provider_opts` to override:
```yaml
models:
opus-47:
provider: anthropic
model: claude-opus-4-7
thinking_budget: adaptive
provider_opts:
thinking_display: omitted # summarized | display | omitted
```
| Value | Behavior |
| ------------ | ------------------------------------------------------------------------------------- |
| `summarized` | Thinking blocks returned with a text summary (docker-agent default for adaptive thinking). |
| `display` | Full thinking blocks returned for display. |
| `omitted` | Thinking blocks hidden — only the signature is returned. |
Full thinking tokens are billed regardless of `thinking_display`.
### Task budget (Anthropic)
`task_budget` caps total tokens across an entire multi-step agentic task (thinking + tool calls + output combined):
```yaml
models:
opus-bounded:
provider: anthropic
model: claude-opus-4-7
thinking_budget: adaptive
task_budget: 128000 # total token ceiling for the whole task
```
See the [Anthropic provider page](../../providers/anthropic/index.md#task-budget) for details.
## Google Gemini
Gemini 2.5 and Gemini 3 use different formats.
### Gemini 2.5 (token budget)
```yaml
models:
gemini-off:
provider: google
model: gemini-2.5-flash
thinking_budget: 0 # disable thinking
gemini-dynamic:
provider: google
model: gemini-2.5-flash
thinking_budget: -1 # let the model decide (default)
gemini-fixed:
provider: google
model: gemini-2.5-flash
thinking_budget: 8192 # fixed token budget (max 24576 for Flash, 32768 for Pro)
```
### Gemini 3 (level-based)
```yaml
models:
gemini3-flash:
provider: google
model: gemini-3-flash
thinking_budget: medium # minimal | low | medium | high
gemini3-pro:
provider: google
model: gemini-3-pro
thinking_budget: high # low | high (Pro supports fewer levels)
```
## AWS Bedrock (Claude)
Bedrock Claude uses a token budget like Anthropic. String effort levels (`minimal`–`max`) are mapped automatically:
| Effort level | Token budget |
| ------------ | ------------ |
| `minimal` | 1,024 |
| `low` | 2,048 |
| `medium` | 8,192 |
| `high` | 16,384 |
| `xhigh`/`max`| 32,768 |
```yaml
models:
bedrock-claude-thinker:
provider: amazon-bedrock
model: global.anthropic.claude-sonnet-4-5-20250929-v1:0
thinking_budget: 8192 # or use an effort level: medium
provider_opts:
region: us-east-1
bedrock-claude-interleaved:
provider: amazon-bedrock
model: global.anthropic.claude-sonnet-4-5-20250929-v1:0
thinking_budget: high
provider_opts:
region: us-east-1
# interleaved_thinking is auto-enabled when thinking_budget is set
```
**Claude Opus 4.6+ on Bedrock requires adaptive thinking** — these models reject `thinking.type=enabled` (token budgets). Configure them with `adaptive` or `adaptive/<effort>`; docker-agent auto-coerces token budgets and effort levels on these models with a warning:
```yaml
models:
bedrock-opus-adaptive:
provider: amazon-bedrock
model: global.anthropic.claude-opus-4-8
thinking_budget: adaptive/high
provider_opts:
region: us-east-1
```
> [!WARNING]
> **Bedrock thinking requirements**
>
> Bedrock Claude requires token-based `thinking_budget` values to be ≥ 1024 and less than `max_tokens`. docker-agent logs a warning and ignores the budget if either condition is violated. Interleaved thinking requires the `interleaved-thinking-2025-05-14` beta header, which docker-agent adds automatically; it is auto-enabled whenever a token thinking budget is set on a Bedrock-hosted Claude model (adaptive thinking interleaves on its own).
## Docker Model Runner (local models)
For local models, `thinking_budget` is forwarded to the inference engine. Both token counts and effort strings work; effort strings map to the same token scale as Bedrock (`minimal`=1024 … `xhigh`/`max`=32768), and `adaptive` means unlimited:
```yaml
models:
local:
provider: dmr
model: ai/qwen3
thinking_budget: medium # llama.cpp: reasoning-budget=8192; vLLM: thinking_token_budget=8192
```
- **llama.cpp**: sent as `reasoning-budget` at model-configure time.
- **vLLM**: sent as `thinking_token_budget` on each request.
- **MLX / SGLang**: no reasoning-budget knob; the value is silently ignored.
See the [Docker Model Runner provider page](../../providers/dmr/index.md) for details.
## xAI (Grok) and Mistral
xAI and Mistral run through docker-agent's OpenAI-compatible client, but the `reasoning_effort` parameter is only sent for OpenAI reasoning model names (o-series, gpt-5). **Setting `thinking_budget` on Grok or Mistral models currently has no effect** — the value is accepted by config validation but never sent to the API.
Grok and Mistral reasoning models (e.g. `grok-3-mini`, `magistral`) manage reasoning on their own; for non-reasoning models, consider the [think tool](../../tools/think/index.md) instead.
## Disabling Thinking
Use `none` or `0` to disable thinking on any provider:
```yaml
models:
fast-model:
provider: openai
model: gpt-5-mini
thinking_budget: none
gemini-no-think:
provider: google
model: gemini-2.5-flash
thinking_budget: 0
```
`none` and `0` clear docker-agent's thinking configuration — no thinking parameter is sent. Models that always reason (OpenAI o-series, gpt-5, Gemini 3) then fall back to the API's default behavior and still reason internally; only models with optional thinking (Gemini 2.5, Claude, local models) are fully disabled.
## Choosing an Effort Level
| Task complexity | Recommended level |
| -------------------------------- | ----------------------- |
| Simple factual Q&A | `none` / `minimal` |
| General-purpose chat | `low` / `medium` |
| Coding, debugging, analysis | `medium` / `high` |
| Complex reasoning, planning | `high` / `xhigh` |
| Research, difficult math/logic | `xhigh` / `max` |
| Long agentic tasks (Anthropic) | `adaptive` |
## Changing Thinking Level at Runtime
While running in the TUI, press **Shift+Tab** to cycle the thinking effort level for the current model without editing your YAML config, or type `/effort <level>` to jump straight to a specific level (e.g. `/effort high`):
- The level steps through the model's supported range (model-specific), wrapping around — for example `none → minimal → low → medium → high → none` on OpenAI gpt-5/o-series, `none → minimal → low → medium → high → xhigh → none` on gpt-5.2+, `none → low → medium → high → max → none` on Anthropic Opus 4.6 and Sonnet 4.6, and `none → low → medium → high → xhigh → max → none` on Anthropic Opus 4.7+, Fable 5, and Mythos 5. For older Anthropic models (e.g. Sonnet 4.5) that only accept token budgets, effort-string cycling has no effect — use an integer `thinking_budget` in your YAML config instead.
- The current level is shown in the sidebar next to the model name (e.g. `openai/gpt-5 • high`).
- This applies as a session override — it is **not** saved to the config file. The next session starts from the level defined in your YAML.
- For models that don't support reasoning, and for remote runtimes, Shift+Tab is a no-op and an informational message is displayed.
- `/effort` only accepts levels the current model supports; requesting an unsupported level shows the model's supported list. Like Shift+Tab, it is unavailable for non-reasoning models and remote runtimes.
## Sharing Thinking Config Across Models
Define a provider with a default `thinking_budget` and all models that reference it inherit it:
```yaml
providers:
deep-anthropic:
provider: anthropic
thinking_budget: adaptive/high
max_tokens: 32768
models:
claude-smart:
provider: deep-anthropic
model: claude-opus-4-6 # inherits thinking_budget: adaptive/high
claude-faster:
provider: deep-anthropic
model: claude-opus-4-6
thinking_budget: low # overrides to adaptive/low
```
## Full Example
See [`examples/thinking_budget.yaml`](https://github.com/docker/docker-agent/blob/main/examples/thinking_budget.yaml) for a runnable config covering all providers.

View File

@@ -0,0 +1,423 @@
---
title: "Tips & Best Practices"
description: "Expert guidance for building effective, efficient, and secure agents."
keywords: docker agent, ai agents, guides, tips & best practices
weight: 10
aliases:
- /ai/docker-agent/best-practices/
---
_Expert guidance for building effective, efficient, and secure agents._
## Configuration Tips
### Auto Mode for Quick Start
Don't have a config file? docker-agent can automatically detect your available API keys and use an appropriate model:
```bash
# Automatically uses the best available provider
$ docker agent run
# Provider priority: Anthropic → OpenAI → Google → Mistral → Amazon Bedrock → DMR
```
The special `auto` model value also works in configs:
```yaml
agents:
root:
model: auto # Uses best available provider
description: Adaptive assistant
instruction: You are a helpful assistant.
```
### Environment Variable Interpolation
Commands support JavaScript template literal syntax for environment variables:
```yaml
agents:
root:
model: openai/gpt-4o
description: Deployment assistant
instruction: You help with deployments.
commands:
# Simple variable
greet: "Hello ${env.USER}!"
# With default value
deploy: "Deploy to ${env.ENV || 'staging'}"
# Multiple variables
release: "Release ${env.PROJECT} v${env.VERSION || '1.0.0'}"
```
### Model Aliases Are Auto-Pinned
docker-agent automatically resolves model aliases to their latest pinned versions. This ensures reproducible behavior:
```yaml
# You write:
model: anthropic/claude-sonnet-4-5
# docker-agent resolves to:
# anthropic/claude-sonnet-4-5-20250929 (or latest available)
```
To use a specific version, specify it explicitly in your config.
## Performance Tips
### Defer Tools for Faster Startup
Large MCP toolsets can slow down agent startup. Use `defer` to load tools on-demand:
```yaml
agents:
root:
model: openai/gpt-4o
description: Multi-tool assistant
instruction: You have many tools available.
toolsets:
- type: mcp
ref: docker:github-official
defer: true
- type: mcp
ref: docker:slack
defer: true
- type: mcp
ref: docker:linear
defer: true
```
Or defer specific tools within a toolset:
```yaml
toolsets:
- type: mcp
ref: docker:github-official
defer:
- "list_issues"
- "search_repos"
- type: mcp
ref: docker:slack
defer:
- "list_channels"
```
### Filter MCP Tools
Many MCP servers expose dozens of tools. Filter to only what you need:
```yaml
toolsets:
- type: mcp
ref: docker:github-official
# Only expose these specific tools
tools:
- list_issues
- create_issue
- get_pull_request
- create_pull_request
```
Fewer tools means faster tool selection and less confusion for the model.
### Set max_iterations
Always set `max_iterations` for agents with powerful tools to prevent infinite loops:
```yaml
agents:
developer:
model: anthropic/claude-sonnet-4-5
description: Development assistant
instruction: You are a developer.
max_iterations: 30 # Reasonable limit for development tasks
toolsets:
- type: filesystem
- type: shell
```
Typical values: 20-30 for development agents, 10-15 for simple tasks.
## Reliability Tips
### Use Fallback Models
Configure fallback models for resilience against provider outages or rate limits:
```yaml
agents:
root:
model: anthropic/claude-sonnet-4-5
description: Reliable assistant
instruction: You are a helpful assistant.
fallback:
models:
# Different provider for resilience
- openai/gpt-4o
# Cheaper model as last resort
- openai/gpt-4o-mini
retries: 2 # Retry 5xx errors twice
cooldown: 1m # Stick with fallback for 1 min after rate limit
```
**Best practices for fallback chains:**
- Use different providers for true redundancy
- Order by preference (best first)
- Include a cheaper/faster model as last resort
### Use Think Tool for Non-Reasoning Models
The `think` tool provides a reasoning scratchpad for models that lack built-in thinking capabilities:
```yaml
toolsets:
- type: think # Useful for models without native reasoning
```
The agent uses it as a scratchpad for planning and decision-making. If your model already supports a [thinking budget](../../configuration/models/index.md#thinking-budget) (e.g., Claude with extended thinking, OpenAI o-series, Gemini with thinking enabled), you don't need this tool — the model can reason internally.
## Security Tips
### Use --yolo Mode Carefully
The `--yolo` flag auto-approves all tool calls without confirmation:
```bash
# Auto-approve everything (use with caution!)
$ docker agent run agent.yaml --yolo
```
**When it's appropriate:**
- CI/CD pipelines with controlled inputs
- Automated testing
- Agents with only safe, read-only tools
**When to avoid:**
- Interactive sessions with untested prompts
- Agents with shell or filesystem write access
- Any situation where unreviewed actions could cause harm
### Combine Permissions with Sandbox
For defense in depth, use both permissions and [sandbox mode](../../configuration/sandbox/index.md):
```yaml
agents:
secure_dev:
model: anthropic/claude-sonnet-4-5
description: Secure development assistant
instruction: You are a secure coding assistant.
toolsets:
- type: filesystem
- type: shell
permissions:
allow:
- "read_*"
- "shell:cmd=go*"
- "shell:cmd=npm*"
deny:
- "shell:cmd=sudo*"
- "shell:cmd=rm*-rf*"
```
```bash
# Run with sandbox enabled
docker-agent run --sandbox agent.yaml
```
### Set Global Permission Guardrails
Use [global permissions](../../configuration/permissions/index.md#global-permissions) in your user config to enforce safety rules across every agent:
```yaml
# ~/.config/cagent/config.yaml
settings:
permissions:
deny:
- "shell:cmd=sudo*"
- "shell:cmd=rm*-rf*"
- "shell:cmd=git push --force*"
allow:
- "read_*"
- "shell:cmd=ls*"
- "shell:cmd=cat*"
```
These rules merge with any agent-level permissions. Deny patterns from your global config cannot be overridden by agent configs, so you can trust that dangerous commands stay blocked regardless of which agent you run.
### Use Hooks for Audit Logging
Log all tool calls for compliance or debugging:
```yaml
agents:
audited:
model: openai/gpt-4o
description: Audited assistant
instruction: You are a helpful assistant.
hooks:
post_tool_use:
- matcher: "*"
hooks:
- type: command
command: "./scripts/audit-log.sh"
```
## Multi-Agent Tips
### Handoffs vs Sub-Agents
Understand the difference between `sub_agents` and `handoffs`:
- **`sub_agents` (transfer_task)** — delegates a task to a child in a sub-session, waits for the result, then continues. Hierarchical: the parent remains in control.
```yaml
sub_agents: [researcher, writer]
```
- **`handoffs` (peer-to-peer)** — hands off the entire conversation to another agent in the same session. The active agent switches and sees the full history. Agents can form cycles.
```yaml
handoffs:
- specialist
- summarizer
```
See [Multi-Agent Systems](../../concepts/multi-agent/index.md) for a detailed comparison.
### Give Sub-Agents Clear Descriptions
The root agent uses descriptions to decide which sub-agent to delegate to:
```yaml
agents:
root:
model: anthropic/claude-sonnet-4-5
description: Technical lead
instruction: Delegate to specialists based on the task.
sub_agents: [frontend, backend, devops]
frontend:
model: openai/gpt-4o
# Good: specific and actionable
description: |
Frontend specialist. Handles React, TypeScript, CSS,
UI components, and browser-related issues.
backend:
model: openai/gpt-4o
# Good: clear domain boundaries
description: |
Backend specialist. Handles APIs, databases,
server logic, and Go/Python code.
devops:
model: openai/gpt-4o
description: |
DevOps specialist. Handles CI/CD, Docker, Kubernetes,
infrastructure, and deployment pipelines.
```
## Debugging Tips
### Enable Debug Logging
Use the `--debug` flag to see detailed execution logs:
```bash
# Default log location: ~/.cagent/cagent.debug.log
$ docker agent run agent.yaml --debug
# Custom log location
$ docker agent run agent.yaml --debug --log-file ./debug.log
```
### Check Token Usage
Use the `/cost` command during a session to see token consumption:
```text
/cost
Token Usage:
Input: 12,456 tokens
Output: 3,789 tokens
Total: 16,245 tokens
```
### Compact Long Sessions
If a session gets too long, use `/compact` to summarize and reduce context:
```text
/compact
Session compacted. Summary generated and history trimmed.
```
## More Tips
### User-Defined Default Model
Set your preferred default model in `~/.config/cagent/config.yaml`:
```yaml
settings:
default_model: anthropic/claude-sonnet-4-5
```
This model is used when you run `docker agent run` without a config file.
### GitHub PR Reviewer Example
Use docker-agent as a GitHub Actions PR reviewer:
```yaml
# .github/workflows/pr-review.yml
name: PR Review
on:
pull_request:
types: [opened, synchronize]
jobs:
review:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run docker-agent review
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
# Install docker-agent
curl -fsSL https://get.docker-agent.dev | sh
# Run the review
docker agent run --exec reviewer.yaml --yolo \
"Review PR #${{ github.event.pull_request.number }}"
```
With a simple reviewer agent:
```yaml
# reviewer.yaml
agents:
root:
model: anthropic/claude-sonnet-4-5
description: PR reviewer
instruction: |
Review pull requests for code quality, bugs, and security issues.
Be constructive and specific in your feedback.
toolsets:
- type: mcp
ref: docker:github-official
- type: think
```

View File

@@ -0,0 +1,5 @@
---
title: "Model Providers"
description: "Model providers supported by Docker Agent."
weight: 60
---

View File

@@ -0,0 +1,239 @@
---
title: "Anthropic"
description: "Use Claude Sonnet 4, Claude Sonnet 4.5, and other Anthropic models with docker-agent."
keywords: docker agent, ai agents, model providers, llm, anthropic
weight: 20
---
_Use Claude Sonnet 4, Claude Sonnet 4.5, and other Anthropic models with docker-agent._
## Setup
```bash
# Set your API key
export ANTHROPIC_API_KEY="sk-ant-..."
```
### Workload Identity Federation (no API key)
Authenticate with short-lived tokens minted from your own OIDC identity
provider instead of a long-lived API key. See Anthropic's
[Workload Identity Federation guide](https://platform.claude.com/docs/en/build-with-claude/workload-identity-federation)
to provision a Federation Rule, then configure docker-agent with a typed
`auth:` block:
```yaml
providers:
anthropic-wif:
provider: anthropic
auth:
type: workload_identity_federation
workload_identity_federation:
federation_rule_id: fdrl_REPLACE_ME
organization_id: 00000000-0000-0000-0000-000000000000
# Optional: only required for target_type=SERVICE_ACCOUNT rules.
service_account_id: svac_REPLACE_ME
identity_token:
# Pick exactly one of: file, env, command, url
file: /var/run/secrets/anthropic.com/token
models:
claude:
provider: anthropic-wif
model: claude-sonnet-4-5
```
`identity_token` accepts four mutually exclusive sources:
| Source | When to use |
| --------- | ------------------------------------------------------------------------------------------------------------------------ |
| `file` | Kubernetes projected service-account tokens, SPIFFE/SPIRE helpers, Vault sidecars — anything that rotates a file on disk |
| `env` | The token is already exported in an environment variable |
| `command` | Shell out to a CLI on every refresh (`gcloud auth print-identity-token`, `az account get-access-token`, ...) |
| `url` | Fetch from an HTTP(S) endpoint (cloud metadata servers, GitHub Actions OIDC token URL, ...) |
For `url`, both the URL and any header values support `${env.VAR}` expansion
against the runtime environment (the legacy `${VAR}` form is also accepted),
which lets you wire the GitHub Actions OIDC
token endpoint without putting secrets in YAML:
```yaml
identity_token:
url: ${env.ACTIONS_ID_TOKEN_REQUEST_URL}&audience=https://api.anthropic.com
headers:
Authorization: bearer ${env.ACTIONS_ID_TOKEN_REQUEST_TOKEN}
response_field: value
```
`auth:` is mutually exclusive with `--gateway`. Token-refresh failures are
surfaced through the normal error path with a clear `anthropic workload
identity federation: failed to refresh identity token from <kind> source
(federation_rule=fdrl_…): ...` message in the TUI.
A complete walkthrough of all four sources lives in
[`examples/anthropic_wif.yaml`](https://github.com/docker/docker-agent/blob/main/examples/anthropic_wif.yaml).
## Configuration
### Inline
```yaml
agents:
root:
model: anthropic/claude-sonnet-4-5
```
### Named Model
```yaml
models:
claude:
provider: anthropic
model: claude-sonnet-4-5
max_tokens: 64000
```
## Available Models
| Model ID | Description |
| ------------------- | --------------------------------------------------- |
| `claude-opus-4-7` | Highest-capability Opus model; supports task budget |
| `claude-sonnet-4-5` | Most capable Sonnet; supports extended thinking |
| `claude-sonnet-4-0` | Previous Sonnet generation, still supported |
| `claude-haiku-4-5` | Fast and inexpensive, good for tight loops |
## Thinking Budget
Anthropic accepts either an integer token budget or a string effort value. Thinking is off unless you set `thinking_budget`; when set, interleaved thinking is auto-enabled.
**Token budget** (1024–32768; works on all extended-thinking Claude models):
```yaml
models:
claude-deep:
provider: anthropic
model: claude-sonnet-4-5
thinking_budget: 16384 # must be < max_tokens
```
**Adaptive / effort-based** (Claude Opus 4.6+, Sonnet 4.6 — every string value is sent as adaptive thinking via `output_config.effort`):
```yaml
models:
opus-adaptive:
provider: anthropic
model: claude-opus-4-6
thinking_budget: adaptive # model decides effort (defaults to high)
opus-effort:
provider: anthropic
model: claude-opus-4-6
thinking_budget: high # low | medium | high | xhigh | max (same as adaptive/<effort>)
```
On models that reject token-based thinking (Opus 4.6, 4.7, 4.8, Sonnet 4.6), an integer budget is automatically coerced to `adaptive` with a logged warning. See the [Thinking / Reasoning guide](../../guides/thinking/index.md) for the full cross-provider reference.
## Interleaved Thinking
Auto-enabled whenever a thinking budget is configured on a Claude model. Allows tool calls during model reasoning for more integrated problem-solving:
```yaml
models:
claude:
provider: anthropic
model: claude-sonnet-4-5
provider_opts:
interleaved_thinking: false # disable if needed
```
## Task Budget
`task_budget` caps the **total** number of tokens the model may spend across a
multi-step agentic task — combined thinking, tool calls, and final output. It
is forwarded as
[`output_config.task_budget`](https://platform.claude.com/docs/en/about-claude/models/whats-new-claude-4-7)
and is ideal for letting long-running agents self-regulate effort without
tightening `max_tokens` on every call.
docker-agent automatically attaches the required `task-budgets-2026-03-13`
beta header whenever this field is set. You can configure `task_budget` on
**any** Claude model — docker-agent never gates it by model name. At the time
of writing, only **Claude Opus 4.7** actually honors the field; other Claude
models (Sonnet 4.5, Opus 4.5 / 4.6, etc.) are expected to reject requests
that include it. Check the Anthropic release notes linked above for the
current list of supported models.
```yaml
models:
opus:
provider: anthropic
model: claude-opus-4-7
task_budget: 128000 # integer shorthand → { type: tokens, total: 128000 }
thinking_budget: adaptive
```
Object form (forward-compatible with future budget types):
```yaml
opus:
provider: anthropic
model: claude-opus-4-7
task_budget:
type: tokens
total: 128000
```
See the full schema on the [Model Configuration](../../configuration/models/index.md#task-budget) page.
## Server-Side Fallbacks
When the primary model refuses a request (e.g. Claude Fable 5's safety
classifiers ending the turn with stop reason `refusal`), Anthropic can retry
the request with backup models in a single round trip. Set `fallbacks` in
`provider_opts` to a list of model IDs, in priority order:
```yaml
models:
fable:
provider: anthropic
model: claude-fable-5
provider_opts:
fallbacks:
- claude-opus-4-8
- claude-sonnet-4-6
```
docker-agent automatically attaches the required
`server-side-fallback-2026-06-01` beta header and forwards the option as
`fallbacks: [{"model": "..."}]`. The response's `model` field reports which
model actually served the request.
Fallback models receive the exact same request as the primary model
(thinking configuration, task budget, beta features, ...), so list only
models that accept the same request shape. Not available on Bedrock, Vertex
AI, or the Message Batches API.
## Thinking Display
Controls whether thinking blocks are returned in responses when thinking is enabled. Newer Claude models (Opus 4.7+, Fable 5) hide thinking content by default (`omitted`); docker-agent counters this by requesting `summarized` thinking whenever an adaptive/effort-based budget is used without an explicit `thinking_display`, so reasoning stays visible in the UI. Set `thinking_display` in `provider_opts` to override:
```yaml
models:
claude-opus-4-7:
provider: anthropic
model: claude-opus-4-7
thinking_budget: adaptive
provider_opts:
thinking_display: omitted # "summarized", "display", or "omitted"
```
Valid values:
- `summarized`: thinking blocks are returned with summarized thinking text (docker-agent's default for adaptive/effort-based budgets).
- `display`: thinking blocks are returned for display.
- `omitted`: thinking blocks are returned with an empty thinking field; the signature is still returned for multi-turn continuity. Useful to reduce time-to-first-text-token when streaming.
Note: `thinking_display` applies to both `thinking_budget` with token counts and adaptive/effort-based budgets. For token-count budgets no default is applied (the API already defaults to `summarized`). Full thinking tokens are billed regardless of the `thinking_display` value.
> [!NOTE]
> Anthropic thinking budget values below 1024 or greater than or equal to `max_tokens` are ignored (a warning is logged).

View File

@@ -0,0 +1,90 @@
---
title: "Baseten"
description: "Use Baseten AI models with docker-agent."
keywords: docker agent, ai agents, model providers, llm, baseten
weight: 40
---
_Use Baseten AI models with docker-agent._
## Overview
Baseten provides AI models through an OpenAI-compatible API. docker-agent includes built-in support for Baseten as an alias provider.
## Setup
1. Get an API key from [Baseten](https://www.baseten.co/)
2. Set the environment variable:
```bash
export BASETEN_API_KEY=your-api-key
```
## Usage
### Inline Syntax
The simplest way to use Baseten:
```yaml
agents:
root:
model: baseten/deepseek-ai/DeepSeek-V3.1
description: Assistant using Baseten
instruction: You are a helpful assistant.
```
### Named Model
For more control over parameters:
```yaml
models:
baseten_model:
provider: baseten
model: deepseek-ai/DeepSeek-V3.1
temperature: 0.7
max_tokens: 8192
agents:
root:
model: baseten_model
description: Assistant using Baseten
instruction: You are a helpful assistant.
```
## Available Models
Baseten hosts various open models through its Model APIs. Check the [Baseten documentation](https://docs.baseten.co/) for the current model catalog.
| Model | Description |
| ------------------------------ | ------------------------------ |
| `deepseek-ai/DeepSeek-V3.1` | DeepSeek V3.1 model |
| `moonshotai/Kimi-K2.5` | Moonshot Kimi K2.5 model |
| `openai/gpt-oss-120b` | GPT-OSS 120B model |
| `zai-org/GLM-5` | GLM-5 model |
## How It Works
Baseten is implemented as a built-in alias in docker-agent:
- **API Type:** OpenAI-compatible (`openai_chatcompletions`)
- **Base URL:** `https://inference.baseten.co/v1`
- **Token Variable:** `BASETEN_API_KEY`
## Example: Code Assistant
```yaml
agents:
coder:
model: baseten/deepseek-ai/DeepSeek-V3.1
description: Code assistant using DeepSeek
instruction: |
You are an expert programmer using DeepSeek V3.1.
Write clean, well-documented code.
Follow best practices for the language being used.
toolsets:
- type: filesystem
- type: shell
- type: think
```

View File

@@ -0,0 +1,174 @@
---
title: "AWS Bedrock"
description: "Access Claude, Nova, Llama, and more through AWS infrastructure with enterprise-grade security and compliance."
keywords: docker agent, ai agents, model providers, llm, aws bedrock
weight: 30
---
_Access Claude, Nova, Llama, and more through AWS infrastructure with enterprise-grade security and compliance._
## Prerequisites
- AWS account with Bedrock enabled in your region
- Model access granted in the [Bedrock Console](https://console.aws.amazon.com/bedrock/) (some models require approval)
- AWS credentials configured (see authentication below)
## Configuration
```yaml
models:
bedrock-claude:
provider: amazon-bedrock
model: global.anthropic.claude-sonnet-4-5-20250929-v1:0
max_tokens: 64000
provider_opts:
region: us-east-1
```
## Authentication
### Option 1: Bedrock API Key (Simplest)
```bash
export AWS_BEARER_TOKEN_BEDROCK="your-key"
```
```yaml
models:
bedrock:
provider: amazon-bedrock
model: global.anthropic.claude-sonnet-4-5-20250929-v1:0
token_key: AWS_BEARER_TOKEN_BEDROCK # env var name
provider_opts:
region: us-east-1
```
### Option 2: AWS Credentials (Default)
Uses the standard AWS SDK credential chain: env vars → shared credentials → config → IAM roles.
```yaml
models:
bedrock:
provider: amazon-bedrock
model: global.anthropic.claude-sonnet-4-5-20250929-v1:0
provider_opts:
profile: my-aws-profile
region: us-east-1
```
### With IAM Role Assumption
```yaml
models:
bedrock:
provider: amazon-bedrock
model: global.anthropic.claude-sonnet-4-5-20250929-v1:0
provider_opts:
role_arn: "arn:aws:iam::123456789012:role/BedrockAccessRole"
external_id: "my-external-id"
```
## Provider Options
| Option | Type | Default | Description |
| ------------------------ | ------ | ---------------------- | ------------------------------------ |
| `region` | string | us-east-1 | AWS region |
| `profile` | string | — | AWS profile name |
| `role_arn` | string | — | IAM role ARN for assume role |
| `role_session_name` | string | docker-agent-bedrock-session | Session name for assumed role |
| `external_id` | string | — | External ID for role assumption |
| `endpoint_url` | string | — | Custom endpoint (VPC/testing) |
| `interleaved_thinking` | bool | auto | Allow reasoning between tool calls (Claude); auto-enabled when a thinking budget is set on a Claude model; adds the required beta header automatically |
| `disable_prompt_caching` | bool | false | Disable automatic prompt caching |
## Inference Profiles
Use inference profile prefixes for optimal routing:
| Prefix | Routes To |
| --------- | ---------------------------------------- |
| `global.` | All commercial AWS regions (recommended) |
| `us.` | US regions only |
| `eu.` | EU regions only (GDPR compliance) |
| `apac.` | Asia Pacific regions only |
> [!TIP]
> **Inference profiles**
>
> Use `global.` prefix on model IDs for automatic cross-region routing. Use `eu.` prefix for GDPR compliance.
## Thinking Budget (Claude on Bedrock)
Bedrock Claude models support extended thinking — an internal reasoning phase before the model produces its response. Set `thinking_budget` to a token count (1024–32768) or an effort level string that maps automatically:
| Effort level | Token budget |
| ------------ | ------------ |
| `minimal` | 1,024 |
| `low` | 2,048 |
| `medium` | 8,192 |
| `high` | 16,384 |
| `xhigh`/`max`| 32,768 |
```yaml
models:
bedrock-claude-thinking:
provider: amazon-bedrock
model: global.anthropic.claude-sonnet-4-5-20250929-v1:0
thinking_budget: 8192 # tokens, or use an effort string like "medium"
max_tokens: 16384 # must be > thinking_budget
provider_opts:
region: us-east-1
```
`thinking_budget` must be ≥ 1024 and less than `max_tokens`. Values outside this range are logged as a warning and ignored.
### Adaptive thinking (Opus 4.6+)
Newer Claude Opus models (4.6, 4.7, 4.8) **reject token-based thinking** — Bedrock returns a `ValidationException` asking for `thinking.type=adaptive`. For these models, use adaptive thinking:
```yaml
models:
bedrock-opus-adaptive:
provider: amazon-bedrock
model: global.anthropic.claude-opus-4-8
thinking_budget: adaptive/high # adaptive | adaptive/low | adaptive/medium | adaptive/high | adaptive/xhigh | adaptive/max
provider_opts:
region: us-east-1
```
docker-agent recognizes these models (including Bedrock-style IDs) and transparently coerces a configured token budget or effort level to adaptive thinking, logging a warning — so `thinking_budget: 32768` on Opus 4.8 won't fail, but `adaptive` or `adaptive/<effort>` is the recommended configuration. On older models that still use token-based thinking (e.g. Sonnet 4.5), `adaptive` is forwarded as-is and rejected by Bedrock — use a token count or effort level there instead.
> [!NOTE]
> **Temperature and top_p**
>
> Bedrock Claude suppresses `temperature` and `top_p` while extended thinking is active — Anthropic requires `temperature=1.0` internally.
## Interleaved Thinking (Claude on Bedrock)
Interleaved thinking lets the model reason between tool calls, not just at the start. This is useful for complex agentic tasks. Enable it alongside a thinking budget:
```yaml
models:
bedrock-claude-interleaved:
provider: amazon-bedrock
model: global.anthropic.claude-sonnet-4-5-20250929-v1:0
thinking_budget: high
provider_opts:
region: us-east-1
# interleaved_thinking is auto-enabled when thinking_budget is set
```
docker-agent auto-enables `interleaved_thinking` whenever a thinking budget is configured on a Bedrock-hosted Claude model and automatically adds the `interleaved-thinking-2025-05-14` beta header. If you set `interleaved_thinking: false` while a thinking budget is active, a warning is logged because the budget may be ignored by Bedrock without the beta header.
See the [Thinking / Reasoning guide](../../guides/thinking/index.md) for a full cross-provider overview.
## Prompt Caching
Automatically enabled for supported models to reduce latency and costs. System prompts, tool definitions, and recent messages are cached with a 5-minute TTL.
```bash
# Disable if needed
provider_opts:
disable_prompt_caching: true
```

View File

@@ -0,0 +1,102 @@
---
title: "Cerebras"
description: "Use Cerebras models with docker-agent."
keywords: docker agent, ai agents, model providers, llm, cerebras
weight: 50
---
_Use Cerebras models with docker-agent._
## Overview
[Cerebras](https://www.cerebras.ai/) serves open-weight models such as GPT-OSS
and GLM through an OpenAI-compatible API on its wafer-scale hardware, delivering
some of the highest tokens/sec available. That speed makes it a strong fit for
latency-sensitive coding workflows. docker-agent includes built-in support for
Cerebras as an alias provider.
## Setup
1. Create an API key from the [Cerebras Cloud console](https://cloud.cerebras.ai/).
2. Set the environment variable:
```bash
export CEREBRAS_API_KEY=your-api-key
```
## Usage
### Inline Syntax
The simplest way to use Cerebras:
```yaml
agents:
root:
model: cerebras/gpt-oss-120b
description: Assistant using Cerebras
instruction: You are a helpful assistant.
```
### Named Model
For more control over parameters:
```yaml
models:
cerebras_model:
provider: cerebras
model: gpt-oss-120b
temperature: 0.7
max_tokens: 8192
agents:
root:
model: cerebras_model
description: Assistant using Cerebras
instruction: You are a helpful assistant.
```
## Available Models
Cerebras hosts a curated set of open-weight models. Check the
[Cerebras models documentation](https://inference-docs.cerebras.ai/models/overview)
for current model IDs, context limits, and pricing.
| Model | Description |
| --- | --- |
| `gpt-oss-120b` | Open-weight GPT-OSS reasoning model with tool calling |
| `zai-glm-4.7` | Z.AI GLM-4.7 reasoning model with tool calling |
> Model IDs are case-sensitive and must be passed exactly as the catalogue lists
> them. Cerebras may serve additional models not in the built-in catalog; those
> still work but resolve to default capability metadata locally.
## How It Works
Cerebras is implemented as a built-in alias in docker-agent:
- **API Type:** OpenAI-compatible (`openai_chatcompletions`)
- **Base URL:** `https://api.cerebras.ai/v1`
- **Token Variable:** `CEREBRAS_API_KEY`
Because Cerebras fronts open-weight models whose chat templates may only accept
a single leading system message, docker-agent coalesces its per-source system
messages (agent instruction plus each toolset's instructions) into one before
sending the request.
## Example: Code Assistant
```yaml
agents:
coder:
model: cerebras/gpt-oss-120b
description: Fast code assistant using Cerebras
instruction: |
You are an expert programmer.
Write clean, well-documented code and follow language best practices.
toolsets:
- type: filesystem
- type: shell
- type: think
```

View File

@@ -0,0 +1,114 @@
---
title: "Cloudflare AI Gateway"
description: "Use Cloudflare AI Gateway models with docker-agent."
keywords: docker agent, ai agents, model providers, llm, cloudflare ai gateway
weight: 60
---
_Use Cloudflare AI Gateway models with docker-agent._
## Overview
[Cloudflare AI Gateway](https://developers.cloudflare.com/ai-gateway/) is a
single OpenAI-compatible endpoint that routes to models from OpenAI, Anthropic,
Workers AI and more, with caching, rate limiting and observability. docker-agent
includes built-in support for AI Gateway as an alias provider.
The alias sends your token in the standard `Authorization: Bearer` header, so it
works out of the box with a gateway that has **authentication disabled** (the
default), typically to route to your own Workers AI models through a gateway you
own. See [Authentication](#authentication) below for the unified-billing /
authenticated-gateway caveat.
## Setup
The gateway endpoint is account- and gateway-scoped, so its base URL is resolved
from your account ID and gateway ID. Three environment variables are required:
```bash
export CLOUDFLARE_ACCOUNT_ID=your-account-id
export CLOUDFLARE_GATEWAY_ID=your-gateway-id
export CLOUDFLARE_API_TOKEN=your-api-token
```
Create a gateway from the
[AI Gateway dashboard](https://dash.cloudflare.com/?to=/:account/ai/ai-gateway)
and an API token with the appropriate permissions.
## Usage
AI Gateway model IDs use the gateway's `provider/model` form (for example
`workers-ai/@cf/meta/llama-3.1-8b-instruct` or `openai/gpt-4o`); the gateway
routes each request to the underlying provider.
### Inline Syntax
```yaml
agents:
root:
model: cloudflare-ai-gateway/workers-ai/@cf/meta/llama-3.1-8b-instruct
description: Assistant using Cloudflare AI Gateway
instruction: You are a helpful assistant.
```
### Named Model
For more control over parameters:
```yaml
models:
cloudflare_model:
provider: cloudflare-ai-gateway
model: "workers-ai/@cf/meta/llama-3.1-8b-instruct"
temperature: 0.7
max_tokens: 8192
agents:
root:
model: cloudflare_model
description: Assistant using Cloudflare AI Gateway
instruction: You are a helpful assistant.
```
## Available Models
AI Gateway exposes models from many providers behind one endpoint. Check the
[AI Gateway documentation](https://developers.cloudflare.com/ai-gateway/) for
the current provider list, model IDs, and how billing works.
> Model IDs are case-sensitive and must be passed exactly as the gateway lists
> them, including the `provider/` prefix.
## How It Works
Cloudflare AI Gateway is implemented as a built-in alias in docker-agent:
- **API Type:** OpenAI-compatible (`openai_chatcompletions`)
- **Base URL:** `https://gateway.ai.cloudflare.com/v1/${CLOUDFLARE_ACCOUNT_ID}/${CLOUDFLARE_GATEWAY_ID}/compat`
- **Token Variable:** `CLOUDFLARE_API_TOKEN`
The base URL is templated: `${CLOUDFLARE_ACCOUNT_ID}` and
`${CLOUDFLARE_GATEWAY_ID}` are substituted from the environment when the provider
is built, so both must be set in addition to `CLOUDFLARE_API_TOKEN`. Because the
gateway can route to open-weight models with strict chat templates, docker-agent
coalesces consecutive system messages into a single leading one for this
provider.
## Authentication
docker-agent authenticates by sending `CLOUDFLARE_API_TOKEN` in the standard
`Authorization: Bearer` header. On the `.../compat` endpoint that header is
treated as the **provider** key, so this alias works when:
- the gateway has **authentication disabled** (the default), and
- the routed models accept that token as their provider key, which is the case
for **Workers AI** models (`workers-ai/@cf/...`).
A gateway with **authentication enabled** (required for
[unified billing](https://developers.cloudflare.com/ai-gateway/features/unified-billing/))
instead expects the token in Cloudflare's `cf-aig-authorization` header. The
alias does not send that header, and custom `provider_opts.http_headers` values
are not environment-expanded, so an authenticated gateway is **not supported out
of the box** today. For that setup, use an unauthenticated gateway, or configure
a [custom provider](../custom/index.md) against the
Cloudflare AI Gateway REST API.

View File

@@ -0,0 +1,90 @@
---
title: "Cloudflare Workers AI"
description: "Use Cloudflare Workers AI models with docker-agent."
keywords: docker agent, ai agents, model providers, llm, cloudflare workers ai
weight: 70
---
_Use Cloudflare Workers AI models with docker-agent._
## Overview
[Cloudflare Workers AI](https://developers.cloudflare.com/workers-ai/) runs
open-weight models (Llama, Mistral, Qwen, Gemma, and more) on Cloudflare's
global edge network through an OpenAI-compatible endpoint. No separate provider
accounts are needed for the supported models. docker-agent includes built-in
support for Workers AI as an alias provider.
## Setup
Workers AI is account-scoped, so its base URL is resolved from your account ID.
Two environment variables are required:
```bash
export CLOUDFLARE_ACCOUNT_ID=your-account-id
export CLOUDFLARE_API_TOKEN=your-api-token
```
Create an API token with the `Workers AI` permission from the
[Cloudflare dashboard](https://dash.cloudflare.com/profile/api-tokens). Your
account ID is shown on the Workers AI page.
## Usage
Workers AI model IDs use the `@cf/...` form (for example
`@cf/meta/llama-3.1-8b-instruct`).
### Inline Syntax
```yaml
agents:
root:
model: cloudflare-workers-ai/@cf/meta/llama-3.1-8b-instruct
description: Assistant using Cloudflare Workers AI
instruction: You are a helpful assistant.
```
### Named Model
For more control over parameters:
```yaml
models:
cloudflare_model:
provider: cloudflare-workers-ai
model: "@cf/meta/llama-3.1-8b-instruct"
temperature: 0.7
max_tokens: 8192
agents:
root:
model: cloudflare_model
description: Assistant using Cloudflare Workers AI
instruction: You are a helpful assistant.
```
## Available Models
Check the
[Workers AI models catalog](https://developers.cloudflare.com/workers-ai/models/)
for the current list, IDs, and pricing.
| Model | Description |
| --- | --- |
| `@cf/meta/llama-3.1-8b-instruct` | Meta Llama 3.1 8B Instruct |
| `@cf/mistralai/mistral-small-3.1-24b-instruct` | Mistral Small 3.1 24B Instruct |
| `@cf/qwen/qwen2.5-coder-32b-instruct` | Qwen 2.5 Coder 32B Instruct |
## How It Works
Cloudflare Workers AI is implemented as a built-in alias in docker-agent:
- **API Type:** OpenAI-compatible (`openai_chatcompletions`)
- **Base URL:** `https://api.cloudflare.com/client/v4/accounts/${CLOUDFLARE_ACCOUNT_ID}/ai/v1`
- **Token Variable:** `CLOUDFLARE_API_TOKEN`
The base URL is templated: `${CLOUDFLARE_ACCOUNT_ID}` is substituted from the
environment when the provider is built, so `CLOUDFLARE_ACCOUNT_ID` must be set in
addition to `CLOUDFLARE_API_TOKEN`. Because Workers AI serves open-weight models
with strict chat templates, docker-agent coalesces consecutive system messages
into a single leading one for this provider.

View File

@@ -0,0 +1,265 @@
---
title: "Provider Definitions"
description: "Define reusable provider configurations with shared defaults for any provider type — OpenAI, Anthropic, Google, Bedrock, and more."
keywords: docker agent, ai agents, model providers, llm, provider definitions
weight: 280
---
_Define reusable provider configurations with shared defaults for any provider type — OpenAI, Anthropic, Google, Bedrock, and more._
## Overview
The `providers` section in your agent YAML lets you define named provider configurations that models can reference. This is useful for:
- **Grouping shared defaults** — Set temperature, max_tokens, thinking_budget once and share across models
- **Custom endpoints** — Connect to self-hosted models, API proxies, or gateways
- **Centralizing credentials** — Define token_key once for all models using a provider
- **Any provider type** — Works with OpenAI, Anthropic, Google, Bedrock, and any OpenAI-compatible API
> [!NOTE]
> **Works with any provider**
>
> The `providers` section supports all provider types: `openai`, `anthropic`, `google`, `amazon-bedrock`, `dmr`, and any built-in alias. When the `provider` field is not set, it defaults to `openai` for backward compatibility.
## Configuration
### OpenAI-compatible endpoint
```yaml
providers:
my_gateway:
base_url: https://api.example.com/v1
token_key: MY_API_KEY
models:
my_model:
provider: my_gateway
model: gpt-4o
agents:
root:
model: my_model
instruction: You are a helpful assistant.
```
### Anthropic with shared defaults
```yaml
providers:
my_anthropic:
provider: anthropic
token_key: MY_ANTHROPIC_KEY
max_tokens: 16384
thinking_budget: 8192
models:
claude_smart:
provider: my_anthropic
model: claude-sonnet-4-5
# Inherits max_tokens: 16384, thinking_budget: 8192
claude_fast:
provider: my_anthropic
model: claude-haiku-4-5
thinking_budget: 1024 # Overrides provider default
agents:
root:
model: claude_smart
instruction: You are a helpful assistant.
```
### Google with shared temperature
```yaml
providers:
my_google:
provider: google
temperature: 0.3
models:
gemini:
provider: my_google
model: gemini-2.5-flash
# Inherits temperature: 0.3
agents:
root:
model: gemini
instruction: You are a helpful assistant.
```
## Provider Properties
| Property | Type | Description | Default |
| --------------------- | ---------- | ------------------------------------------------------------------------------------- | ------------------------ |
| `provider` | string | Underlying provider type: `openai`, `anthropic`, `google`, `amazon-bedrock`, `dmr`, etc. | `openai` |
| `api_type` | string | API schema: `openai_chatcompletions` or `openai_responses`. Only for OpenAI-compatible providers. When omitted, the API type is selected automatically based on the model name: newer models (gpt-4.1, o-series, gpt-5, Codex) default to `openai_responses`; all others default to `openai_chatcompletions`. | `auto (model-dependent)` |
| `base_url` | string | Base URL for the API endpoint. Required for OpenAI-compatible providers, optional for native providers. | — |
| `token_key` | string | Environment variable name containing the API token. | — |
| `unload_api` | string | Optional path (or absolute URL) to the provider's model-unload endpoint. Used by the [`unload`](../../configuration/hooks/index.md#available-built-ins) built-in hook to release model resources between agent switches. Relative paths resolve against `base_url`'s scheme + host; absolute URLs are used verbatim. Today only Docker Model Runner ships a provider that calls this endpoint; cloud providers don't implement the underlying interface and the hook silently skips them. | — |
| `temperature` | float | Default sampling temperature (0.0–2.0). | — |
| `max_tokens` | int | Default maximum response tokens. | — |
| `top_p` | float | Default nucleus sampling threshold (0.0–1.0). | — |
| `frequency_penalty` | float | Default frequency penalty (-2.0–2.0). | — |
| `presence_penalty` | float | Default presence penalty (-2.0–2.0). | — |
| `parallel_tool_calls` | boolean | Whether to enable parallel tool calls by default. | — |
| `track_usage` | boolean | Whether to track token usage by default. | — |
| `thinking_budget` | string/int | Default reasoning effort/budget. | — |
| `task_budget` | int/object | Default total token budget for an agentic task (forwarded to Anthropic; honored by Claude Opus 4.7 today). Integer shorthand or `{type: tokens, total: N}`. | — |
| `provider_opts` | object | Provider-specific options passed through to the client. | — |
## Default Inheritance
Models referencing a provider inherit all its defaults. Model-level settings always take precedence:
```yaml
providers:
my_anthropic:
provider: anthropic
token_key: MY_ANTHROPIC_KEY
max_tokens: 16384
temperature: 0.7
thinking_budget: high
models:
# Inherits everything from provider
claude_default:
provider: my_anthropic
model: claude-sonnet-4-5
# Overrides temperature and thinking_budget, inherits the rest
claude_custom:
provider: my_anthropic
model: claude-sonnet-4-5
temperature: 0.2
thinking_budget: low
```
## Shorthand Syntax
Once a provider is defined, you can use the shorthand `provider_name/model` syntax:
```yaml
agents:
root:
model: my_gateway/gpt-4o-mini # uses the provider's defaults
researcher:
model: my_anthropic/claude-sonnet-4-5 # uses anthropic provider defaults
```
## API Types
Only applicable for OpenAI-compatible providers (when `provider` is `openai` or unset):
- **`openai_chatcompletions`** — Standard OpenAI Chat Completions API. Works with most OpenAI-compatible endpoints.
- **`openai_responses`** — OpenAI Responses API. For newer models that require the Responses API format.
> If `api_type` is not set, docker-agent automatically selects the API type based on the model name. You only need to set `api_type` explicitly to override the detected default.
## Examples
### vLLM / Ollama
```yaml
providers:
local_llm:
base_url: http://localhost:8000/v1
agents:
root:
model: local_llm/llama-3.1-8b
```
> [!NOTE]
> **Reasoning tokens from OpenAI-compatible providers**
>
> Models that stream reasoning under `delta.reasoning` (e.g. Qwen3 served via OVHcloud AI Endpoints, OpenRouter, or a self-hosted vLLM / SGLang deployment) are fully supported. Docker Agent reads both the `delta.reasoning_content` and `delta.reasoning` fields from the stream, so thinking blocks are captured and shown in the TUI regardless of which field the server uses.
### API Router (Requesty, LiteLLM)
```yaml
providers:
router:
base_url: https://router.requesty.ai/v1
token_key: REQUESTY_API_KEY
agents:
root:
model: router/anthropic/claude-sonnet-4-5
```
### Azure OpenAI
```yaml
models:
azure_model:
provider: azure
model: gpt-4o
base_url: https://your-llm.openai.azure.com
provider_opts:
api_version: 2024-12-01-preview
```
### Anthropic Team Setup
```yaml
providers:
team_anthropic:
provider: anthropic
token_key: TEAM_ANTHROPIC_KEY
max_tokens: 32768
thinking_budget: high
temperature: 0.5
models:
architect:
provider: team_anthropic
model: claude-sonnet-4-5
reviewer:
provider: team_anthropic
model: claude-haiku-4-5
thinking_budget: low # faster reviews
agents:
root:
model: architect
sub_agents: [code_reviewer]
code_reviewer:
model: reviewer
```
### Multi-Provider with Shared Defaults
```yaml
providers:
fast_openai:
base_url: https://api.openai.com/v1
token_key: OPENAI_API_KEY
temperature: 0.3
max_tokens: 8192
smart_anthropic:
provider: anthropic
token_key: ANTHROPIC_API_KEY
max_tokens: 64000
thinking_budget: high
agents:
root:
model: smart_anthropic/claude-sonnet-4-5
sub_agents: [helper]
helper:
model: fast_openai/gpt-4o-mini
```
## How It Works
When you reference a provider:
1. The provider's `provider` field determines which API client to use (defaults to `openai`)
2. The provider's `base_url` and `token_key` are applied to the model (if not already set on the model)
3. All model-level defaults (temperature, max_tokens, thinking_budget, etc.) are inherited (model settings take precedence)
4. For OpenAI-compatible providers, the `api_type` is stored in `provider_opts.api_type`
5. The model is used with the appropriate API client

View File

@@ -0,0 +1,95 @@
---
title: "DeepSeek"
description: "Use DeepSeek models with docker-agent."
keywords: docker agent, ai agents, model providers, llm, deepseek
weight: 80
---
_Use DeepSeek models with docker-agent._
## Overview
[DeepSeek](https://www.deepseek.com/) serves its frontier chat and reasoning
models through an OpenAI-compatible API, with strong price/performance on coding
and reasoning tasks. docker-agent includes built-in support for DeepSeek as an
alias provider.
## Setup
1. Create an API key from the [DeepSeek Platform](https://platform.deepseek.com/api_keys).
2. Set the environment variable:
```bash
export DEEPSEEK_API_KEY=your-api-key
```
## Usage
### Inline Syntax
The simplest way to use DeepSeek:
```yaml
agents:
root:
model: deepseek/deepseek-chat
description: Assistant using DeepSeek
instruction: You are a helpful assistant.
```
### Named Model
For more control over parameters:
```yaml
models:
deepseek_model:
provider: deepseek
model: deepseek-chat
temperature: 0.7
max_tokens: 8192
agents:
root:
model: deepseek_model
description: Assistant using DeepSeek
instruction: You are a helpful assistant.
```
## Available Models
DeepSeek exposes a small, vendor-controlled model lineup. Check the
[DeepSeek models documentation](https://api-docs.deepseek.com/quick_start/pricing)
for current model IDs, context limits, and pricing.
| Model | Description |
| --- | --- |
| `deepseek-chat` | DeepSeek-V3, general-purpose chat and tool calling |
| `deepseek-reasoner` | DeepSeek-R1, extended-reasoning model |
> Model IDs are case-sensitive and must be passed exactly as the catalogue lists
> them.
## How It Works
DeepSeek is implemented as a built-in alias in docker-agent:
- **API Type:** OpenAI-compatible (`openai_chatcompletions`)
- **Base URL:** `https://api.deepseek.com/v1`
- **Token Variable:** `DEEPSEEK_API_KEY`
## Example: Code Assistant
```yaml
agents:
coder:
model: deepseek/deepseek-chat
description: Code assistant using DeepSeek-V3
instruction: |
You are an expert programmer.
Write clean, well-documented code and follow language best practices.
toolsets:
- type: filesystem
- type: shell
- type: think
```

View File

@@ -0,0 +1,252 @@
---
title: "Docker Model Runner"
description: "Run AI models locally with Docker — no API keys, no costs, full data privacy."
keywords: docker agent, ai agents, model providers, llm, docker model runner
weight: 90
---
_Run AI models locally with Docker — no API keys, no costs, full data privacy._
## Overview
Docker Model Runner (DMR) lets you run open-source AI models directly on your machine. Models run in Docker, so there's no API key needed and no data leaves your computer.
docker-agent automatically discovers models you have already pulled from DMR. When no model is explicitly configured, auto-selection prefers a locally-installed model (choosing the model specified via the `model:` key in the agent YAML if it is already pulled locally, or otherwise the first available non-embedding model) rather than always defaulting to `ai/qwen3:latest` and triggering a pull prompt.
> [!TIP]
> **No API key needed**
>
> DMR runs models locally — your data never leaves your machine. Great for development, sensitive data, or offline use.
## Prerequisites
- [Docker Desktop](https://www.docker.com/products/docker-desktop/) with the Model Runner feature enabled
- Verify with: `docker model status --json`
## Configuration
### Inline
```yaml
agents:
root:
model: dmr/ai/qwen3
```
### Named Model
```yaml
models:
local:
provider: dmr
model: ai/qwen3
max_tokens: 8192
```
## Available Models
Any model available through Docker Model Runner can be used. Common options:
| Model | Description |
| ------------- | ----------------------------------------------------- |
| `ai/qwen3` | Qwen 3 — versatile, good for coding and general tasks |
| `ai/llama3.2` | Llama 3.2 — Meta's open-source model |
## Runtime Flags
Pass flags to the underlying inference runtime (e.g., llama.cpp) using `provider_opts.runtime_flags`:
```yaml
models:
local:
provider: dmr
model: ai/qwen3
max_tokens: 8192
provider_opts:
runtime_flags: ["--threads", "8"]
```
Runtime flags also accept a single string:
```yaml
provider_opts:
runtime_flags: "--threads 8"
```
Use only flags your Model Runner backend allows (see `docker model configure --help` and backend docs). **Do not** put sampling parameters (`temperature`, `top_p`, penalties) in `runtime_flags` — set them on the model (`temperature`, `top_p`, etc.); they are sent **per request** via the OpenAI-compatible chat API.
## Context size
`max_tokens` controls the **maximum output tokens** per chat completion request. To set the engine's **total context window**, use `provider_opts.context_size`:
```yaml
models:
local:
provider: dmr
model: ai/qwen3
max_tokens: 4096 # max output tokens (per-request)
provider_opts:
context_size: 32768 # total context window (sent via _configure)
```
If `context_size` is omitted, Model Runner uses its default. `max_tokens` is **not** used as the context window.
docker-agent's auto-compaction scales its summary and keep-tail token budgets proportionally to `context_size`. This ensures compaction works correctly even for small context windows — for example, an 8k-token local model will not have its session history wiped during compaction.
## Thinking / reasoning budget
When using the **llama.cpp** backend, `thinking_budget` is sent as structured `llamacpp.reasoning-budget` on `_configure` (maps to `--reasoning-budget`). String efforts use the same token mapping as other providers; `adaptive` maps to unlimited (`-1`).
When using the **vLLM** backend, `thinking_budget` is sent as `thinking_token_budget` in each chat completion request. Effort levels map to token counts using the same scale as other providers; `adaptive` maps to unlimited (`-1`).
```yaml
models:
local:
provider: dmr
model: ai/qwen3
thinking_budget: medium # llama.cpp: reasoning-budget=8192; vLLM: thinking_token_budget=8192
```
On **MLX** and **SGLang** backends, `thinking_budget` is silently ignored — those engines do not currently expose a per-request reasoning token budget knob.
## vLLM-specific configuration
When running a model on the **vLLM** backend, additional engine-level settings can be passed via `provider_opts` and are forwarded to model-runner's `_configure` endpoint:
- `gpu_memory_utilization` — fraction of GPU memory (0.0–1.0) vLLM may use. Values outside this range are rejected.
- `hf_overrides` — map of Hugging Face config overrides applied when vLLM loads the model.
```yaml
models:
vllm-local:
provider: dmr
model: ai/some-model-safetensors
provider_opts:
gpu_memory_utilization: 0.9
hf_overrides:
max_model_len: 8192
dtype: bfloat16
```
`hf_overrides` keys (including nested ones) must match `^[a-zA-Z_][a-zA-Z0-9_]*$` — the same rule model-runner enforces server-side to block injection via flags. Invalid keys are rejected at client creation time so you fail fast instead of after a round-trip.
These options are ignored on non-vLLM backends.
## Keeping models resident in memory (`keep_alive`)
By default model-runner unloads idle models after a few minutes. Override the idle timeout via `provider_opts.keep_alive`:
```yaml
models:
sticky:
provider: dmr
model: ai/qwen3
provider_opts:
keep_alive: "30m" # duration string
# keep_alive: "0" # unload immediately after each request
# keep_alive: "-1" # keep loaded forever
```
Accepted values: any Go duration string (`"30s"`, `"5m"`, `"1h"`, `"2h30m"`), `"0"` (immediate unload), or `"-1"` (never unload). Invalid values are rejected before the configure request is sent.
## Unloading models on agent switch
In multi-agent setups where two DMR models can't fit in GPU memory simultaneously, wire the [`unload`](../../configuration/hooks/index.md#available-built-ins) built-in hook into each agent's `on_agent_switch` chain. Every time the active agent transfers control, the runtime POSTs to the engine's `_unload` endpoint to free the previous model's resources before the next one is loaded:
```yaml
agents:
coder:
model: qwen3-large
handoffs: [reviewer]
hooks:
on_agent_switch:
- type: builtin
command: unload
reviewer:
model: qwen3-coder
handoffs: [coder]
hooks:
on_agent_switch:
- type: builtin
command: unload
```
The unload URL is derived from `base_url` by replacing the trailing `/v1` segment (e.g. `http://127.0.0.1:12434/engines/llama.cpp/v1/` → `http://127.0.0.1:12434/engines/llama.cpp/_unload`). Override it explicitly via the provider-level `unload_api` field when running against a non-standard model-runner deployment:
```yaml
providers:
my_dmr:
provider: dmr
base_url: http://model-runner.docker.internal/engines/v1
unload_api: /engines/_unload # default; absolute URLs also work
models:
big:
provider: my_dmr
model: ai/qwen3
```
Unload errors are logged and swallowed — a stuck or unreachable engine never blocks an agent transfer (each call is bounded to 10 s). Pair this with [`keep_alive`](#keeping-models-resident-in-memory-keep_alive) only when you want the model to *also* survive idle periods within a single agent's run; the hook controls **between-agent** unloads independently.
> [!WARNING]
> **Single-tenant assumption**
>
> The `_unload` endpoint is engine-level: it evicts the model from DMR's memory regardless of who is using it. If two concurrent sessions on the same runtime (e.g. an API server serving multiple users) hit the same agent, switching away in session A will yank the model out from under session B's in-flight request, which then has to wait for a reload. Wire `unload` only when the agents using these models are not run concurrently — typically a single TUI/CLI session.
See [`examples/unload_on_switch.yaml`](https://github.com/docker/docker-agent/blob/main/examples/unload_on_switch.yaml) for the full example.
## Operating mode (`mode`)
Model-runner normally infers the backend mode from the request path. You can pin it explicitly via `provider_opts.mode`:
```yaml
provider_opts:
mode: embedding # one of: completion, embedding, reranking, image-generation
```
Most agents don't need this — leave it unset unless you know you need it.
## Raw runtime flags (`raw_runtime_flags`)
`runtime_flags` (a list) is the preferred way to pass flags. If you have a pre-built command-line string you'd rather ship verbatim, use `raw_runtime_flags` instead:
```yaml
provider_opts:
raw_runtime_flags: "--threads 8 --batch-size 512"
```
Model-runner parses the string with shell-style word splitting. `runtime_flags` and `raw_runtime_flags` are mutually exclusive — setting both is an error.
## Speculative Decoding
Use a smaller draft model to predict tokens ahead for faster inference:
```yaml
models:
fast-local:
provider: dmr
model: ai/qwen3:14B
max_tokens: 8192
provider_opts:
speculative_draft_model: ai/qwen3:0.6B-F16
speculative_num_tokens: 16
speculative_acceptance_rate: 0.8
```
## Custom Endpoint
If `base_url` is omitted, docker-agent auto-discovers the DMR endpoint. To set manually:
```yaml
models:
local:
provider: dmr
model: ai/qwen3
base_url: http://127.0.0.1:12434/engines/llama.cpp/v1
```
## Troubleshooting
- **Plugin not found:** Ensure Docker Model Runner is enabled in Docker Desktop. docker-agent will fall back to the default URL.
- **Endpoint empty:** Verify the Model Runner is running with `docker model status --json`.
- **Performance:** Use `runtime_flags` to tune GPU layers (`--ngl`) and thread count (`--threads`).

View File

@@ -0,0 +1,101 @@
---
title: "Fireworks AI"
description: "Use Fireworks AI models with docker-agent."
keywords: docker agent, ai agents, model providers, llm, fireworks ai
weight: 100
---
_Use Fireworks AI models with docker-agent._
## Overview
[Fireworks AI](https://fireworks.ai/) is a fast inference host for open-weight
models, serving Kimi K2, Llama, Qwen, DeepSeek, GLM and others through an
OpenAI-compatible API. docker-agent includes built-in support for Fireworks AI
as an alias provider.
## Setup
1. Create an API key from the [Fireworks dashboard](https://fireworks.ai/account/api-keys).
2. Set the environment variable:
```bash
export FIREWORKS_API_KEY=your-api-key
```
## Usage
### Inline Syntax
The simplest way to use Fireworks AI:
```yaml
agents:
root:
model: fireworks/accounts/fireworks/models/kimi-k2-instruct
description: Assistant using Fireworks AI
instruction: You are a helpful assistant.
```
### Named Model
For more control over parameters:
```yaml
models:
fireworks_model:
provider: fireworks
model: accounts/fireworks/models/kimi-k2-instruct
temperature: 0.7
max_tokens: 8192
agents:
root:
model: fireworks_model
description: Assistant using Fireworks AI
instruction: You are a helpful assistant.
```
## Available Models
Fireworks serves a broad, changing catalog of open-weight models. Model IDs use
the `accounts/fireworks/models/<name>` form. Check the
[Fireworks model library](https://fireworks.ai/models) for current IDs, context
limits, and pricing.
| Model | Description |
| --- | --- |
| `accounts/fireworks/models/kimi-k2-instruct` | Kimi K2, large open MoE chat and tool-calling model |
| `accounts/fireworks/models/llama-v3p3-70b-instruct` | Llama 3.3 70B instruct |
| `accounts/fireworks/models/qwen3-235b-a22b` | Qwen 3 235B MoE |
> Model IDs are case-sensitive and must be passed exactly as the catalogue lists
> them.
## How It Works
Fireworks AI is implemented as a built-in alias in docker-agent:
- **API Type:** OpenAI-compatible (`openai_chatcompletions`)
- **Base URL:** `https://api.fireworks.ai/inference/v1`
- **Token Variable:** `FIREWORKS_API_KEY`
Because Fireworks fronts open-weight models whose chat templates may reject more
than one leading system message, docker-agent coalesces its per-source system
messages into a single one for this provider.
## Example: Code Assistant
```yaml
agents:
coder:
model: fireworks/accounts/fireworks/models/kimi-k2-instruct
description: Code assistant using Kimi K2 on Fireworks AI
instruction: |
You are an expert programmer.
Write clean, well-documented code and follow language best practices.
toolsets:
- type: filesystem
- type: shell
- type: think
```

View File

@@ -0,0 +1,146 @@
---
title: "GitHub Copilot"
description: "Use GitHub Copilot's hosted models (GPT-4o, Claude, Gemini, and more) with docker-agent through your GitHub subscription."
keywords: docker agent, ai agents, model providers, llm, github copilot
weight: 110
---
_Use GitHub Copilot's hosted models with docker-agent through your existing GitHub subscription._
## Overview
GitHub Copilot exposes an OpenAI-compatible Chat Completions API at
`https://api.githubcopilot.com`. docker-agent ships with built-in support for
it as the `github-copilot` provider, so any user with a paid GitHub Copilot
subscription can reuse their entitlement from docker-agent.
## Prerequisites
- An active **GitHub Copilot** subscription (Individual, Business, or Enterprise).
- A **personal access token** with the `copilot` scope, exported as `GITHUB_TOKEN`.
```bash
export GITHUB_TOKEN="ghp_..."
```
## Configuration
### Inline
```yaml
agents:
root:
model: github-copilot/gpt-4o
instruction: You are a helpful assistant.
```
### Named model
```yaml
models:
copilot:
provider: github-copilot
model: gpt-4o
temperature: 0.7
max_tokens: 4000
agents:
root:
model: copilot
```
## Available Models
The exact set of models you can call depends on your Copilot plan. The most
commonly available ones today are:
| Model | Best For |
| ------------------------ | ----------------------------------- |
| `gpt-4o` | Multimodal, balanced performance |
| `gpt-4o-mini` | Fast and cheap |
| `claude-sonnet-4` | Strong coding and analysis |
| `gemini-2.5-pro` | Google's flagship, large context |
| `o3-mini` | Reasoning-focused |
Check the
[GitHub Copilot documentation](https://docs.github.com/en/copilot)
for the current model list.
## `Copilot-Integration-Id` Header
GitHub's Copilot API rejects requests that don't carry a
`Copilot-Integration-Id` header with a `Bad Request` error. docker-agent
automatically sends `copilot-developer-cli` for the `github-copilot`
provider, so PAT-based usage works out of the box.
We specifically chose `copilot-developer-cli` (instead of, say,
`vscode-chat`) because it is the integration id accepted by the Copilot
API for **both** OAuth tokens and Personal Access Tokens. Most
docker-agent users authenticate with a PAT exported as `GITHUB_TOKEN`,
and `vscode-chat` is rejected for those tokens.
If you need to send a different integration id — for example if your
organization allows-lists a specific value — you can override it via
`provider_opts.http_headers`:
```yaml
models:
copilot:
provider: github-copilot
model: gpt-4o
provider_opts:
http_headers:
Copilot-Integration-Id: my-custom-integration
```
Header names are matched case-insensitively, so `copilot-integration-id`
works too.
## Chat Completions vs. Responses API
GitHub Copilot proxies OpenAI models behind two endpoints: the legacy
`/chat/completions` and the newer `/responses`. Newer models (the `gpt-5`
family, Codex variants, etc.) are only served via `/responses` and reject
`/chat/completions` with a `400 Bad Request`. docker-agent auto-selects the
right endpoint per model, so no configuration is needed in the common case.
If you ever need to force one or the other, set `api_type` explicitly:
```yaml
models:
copilot:
provider: github-copilot
model: gpt-5
provider_opts:
api_type: openai_responses # or openai_chatcompletions
```
## Custom HTTP Headers
`provider_opts.http_headers` is a generic escape hatch that works for any
OpenAI-compatible provider, not just GitHub Copilot. Every key/value pair
is added to every outgoing request:
```yaml
models:
my_model:
provider: openai
model: gpt-4o
provider_opts:
http_headers:
X-Request-Source: docker-agent
X-Tenant-Id: my-team
```
## How It Works
GitHub Copilot is implemented as a built-in alias in docker-agent:
- **API type:** OpenAI-compatible (Chat Completions)
- **Base URL:** `https://api.githubcopilot.com`
- **Token variable:** `GITHUB_TOKEN`
- **Default headers:** `Copilot-Integration-Id: copilot-developer-cli`
This means the same client as OpenAI is used, so every OpenAI feature
supported by docker-agent (tool calling, structured output, multimodal
inputs, etc.) is available when the underlying model supports it.

View File

@@ -0,0 +1,173 @@
---
title: "Google Gemini"
description: "Use Gemini 2.5 Flash, Gemini 3 Pro, and other Google models with docker-agent."
keywords: docker agent, ai agents, model providers, llm, google gemini
weight: 120
---
_Use Gemini 2.5 Flash, Gemini 3 Pro, and other Google models with docker-agent._
## Setup
docker-agent reads the first credential it finds from these environment variables (see `pkg/model/provider/gemini/client.go`):
| Variable | Purpose |
| --------------------------- | ----------------------------------------------------------------------------------- |
| `GOOGLE_API_KEY` | Primary Gemini API key. |
| `GEMINI_API_KEY` | Alternative name for the Gemini API key (also used by the official Google SDK). |
| `GOOGLE_GENAI_USE_VERTEXAI` | When set (any value), routes through Vertex AI instead of the Gemini Developer API. |
| `GOOGLE_CLOUD_PROJECT` | GCP project used when `GOOGLE_GENAI_USE_VERTEXAI` is set or for Vertex AI Model Garden. |
| `GOOGLE_CLOUD_LOCATION` | GCP region for Vertex AI (defaults to the SDK default). |
```bash
# Gemini Developer API
export GOOGLE_API_KEY="AI..." # or GEMINI_API_KEY
# Vertex AI (no API key; uses Application Default Credentials)
gcloud auth application-default login
export GOOGLE_GENAI_USE_VERTEXAI=1
export GOOGLE_CLOUD_PROJECT="my-gcp-project"
export GOOGLE_CLOUD_LOCATION="us-central1"
```
## Configuration
### Inline
```yaml
agents:
root:
model: google/gemini-3.5-flash
```
### Named Model
```yaml
models:
gemini:
provider: google
model: gemini-3.5-flash
temperature: 0.5
```
## Available Models
| Model | Best For |
| ------------------ | ------------------------------- |
| `gemini-3-pro` | Most capable Gemini model |
| `gemini-3-flash` | Fast, efficient, good balance |
| `gemini-2.5-flash` | Fast inference, cost-effective |
| `gemini-2.5-pro` | Strong reasoning, large context |
## Thinking Budget
Gemini supports two approaches depending on the model version:
> [!WARNING]
> **Different thinking formats**
>
> Gemini 2.5 uses **token-based** budgets (integers). Gemini 3 uses **level-based** budgets (strings like `low`, `high`). Make sure you use the right format for your model version.
### Gemini 2.5 (Token-based)
```yaml
models:
gemini-no-thinking:
provider: google
model: gemini-2.5-flash
thinking_budget: 0 # disable thinking
gemini-dynamic:
provider: google
model: gemini-2.5-flash
thinking_budget: -1 # dynamic (model decides) — default
gemini-fixed:
provider: google
model: gemini-2.5-flash
thinking_budget: 8192 # fixed token budget
```
### Gemini 3 (Level-based)
```yaml
models:
gemini-3-pro:
provider: google
model: gemini-3-pro
thinking_budget: high # default for Pro: low | high
gemini-3-flash:
provider: google
model: gemini-3-flash
thinking_budget: medium # default for Flash: minimal | low | medium | high
```
## Built-in Tools (Grounding)
Gemini models support built-in tools that let the model access Google Search and Google Maps
directly during generation. Enable them via `provider_opts`:
```yaml
models:
gemini-grounded:
provider: google
model: gemini-2.5-flash
provider_opts:
google_search: true
google_maps: true
code_execution: true
```
| Option | Description |
| ---------------- | ---------------------------------------------------- |
| `google_search` | Enables Google Search grounding for up-to-date info |
| `google_maps` | Enables Google Maps grounding for location queries |
| `code_execution` | Enables server-side code execution for computations |
## Vertex AI Model Garden
You can use non-Gemini models (e.g. Claude, Llama) hosted on Google Cloud's
[Vertex AI Model Garden](https://cloud.google.com/vertex-ai/generative-ai/docs/partner-models/use-partner-models)
through the `google` provider. When a `publisher` is specified in `provider_opts`,
requests are routed through the appropriate Vertex AI endpoint instead of the
Gemini SDK:
- **Anthropic Claude** (`publisher: anthropic`) uses the Anthropic-native
`:rawPredict` / `:streamRawPredict` endpoints. Claude models on Vertex AI do
not support the OpenAI `/chat/completions` path.
- **Other publishers** (e.g. `meta`, `mistral`) use Vertex AI's
OpenAI-compatible `/chat/completions` endpoint.
### Authentication
Vertex AI uses Google Cloud Application Default Credentials (ADC). Make sure you
are authenticated:
```bash
gcloud auth application-default login
```
### Configuration
```yaml
models:
claude-on-vertex:
provider: google
model: claude-sonnet-4-20250514
provider_opts:
project: my-gcp-project # GCP project ID (or set GOOGLE_CLOUD_PROJECT)
location: us-east5 # GCP region (or set GOOGLE_CLOUD_LOCATION)
publisher: anthropic # Model publisher (anthropic, meta, etc.)
```
| Option | Description |
| ----------- | ------------------------------------------------------------------------------------ |
| `project` | GCP project ID. Falls back to `GOOGLE_CLOUD_PROJECT` env var |
| `location` | GCP region (e.g. `us-east5`, `us-central1`). Falls back to `GOOGLE_CLOUD_LOCATION` |
| `publisher` | Model publisher (e.g. `anthropic`, `meta`, `mistral`). Must not be `google` |
> [!NOTE]
> **Gemini models on Vertex AI**
>
> Setting `publisher: google` (or omitting `publisher`) uses the native Gemini SDK path. The Model Garden endpoint is only used for non-Google publishers.

View File

@@ -0,0 +1,98 @@
---
title: "Groq"
description: "Use Groq fast-inference models with docker-agent."
keywords: docker agent, ai agents, model providers, llm, groq
weight: 130
---
_Use Groq models with docker-agent._
## Overview
[Groq](https://groq.com/) serves open-weight models on its LPU inference engine
through an OpenAI-compatible API, with a focus on very low latency. docker-agent
includes built-in support for Groq as an alias provider.
## Setup
1. Create an API key from the [Groq Console](https://console.groq.com/keys).
2. Set the environment variable:
```bash
export GROQ_API_KEY=your-api-key
```
## Usage
### Inline Syntax
The simplest way to use Groq:
```yaml
agents:
root:
model: groq/llama-3.3-70b-versatile
description: Assistant using Groq
instruction: You are a helpful assistant.
```
### Named Model
For more control over parameters:
```yaml
models:
groq_model:
provider: groq
model: llama-3.3-70b-versatile
temperature: 0.7
max_tokens: 8192
agents:
root:
model: groq_model
description: Assistant using Groq
instruction: You are a helpful assistant.
```
## Available Models
Groq hosts a rotating catalogue of open-weight models. Check the
[Groq models documentation](https://console.groq.com/docs/models) for current
model IDs, context limits, and rate limits.
| Model | Description |
| --- | --- |
| `llama-3.3-70b-versatile` | Llama 3.3 70B, reliable general-purpose chat and tool calling |
| `llama-3.1-8b-instant` | Llama 3.1 8B, fastest and cheapest |
| `openai/gpt-oss-120b` | GPT-OSS 120B, strong reasoning and tool calling |
| `openai/gpt-oss-20b` | GPT-OSS 20B, compact reasoning model |
| `qwen/qwen3-32b` | Qwen3 32B, reasoning and tool calling |
| `meta-llama/llama-4-scout-17b-16e-instruct` | Llama 4 Scout MoE |
> Model IDs are case-sensitive and must be passed exactly as the catalogue lists
> them.
## How It Works
Groq is implemented as a built-in alias in docker-agent:
- **API Type:** OpenAI-compatible (`openai_chatcompletions`)
- **Base URL:** `https://api.groq.com/openai/v1`
- **Token Variable:** `GROQ_API_KEY`
## Example: Code Assistant
```yaml
agents:
coder:
model: groq/llama-3.3-70b-versatile
description: Code assistant using Llama 3.3
instruction: |
You are an expert programmer.
Write clean, well-documented code and follow language best practices.
toolsets:
- type: filesystem
- type: shell
- type: think
```

View File

@@ -0,0 +1,100 @@
---
title: "Hugging Face"
description: "Use Hugging Face Inference Providers with docker-agent."
keywords: docker agent, ai agents, model providers, llm, hugging face
weight: 140
---
_Use Hugging Face Inference Providers with docker-agent._
## Overview
[Hugging Face Inference Providers](https://huggingface.co/docs/inference-providers/index)
routes requests to open models (Llama, Qwen, DeepSeek, Kimi, GLM and others)
across many backends through a single OpenAI-compatible endpoint. docker-agent
includes built-in support for Hugging Face as an alias provider.
## Setup
1. Create a token from the [Hugging Face token settings](https://huggingface.co/settings/tokens).
2. Set the environment variable:
```bash
export HF_TOKEN=your-token
```
## Usage
### Inline Syntax
The simplest way to use Hugging Face:
```yaml
agents:
root:
model: huggingface/meta-llama/Llama-3.3-70B-Instruct
description: Assistant using Hugging Face Inference Providers
instruction: You are a helpful assistant.
```
### Named Model
For more control over parameters:
```yaml
models:
huggingface_model:
provider: huggingface
model: meta-llama/Llama-3.3-70B-Instruct
temperature: 0.7
max_tokens: 8192
agents:
root:
model: huggingface_model
description: Assistant using Hugging Face Inference Providers
instruction: You are a helpful assistant.
```
## Available Models
Hugging Face routes to a broad, changing catalog of open-weight models. Check the
[Hugging Face models page](https://huggingface.co/models?inference_provider=all)
for current model IDs, context limits, and pricing.
| Model | Description |
| --- | --- |
| `meta-llama/Llama-3.3-70B-Instruct` | Llama 3.3 70B, general-purpose chat and tool calling |
| `Qwen/Qwen3-235B-A22B` | Qwen3 235B mixture-of-experts instruct model |
| `deepseek-ai/DeepSeek-V3.2` | DeepSeek, strong coding and reasoning |
> Model IDs are case-sensitive and must be passed exactly as the catalogue lists
> them.
## How It Works
Hugging Face is implemented as a built-in alias in docker-agent:
- **API Type:** OpenAI-compatible (`openai_chatcompletions`)
- **Base URL:** `https://router.huggingface.co/v1`
- **Token Variable:** `HF_TOKEN`
Because Hugging Face fronts open-weight models whose chat templates may reject
more than one leading system message, docker-agent coalesces its per-source
system messages into a single one for this provider.
## Example: Code Assistant
```yaml
agents:
coder:
model: huggingface/Qwen/Qwen3-Coder-480B-A35B-Instruct
description: Code assistant using Qwen3 Coder on Hugging Face
instruction: |
You are an expert programmer.
Write clean, well-documented code and follow language best practices.
toolsets:
- type: filesystem
- type: shell
- type: think
```

View File

@@ -0,0 +1,221 @@
---
title: "Local Models (Ollama, vLLM, LocalAI)"
description: "Run docker-agent with locally hosted models for privacy, offline use, or cost savings."
keywords: docker agent, ai agents, model providers, llm, local models (ollama, vllm, localai)
linkTitle: "Local Models"
weight: 150
aliases:
- /ai/docker-agent/local-models/
---
_Run docker-agent with locally hosted models for privacy, offline use, or cost savings._
## Overview
docker-agent can connect to any OpenAI-compatible local model server. This guide covers the most popular options:
- **Ollama** — Easy-to-use local model runner
- **vLLM** — High-performance inference server
- **LocalAI** — OpenAI-compatible API for various backends
> [!TIP]
> **Docker Model Runner**
>
> For the easiest local model experience, consider [Docker Model Runner](../dmr/index.md) which is built into Docker Desktop and requires no additional setup.
## Ollama
Ollama is a popular tool for running LLMs locally. docker-agent includes a built-in `ollama` alias for easy configuration.
### Setup
1. Install Ollama from [ollama.ai](https://ollama.ai/)
2. Pull a model:
```bash
ollama pull llama3.2
ollama pull qwen2.5-coder
```
3. Start the Ollama server (usually runs automatically):
```bash
ollama serve
```
### Configuration
Use the built-in `ollama` alias:
```yaml
agents:
root:
model: ollama/llama3.2
description: Local assistant
instruction: You are a helpful assistant.
```
The `ollama` alias automatically uses:
- **Base URL:** `http://localhost:11434/v1`
- **API Type:** OpenAI-compatible
- **No API key required**
### Custom Port or Host
If Ollama runs on a different host or port:
```yaml
models:
my_ollama:
provider: ollama
model: llama3.2
base_url: http://192.168.1.100:11434/v1
agents:
root:
model: my_ollama
description: Remote Ollama assistant
instruction: You are a helpful assistant.
```
### Popular Ollama Models
| Model | Size | Best For |
| ---------------- | ---- | --------------------- |
| `llama3.2` | 3B | General purpose, fast |
| `llama3.1` | 8B | Better reasoning |
| `qwen2.5-coder` | 7B | Code generation |
| `mistral` | 7B | General purpose |
| `codellama` | 7B | Code tasks |
| `deepseek-coder` | 6.7B | Code generation |
## vLLM
vLLM is a high-performance inference server optimized for throughput.
### Setup
```bash
# Install vLLM
pip install vllm
# Start the server
python -m vllm.entrypoints.openai.api_server \
--model meta-llama/Llama-3.2-3B-Instruct \
--port 8000
```
### Configuration
```yaml
providers:
vllm:
api_type: openai_chatcompletions
base_url: http://localhost:8000/v1
agents:
root:
model: vllm/meta-llama/Llama-3.2-3B-Instruct
description: vLLM-powered assistant
instruction: You are a helpful assistant.
```
## LocalAI
LocalAI provides an OpenAI-compatible API that works with various backends.
### Setup
```bash
# Run with Docker
docker run -p 8080:8080 --name local-ai \
-v ./models:/models \
localai/localai:latest-cpu
```
### Configuration
```yaml
providers:
localai:
api_type: openai_chatcompletions
base_url: http://localhost:8080/v1
agents:
root:
model: localai/gpt4all-j
description: LocalAI assistant
instruction: You are a helpful assistant.
```
## Generic Custom Provider
For any OpenAI-compatible server:
```yaml
providers:
my_server:
api_type: openai_chatcompletions
base_url: http://localhost:8000/v1
# token_key: MY_API_KEY # if auth required
agents:
root:
model: my_server/model-name
description: Custom server assistant
instruction: You are a helpful assistant.
```
## Performance Tips
> [!NOTE]
> **Local Model Considerations**
>
> - **Memory:** Larger models need more RAM/VRAM. A 7B model typically needs 8-16GB RAM.
> - **GPU:** GPU acceleration dramatically improves speed. Check your server's GPU support.
> - **Context length:** Local models often have smaller context windows than cloud models.
> - **Tool calling:** Not all local models support function/tool calling. Test your model's capabilities.
## Example: Offline Development Agent
```yaml
agents:
developer:
model: ollama/qwen2.5-coder
description: Offline code assistant
instruction: |
You are a software developer working offline.
Focus on code quality and clear explanations.
max_iterations: 20
toolsets:
- type: filesystem
- type: shell
- type: think
- type: todo
```
## Troubleshooting
### Connection Refused
Ensure your model server is running and accessible:
```bash
curl http://localhost:11434/v1/models # Ollama
curl http://localhost:8000/v1/models # vLLM
```
### Model Not Found
Verify the model is downloaded/available:
```bash
ollama list # List available Ollama models
```
### Slow Responses
- Check if GPU acceleration is enabled
- Try a smaller model
- Reduce `max_tokens` in your config

View File

@@ -0,0 +1,91 @@
---
title: "MiniMax"
description: "Use MiniMax AI models with docker-agent."
keywords: docker agent, ai agents, model providers, llm, minimax
weight: 160
---
_Use MiniMax AI models with docker-agent._
## Overview
MiniMax provides AI models through an OpenAI-compatible API. docker-agent includes built-in support for MiniMax as an alias provider.
## Setup
1. Get an API key from [MiniMax](https://www.minimaxi.com/)
2. Set the environment variable:
```bash
export MINIMAX_API_KEY=your-api-key
```
## Usage
### Inline Syntax
The simplest way to use MiniMax:
```yaml
agents:
root:
model: minimax/MiniMax-M2.5
description: Assistant using MiniMax
instruction: You are a helpful assistant.
```
### Named Model
For more control over parameters:
```yaml
models:
minimax_model:
provider: minimax
model: MiniMax-M2.5
temperature: 0.7
max_tokens: 8192
agents:
root:
model: minimax_model
description: Assistant using MiniMax
instruction: You are a helpful assistant.
```
## Available Models
Check the [MiniMax documentation](https://www.minimaxi.com/document/introduction) for the current model catalog.
| Model | Description |
| ------------------------ | ----------------------------------------------- |
| `MiniMax-M2.5` | Peak performance, 204K context |
| `MiniMax-M2.5-highspeed` | Same as M2.5 but faster (~100 tps) |
| `MiniMax-M2.1` | Multi-language programming capabilities |
| `MiniMax-M2.1-highspeed` | Faster variant of M2.1 (~100 tps) |
| `MiniMax-M2` | Agentic capabilities, advanced reasoning |
## How It Works
MiniMax is implemented as a built-in alias in docker-agent:
- **API Type:** OpenAI-compatible (`openai`)
- **Base URL:** `https://api.minimax.io/v1`
- **Token Variable:** `MINIMAX_API_KEY`
## Example: Code Assistant
```yaml
agents:
coder:
model: minimax/MiniMax-M2.5
description: Code assistant using MiniMax
instruction: |
You are an expert programmer using MiniMax M2.5.
Write clean, well-documented code.
Follow best practices for the language being used.
toolsets:
- type: filesystem
- type: shell
- type: think
```

View File

@@ -0,0 +1,105 @@
---
title: "Mistral"
description: "Use Mistral AI models with docker-agent."
keywords: docker agent, ai agents, model providers, llm, mistral
weight: 170
---
_Use Mistral AI models with docker-agent._
## Overview
Mistral AI provides powerful language models through an OpenAI-compatible API. docker-agent includes built-in support for Mistral as an alias provider.
## Setup
1. Get an API key from [Mistral Console](https://console.mistral.ai/)
2. Set the environment variable:
```bash
export MISTRAL_API_KEY=your-api-key
```
## Usage
### Inline Syntax
The simplest way to use Mistral:
```yaml
agents:
root:
model: mistral/mistral-large-latest
description: Assistant using Mistral
instruction: You are a helpful assistant.
```
### Named Model
For more control over parameters:
```yaml
models:
mistral:
provider: mistral
model: mistral-large-latest
temperature: 0.7
max_tokens: 8192
agents:
root:
model: mistral
description: Assistant using Mistral
instruction: You are a helpful assistant.
```
## Available Models
| Model | Description | Context |
| ----------------------- | --------------------------------- | ------- |
| `mistral-large-latest` | Most capable Mistral model | 128K |
| `mistral-medium-latest` | Balanced performance and cost | 128K |
| `mistral-small-latest` | Fast and cost-effective (default) | 128K |
| `codestral-latest` | Optimized for code generation | 32K |
| `open-mistral-nemo` | Open-weight model | 128K |
| `ministral-8b-latest` | Compact 8B parameter model | 128K |
| `ministral-3b-latest` | Smallest Mistral model | 128K |
Check the [Mistral Models documentation](https://docs.mistral.ai/getting-started/models/) for the latest available models.
## Auto-Detection
When you run `docker agent run` without specifying a config, docker-agent automatically detects available providers. If `MISTRAL_API_KEY` is set and higher-priority providers (OpenAI, Anthropic, Google) are not available, Mistral will be used with `mistral-small-latest` as the default model.
## Extended Thinking
docker-agent's `thinking_budget` field is **not applied** to Mistral models: the underlying OpenAI-compatible client only sends `reasoning_effort` for OpenAI reasoning model names (o-series, gpt-5). Setting `thinking_budget` on a Mistral model passes config validation but has no effect on the request.
Mistral reasoning models (e.g. `magistral`) reason on their own without configuration. For non-reasoning models, use the [think tool](../../tools/think/index.md) instead.
## How It Works
Mistral is implemented as a built-in alias in docker-agent:
- **API Type:** OpenAI-compatible (`openai_chatcompletions`)
- **Base URL:** `https://api.mistral.ai/v1`
- **Token Variable:** `MISTRAL_API_KEY`
This means Mistral uses the same client as OpenAI, making it fully compatible with all OpenAI features supported by docker-agent.
## Example: Code Assistant
```yaml
agents:
coder:
model: mistral/codestral-latest
description: Expert code assistant
instruction: |
You are an expert programmer using Codestral.
Write clean, efficient, well-documented code.
Explain your reasoning when helpful.
toolsets:
- type: filesystem
- type: shell
- type: think
```

View File

@@ -0,0 +1,96 @@
---
title: "Moonshot AI"
description: "Use Moonshot AI (Kimi) models with docker-agent."
keywords: docker agent, ai agents, model providers, llm, moonshot ai
weight: 180
---
_Use Moonshot AI (Kimi) models with docker-agent._
## Overview
[Moonshot AI](https://www.moonshot.ai/) serves its Kimi model family through an
OpenAI-compatible API. The Kimi K2 models have strong momentum for coding and
agentic tasks. docker-agent includes built-in support for Moonshot AI as an
alias provider.
## Setup
1. Create an API key from the [Moonshot AI console](https://platform.moonshot.ai/console/api-keys).
2. Set the environment variable:
```bash
export MOONSHOT_API_KEY=your-api-key
```
## Usage
### Inline Syntax
The simplest way to use Moonshot AI:
```yaml
agents:
root:
model: moonshot/kimi-k2-0905-preview
description: Assistant using Moonshot AI
instruction: You are a helpful assistant.
```
### Named Model
For more control over parameters:
```yaml
models:
moonshot_model:
provider: moonshot
model: kimi-k2-0905-preview
temperature: 0.7
max_tokens: 8192
agents:
root:
model: moonshot_model
description: Assistant using Moonshot AI
instruction: You are a helpful assistant.
```
## Available Models
Moonshot AI exposes a vendor-controlled Kimi model lineup. Check the
[Moonshot API documentation](https://platform.moonshot.ai/docs/api) for current
model IDs, context limits, and pricing.
| Model | Description |
| --- | --- |
| `kimi-k2-0905-preview` | Kimi K2, general-purpose chat, coding, and tool calling |
| `kimi-k2-turbo-preview` | Kimi K2 optimized for higher throughput |
| `kimi-k2-thinking` | Kimi K2 extended-reasoning model |
> Model IDs are case-sensitive and must be passed exactly as the catalogue lists
> them.
## How It Works
Moonshot AI is implemented as a built-in alias in docker-agent:
- **API Type:** OpenAI-compatible (`openai_chatcompletions`)
- **Base URL:** `https://api.moonshot.ai/v1`
- **Token Variable:** `MOONSHOT_API_KEY`
## Example: Code Assistant
```yaml
agents:
coder:
model: moonshot/kimi-k2-0905-preview
description: Code assistant using Kimi K2
instruction: |
You are an expert programmer.
Write clean, well-documented code and follow language best practices.
toolsets:
- type: filesystem
- type: shell
- type: think
```

View File

@@ -0,0 +1,89 @@
---
title: "Nebius"
description: "Use Nebius AI models with docker-agent."
keywords: docker agent, ai agents, model providers, llm, nebius
weight: 190
---
_Use Nebius AI models with docker-agent._
## Overview
Nebius provides AI models through an OpenAI-compatible API. docker-agent includes built-in support for Nebius as an alias provider.
## Setup
1. Get an API key from [Nebius AI](https://nebius.ai/)
2. Set the environment variable:
```bash
export NEBIUS_API_KEY=your-api-key
```
## Usage
### Inline Syntax
The simplest way to use Nebius:
```yaml
agents:
root:
model: nebius/deepseek-ai/DeepSeek-V3
description: Assistant using Nebius
instruction: You are a helpful assistant.
```
### Named Model
For more control over parameters:
```yaml
models:
nebius_model:
provider: nebius
model: deepseek-ai/DeepSeek-V3
temperature: 0.7
max_tokens: 8192
agents:
root:
model: nebius_model
description: Assistant using Nebius
instruction: You are a helpful assistant.
```
## Available Models
Nebius hosts various open models. Check the [Nebius documentation](https://nebius.ai/docs) for the current model catalog.
| Model | Description |
| ----------------------------------- | ------------------------------- |
| `deepseek-ai/DeepSeek-V3` | DeepSeek V3 model |
| `Qwen/Qwen2.5-72B-Instruct` | Qwen 2.5 72B instruction-tuned |
| `meta-llama/Llama-3.3-70B-Instruct` | Llama 3.3 70B instruction-tuned |
## How It Works
Nebius is implemented as a built-in alias in docker-agent:
- **API Type:** OpenAI-compatible (`openai_chatcompletions`)
- **Base URL:** `https://api.studio.nebius.com/v1`
- **Token Variable:** `NEBIUS_API_KEY`
## Example: Code Assistant
```yaml
agents:
coder:
model: nebius/deepseek-ai/DeepSeek-V3
description: Code assistant using DeepSeek
instruction: |
You are an expert programmer using DeepSeek V3.
Write clean, well-documented code.
Follow best practices for the language being used.
toolsets:
- type: filesystem
- type: shell
- type: think
```

View File

@@ -0,0 +1,126 @@
---
title: "OpenAI"
description: "Use GPT-4o, GPT-5, GPT-5-mini, and other OpenAI models with docker-agent."
keywords: docker agent, ai agents, model providers, llm, openai
weight: 200
---
_Use GPT-4o, GPT-5, GPT-5-mini, and other OpenAI models with docker-agent._
## Setup
```bash
# Set your API key
export OPENAI_API_KEY="sk-..."
```
## Configuration
### Inline
```yaml
agents:
root:
model: openai/gpt-5
```
### Named Model
```yaml
models:
gpt:
provider: openai
model: gpt-5
temperature: 0.7
max_tokens: 4000
```
## Available Models
| Model | Best For |
| ------------- | ------------------------------------ |
| `gpt-5` | Most capable, complex reasoning |
| `gpt-5-mini` | Fast, cost-effective, good reasoning |
| `gpt-4o` | Multimodal, balanced performance |
| `gpt-4o-mini` | Cheapest, fast for simple tasks |
Find more model names at [modelnames.ai](https://modelnames.ai/) or in the [official OpenAI docs](https://platform.openai.com/docs/models).
## Thinking Budget
OpenAI reasoning models (o-series, gpt-5, gpt-5-mini) support extended thinking through the `reasoning_effort` API parameter. Set `thinking_budget` to control the effort level:
```yaml
models:
gpt-thinker:
provider: openai
model: gpt-5-mini
thinking_budget: high # minimal | low | medium | high | xhigh
```
**Effort levels:**
| Level | Description |
| --------- | -------------------------------------------------------- |
| `none` | Don't request extra reasoning (alias for `0`); the API's own default still applies. |
| `minimal` | Fastest; lightest reasoning pass. |
| `low` | Quick reasoning for straightforward tasks. |
| `medium` | Balanced default. |
| `high` | More thorough; recommended for complex tasks. |
| `xhigh` | Near-maximum effort; slower but most accurate. |
These are the **only** values OpenAI accepts — token counts, `max`, `adaptive`, and `adaptive/<effort>` are rejected with a configuration error at request time. Older models (o1, o3-mini) only accept `low`/`medium`/`high`.
> [!WARNING]
> **Hidden reasoning tokens**
>
> OpenAI reasoning models always produce hidden reasoning tokens that count against `max_tokens` — even with `thinking_budget: none`. docker-agent automatically raises the output-token floor for its internal low-effort calls so reasoning cannot starve visible text output.
See the [Thinking / Reasoning guide](../../guides/thinking/index.md) for a cross-provider overview.
> [!TIP]
> **Custom endpoints**
>
> Use `base_url` for proxies and OpenAI-compatible services. See [Custom Providers](../custom/index.md) for full setup.
## Custom Endpoint
Use `base_url` to connect to OpenAI-compatible APIs:
```yaml
models:
custom:
provider: openai
model: gpt-5-mini
base_url: https://your-proxy.example.com/v1
```
## WebSocket Transport
For OpenAI Responses API models (gpt-4.1+, o-series, gpt-5), you can use WebSocket streaming instead of the default SSE (Server-Sent Events):
```yaml
models:
fast-gpt:
provider: openai
model: gpt-4.1
provider_opts:
transport: websocket # Use WebSocket instead of SSE
```
### Benefits
- **~40% faster** for workflows with 20+ tool calls
- **Persistent connection** reduces per-turn overhead
- **Server-side caching** of connection state
- **Automatic fallback** to SSE if WebSocket fails
### Requirements
- Only works with Responses API models: `gpt-4.1+`, `o1`, `o3`, `o4`, `gpt-5`
- NOT compatible with the `--models-gateway` flag (automatically falls back to SSE when a gateway is configured)
- Requires `OPENAI_API_KEY` environment variable
### Example
See [`examples/websocket_transport.yaml`](https://github.com/docker/docker-agent/blob/main/examples/websocket_transport.yaml) for a complete example.

View File

@@ -0,0 +1,159 @@
---
title: "OpenCode Go"
description: "Use OpenCode Go models with docker-agent."
keywords: docker agent, ai agents, model providers, llm, opencode go
weight: 210
---
_Use OpenCode Go models with docker-agent._
## Overview
[OpenCode Go](https://opencode.ai/docs/go) is a low-cost subscription service ($5 first month, then $10/month) that provides reliable access to popular open-source coding models. It serves models through both OpenAI-compatible and Anthropic-compatible APIs from globally distributed endpoints.
docker-agent includes built-in support for OpenCode Go as an alias provider.
## Setup
1. Subscribe to OpenCode Go at [opencode.ai/auth](https://opencode.ai/auth)
2. Copy your API key from the console
3. Set the environment variable:
```bash
export OPENCODE_API_KEY=your-api-key
```
## Usage
### Inline Syntax
The simplest way to use OpenCode Go:
```yaml
agents:
root:
model: opencode-go/deepseek-v4-flash
description: Assistant using OpenCode Go
instruction: You are a helpful assistant.
```
### Named Model
For more control over parameters:
```yaml
models:
my_model:
provider: opencode-go
model: deepseek-v4-pro
temperature: 0.7
max_tokens: 8192
agents:
root:
model: my_model
description: Assistant using OpenCode Go
instruction: You are a helpful assistant.
```
## Available Models
You can retrieve the full, up-to-date model list at any time:
```bash
curl https://opencode.ai/zen/go/v1/models
```
### OpenAI-Compatible
These models use the `/v1/chat/completions` endpoint and work directly with the `opencode-go` alias:
| Model | Description |
| ------------------- | ------------------------------------- |
| `deepseek-v4-flash` | Fast and cost-effective DeepSeek model |
| `deepseek-v4-pro` | Most capable DeepSeek model |
| `kimi-k2.7-code` | Kimi K2.7 optimized for code |
| `kimi-k2.6` | Kimi K2.6 model |
| `kimi-k2.5` | Kimi K2.5 model |
| `glm-5.2` | GLM 5.2 flagship model |
| `glm-5.1` | GLM 5.1 model |
| `glm-5` | GLM 5 model |
| `mimo-v2.5` | MiMo V2.5 efficient model |
| `mimo-v2.5-pro` | MiMo V2.5 Pro model |
| `mimo-v2-pro` | MiMo V2 Pro model |
| `mimo-v2-omni` | MiMo V2 Omni model |
| `hy3-preview` | HY3 preview model |
### Anthropic-Compatible
These models use the `/v1/messages` endpoint and require a [custom provider definition](../custom/index.md):
| Model | Description |
| ----------------- | ------------------------ |
| `minimax-m3` | MiniMax M3 model |
| `minimax-m2.7` | MiniMax M2.7 model |
| `minimax-m2.5` | MiniMax M2.5 model |
| `qwen3.7-max` | Qwen 3.7 Max model |
| `qwen3.7-plus` | Qwen 3.7 Plus model |
| `qwen3.6-plus` | Qwen 3.6 Plus model |
| `qwen3.5-plus` | Qwen 3.5 Plus model |
To use an Anthropic-compatible model, define a custom provider:
```yaml
providers:
opengo-ant:
provider: anthropic
base_url: https://opencode.ai/zen/go
token_key: OPENCODE_API_KEY
models:
qwen:
provider: opengo-ant
model: qwen3.7-max
agents:
root:
model: qwen
description: Assistant using Qwen through OpenCode Go
instruction: You are a helpful assistant.
```
## How It Works
OpenCode Go is implemented as a built-in alias in docker-agent:
- **API Type:** OpenAI-compatible (`openai_chatcompletions`)
- **Base URL:** `https://opencode.ai/zen/go/v1`
- **Token Variable:** `OPENCODE_API_KEY`
This means OpenCode Go uses the same client as OpenAI, making it fully compatible with all OpenAI features supported by docker-agent.
For Anthropic-compatible models (MiniMax, Qwen), docker-agent uses a custom provider pointing to the Anthropic client at `https://opencode.ai/zen/go` with the same token.
## Example: Code Assistant
```yaml
agents:
coder:
model: opencode-go/deepseek-v4-flash
description: Expert code assistant
instruction: |
You are an expert programmer using DeepSeek V4 Flash.
Write clean, efficient, well-documented code.
Explain your reasoning when helpful.
toolsets:
- type: filesystem
- type: shell
- type: think
```
## Usage Limits
OpenCode Go subscriptions include the following limits:
- **5-hour rolling limit** — $12 of usage
- **Weekly limit** — $30 of usage
- **Monthly limit** — $60 of usage
Limits are defined as dollar values. More expensive models allow fewer requests per limit period. You can also [add Zen balance](https://opencode.ai/auth) to continue usage beyond the limits.

View File

@@ -0,0 +1,223 @@
---
title: "OpenCode Zen"
description: "Use OpenCode Zen models with docker-agent."
keywords: docker agent, ai agents, model providers, llm, opencode zen
weight: 220
---
_Use OpenCode Zen models with docker-agent._
## Overview
[OpenCode Zen](https://opencode.ai/docs/zen) is a curated gateway of tested and verified AI models provided by the OpenCode team. It offers pay-per-use access to a wide range of models — from GPT and Claude to open-source models — all through a single API key. Several free models are also available.
docker-agent includes built-in support for OpenCode Zen as an alias provider for OpenAI-compatible models. Anthropic and Google models are supported via custom provider definitions.
## Setup
1. Sign in to [OpenCode Zen](https://opencode.ai/auth), add billing information, and copy your API key
2. Set the environment variable:
```bash
export OPENCODE_API_KEY=your-api-key
```
3. Verify available models:
```bash
curl https://opencode.ai/zen/v1/models
```
## Usage
### Inline Syntax
The simplest way to use OpenCode Zen with a free model:
```yaml
agents:
root:
model: opencode-zen/deepseek-v4-flash-free
description: Assistant using OpenCode Zen (free)
instruction: You are a helpful assistant.
```
### Named Model
For more control over parameters:
```yaml
models:
zen_model:
provider: opencode-zen
model: gpt-5.5
temperature: 0.7
max_tokens: 16384
agents:
root:
model: zen_model
description: Assistant using OpenCode Zen
instruction: You are a helpful assistant.
```
## Available Models
### Free Models
These models are available at no cost:
| Model | Description |
| ------------------------- | ---------------------------------- |
| `deepseek-v4-flash-free` | Free DeepSeek V4 Flash |
| `mimo-v2.5-free` | Free MiMo V2.5 model |
| `qwen3.6-plus-free` | Free Qwen 3.6 Plus model |
| `minimax-m3-free` | Free MiniMax M3 model |
| `nemotron-3-ultra-free` | Free Nemotron 3 Ultra model |
| `north-mini-code-free` | Free North Mini Code model |
| `big-pickle` | Free stealth model |
### OpenAI-Compatible (Chat Completions)
These models use the `/v1/chat/completions` endpoint and work directly with the `opencode-zen` alias:
| Model | Description |
| --------------------- | ---------------------------------- |
| `deepseek-v4-pro` | DeepSeek V4 Pro model |
| `deepseek-v4-flash` | Fast DeepSeek V4 model |
| `glm-5.2` | GLM 5.2 model |
| `glm-5.1` | GLM 5.1 model |
| `glm-5` | GLM 5 model |
| `kimi-k2.6` | Kimi K2.6 model |
| `kimi-k2.5` | Kimi K2.5 model |
| `minimax-m2.7` | MiniMax M2.7 model |
| `minimax-m2.5` | MiniMax M2.5 model |
| `grok-build-0.1` | Grok Build 0.1 model |
### OpenAI-Compatible (Responses API)
These models use the `/v1/responses` endpoint and are auto-detected by docker-agent based on the model name:
| Model | Description |
| ---------------------- | --------------------------------- |
| `gpt-5.5` | Latest GPT model |
| `gpt-5.5-pro` | GPT 5.5 Pro, highest capability |
| `gpt-5.4` | GPT 5.4 model |
| `gpt-5.4-pro` | GPT 5.4 Pro model |
| `gpt-5.4-mini` | GPT 5.4 Mini model |
| `gpt-5.4-nano` | GPT 5.4 Nano, fastest |
| `gpt-5.3-codex` | GPT 5.3 Codex for coding |
| `gpt-5.3-codex-spark` | GPT 5.3 Codex Spark |
| `gpt-5.2` | GPT 5.2 model |
| `gpt-5.2-codex` | GPT 5.2 Codex |
| `gpt-5.1` | GPT 5.1 model |
| `gpt-5.1-codex` | GPT 5.1 Codex |
| `gpt-5.1-codex-max` | GPT 5.1 Codex Max |
| `gpt-5.1-codex-mini` | GPT 5.1 Codex Mini |
| `gpt-5` | GPT 5 model |
| `gpt-5-codex` | GPT 5 Codex |
| `gpt-5-nano` | GPT 5 Nano |
### Anthropic-Compatible (Messages API)
These models use the `/v1/messages` endpoint and require a [custom provider definition](../custom/index.md):
| Model | Description |
| ----------------------- | ----------------------------- |
| `claude-fable-5` | Claude Fable 5 model |
| `claude-opus-4-8` | Claude Opus 4.8 model |
| `claude-opus-4-7` | Claude Opus 4.7 model |
| `claude-opus-4-6` | Claude Opus 4.6 model |
| `claude-opus-4-5` | Claude Opus 4.5 model |
| `claude-opus-4-1` | Claude Opus 4.1 model |
| `claude-sonnet-4-6` | Claude Sonnet 4.6 model |
| `claude-sonnet-4-5` | Claude Sonnet 4.5 model |
| `claude-sonnet-4` | Claude Sonnet 4 model |
| `claude-haiku-4-5` | Claude Haiku 4.5 model |
| `qwen3.7-max` | Qwen 3.7 Max model |
| `qwen3.7-plus` | Qwen 3.7 Plus model |
| `qwen3.6-plus` | Qwen 3.6 Plus model |
| `qwen3.5-plus` | Qwen 3.5 Plus model |
To use an Anthropic-compatible model:
```yaml
providers:
opencode-zen-claude:
provider: anthropic
base_url: https://opencode.ai/zen
token_key: OPENCODE_API_KEY
models:
claude:
provider: opencode-zen-claude
model: claude-sonnet-4-5
agents:
root:
model: claude
description: Assistant using Claude through OpenCode Zen
instruction: You are a helpful assistant.
```
### Google-Compatible
These models require a [custom provider definition](../custom/index.md) with a Google-compatible client:
| Model | Description |
| ------------------- | ------------------------ |
| `gemini-3.5-flash` | Gemini 3.5 Flash model |
| `gemini-3.1-pro` | Gemini 3.1 Pro model |
| `gemini-3-flash` | Gemini 3 Flash model |
To use a Google model:
```yaml
providers:
opencode-zen-gemini:
provider: google
base_url: https://opencode.ai/zen
token_key: OPENCODE_API_KEY
models:
gemini:
provider: opencode-zen-gemini
model: gemini-3.5-flash
agents:
root:
model: gemini
description: Assistant using Gemini through OpenCode Zen
instruction: You are a helpful assistant.
```
## How It Works
OpenCode Zen is implemented as a built-in alias in docker-agent:
- **API Type:** OpenAI-compatible (auto-detects Responses API for GPT models, Chat Completions for others)
- **Base URL:** `https://opencode.ai/zen/v1`
- **Token Variable:** `OPENCODE_API_KEY`
The same API key works for both OpenCode Go and OpenCode Zen — they are part of the same platform. Zen uses a pay-per-use billing model, while Go uses a fixed subscription.
For Anthropic-compatible models, docker-agent uses a custom provider pointing to the Anthropic client at `https://opencode.ai/zen` with the same token. For Google models, a custom provider points to the Google client at `https://opencode.ai/zen` (the Google SDK appends its own `/v1beta/models/...` path segment).
### Differences from OpenCode Go
| Aspect | OpenCode Zen | OpenCode Go |
|--------|-------------|-------------|
| Billing | Pay-per-use | $10/month subscription |
| Models | GPT-5.x, Claude, Gemini, open-source | Open-source only |
| Free models | Yes (7 models) | No |
| Base URL | `https://opencode.ai/zen/v1` | `https://opencode.ai/zen/go/v1` |
## Usage Limits and Pricing
OpenCode Zen uses a pay-per-use model. See the [OpenCode Zen documentation](https://opencode.ai/docs/zen) for current pricing. Automatic top-up and monthly usage limits are available from the console.
You can retrieve the full model catalog at any time:
```bash
curl https://opencode.ai/zen/v1/models
```

View File

@@ -0,0 +1,87 @@
---
title: "OpenRouter"
description: "Use OpenRouter models with docker-agent."
keywords: docker agent, ai agents, model providers, llm, openrouter
weight: 230
---
_Use OpenRouter models with docker-agent._
## Overview
OpenRouter provides access to models from many providers through an OpenAI-compatible API. docker-agent includes built-in support for OpenRouter as an alias provider.
## Setup
1. Get an API key from [OpenRouter](https://openrouter.ai/settings/keys)
2. Set the environment variable:
```bash
export OPENROUTER_API_KEY=your-api-key
```
## Usage
### Inline Syntax
The simplest way to use OpenRouter:
```yaml
agents:
root:
model: openrouter/meta-llama/llama-3.3-70b-instruct
description: Assistant using OpenRouter
instruction: You are a helpful assistant.
```
OpenRouter model IDs usually include the upstream provider name, such as `anthropic/claude-sonnet-4-5` or `meta-llama/llama-3.3-70b-instruct`. docker-agent splits only the first slash, so the full upstream model ID is preserved.
### Named Model
For more control over parameters:
```yaml
models:
openrouter_llama:
provider: openrouter
model: meta-llama/llama-3.3-70b-instruct
temperature: 0.7
max_tokens: 8192
agents:
root:
model: openrouter_llama
description: Assistant using OpenRouter
instruction: You are a helpful assistant.
```
## Pricing and Model Metadata
docker-agent fetches OpenRouter model metadata from [models.dev](https://models.dev/), including pricing per 1M input/output tokens, cache pricing when available, context limits, output limits, and modalities. This powers cost tracking and the model picker in the same way as other first-class providers.
If models.dev is unavailable, docker-agent falls back to its embedded catalog snapshot.
## How It Works
OpenRouter is implemented as a built-in alias in docker-agent:
- **API Type:** OpenAI-compatible (`openai`)
- **Base URL:** `https://openrouter.ai/api/v1`
- **Token Variable:** `OPENROUTER_API_KEY`
## Example: Code Assistant
```yaml
agents:
coder:
model: openrouter/meta-llama/llama-3.3-70b-instruct
description: Code assistant using OpenRouter
instruction: |
You are an expert programmer.
Write clean, maintainable code.
Explain trade-offs when helpful.
toolsets:
- type: filesystem
- type: shell
- type: think
```

View File

@@ -0,0 +1,99 @@
---
title: "Model Providers"
description: "docker-agent supports multiple AI model providers. Choose the right one for your use case, or use multiple providers in the same configuration."
keywords: docker agent, ai agents, model providers, llm
linkTitle: "Overview"
weight: 10
aliases:
- /ai/docker-agent/model-providers/
---
_docker-agent supports multiple AI model providers. Choose the right one for your use case, or use multiple providers in the same configuration._
## Supported Providers
- [**OpenAI**](../openai/index.md) — GPT-5, GPT-5-mini, GPT-4o. The most widely used AI models.
- [**Anthropic**](../anthropic/index.md) — Claude Sonnet 4.5, Claude Opus 4.7. Excellent for coding and analysis.
- [**Google Gemini**](../google/index.md) — Gemini 2.5 Flash, Gemini 3 Pro. Fast and cost-effective.
- [**AWS Bedrock**](../bedrock/index.md) — access Claude, Nova, Llama, and more through AWS infrastructure.
- [**Docker Model Runner**](../dmr/index.md) — run models locally with Docker. No API keys, no costs.
- [**Provider Definitions**](../custom/index.md) — define reusable provider configurations with shared defaults for any provider type.
## Quick Comparison
| Provider | Key | Local? | Strengths |
| ------------------- | ---------------- | ------ | ----------------------------------------------------- |
| OpenAI | `openai` | No | Broad model selection, tool calling, multimodal |
| Anthropic | `anthropic` | No | Strong coding, extended thinking, large context |
| Google | `google` | No | Fast inference, competitive pricing, multimodal |
| AWS Bedrock | `amazon-bedrock` | No | Enterprise features, multiple models, AWS integration |
| Docker Model Runner | `dmr` | Yes | No API costs, data privacy, offline capable |
## Additional Built-in Providers
docker-agent also includes built-in aliases for these providers:
| Provider | Alias | API Key / Env Variable |
| -------------- | ---------------- | ----------------------------------- |
| OpenCode Zen | `opencode-zen` | `OPENCODE_API_KEY` |
| OpenCode Go | `opencode-go` | `OPENCODE_API_KEY` |
| Mistral | `mistral` | `MISTRAL_API_KEY` |
| xAI (Grok) | `xai` | `XAI_API_KEY` |
| Nebius | `nebius` | `NEBIUS_API_KEY` |
| MiniMax | `minimax` | `MINIMAX_API_KEY` |
| Baseten | `baseten` | `BASETEN_API_KEY` |
| OVHcloud | `ovhcloud` | `OVH_AI_ENDPOINTS_ACCESS_TOKEN` |
| Groq | `groq` | `GROQ_API_KEY` |
| Fireworks AI | `fireworks` | `FIREWORKS_API_KEY` |
| DeepSeek | `deepseek` | `DEEPSEEK_API_KEY` |
| Cerebras | `cerebras` | `CEREBRAS_API_KEY` |
| Together AI | `together` | `TOGETHER_API_KEY` |
| Hugging Face | `huggingface` | `HF_TOKEN` |
| Cloudflare Workers AI | `cloudflare-workers-ai` | `CLOUDFLARE_API_TOKEN` + `CLOUDFLARE_ACCOUNT_ID` |
| Moonshot AI | `moonshot` | `MOONSHOT_API_KEY` |
| Vercel AI Gateway | `vercel` | `AI_GATEWAY_API_KEY` |
| Cloudflare AI Gateway | `cloudflare-ai-gateway` | `CLOUDFLARE_API_TOKEN` + `CLOUDFLARE_ACCOUNT_ID` + `CLOUDFLARE_GATEWAY_ID` |
| Requesty | `requesty` | `REQUESTY_API_KEY` |
| OpenRouter | `openrouter` | `OPENROUTER_API_KEY` |
| Azure OpenAI | `azure` | `AZURE_API_KEY` + `base_url` |
| Ollama | `ollama` | None (local; optional `base_url`) |
| GitHub Copilot | `github-copilot` | `GITHUB_TOKEN` (PAT with `copilot` scope) |
```bash
# Use built-in providers inline
agents:
root:
model: mistral/mistral-large-latest
```
> [!TIP]
> **Multi-provider teams**
>
> Use expensive models for complex reasoning and cheaper/local models for routine tasks. See the example below.
## Using Multiple Providers
Different agents can use different providers in the same configuration:
```yaml
models:
claude:
provider: anthropic
model: claude-sonnet-4-5
max_tokens: 64000
gpt:
provider: openai
model: gpt-5
local:
provider: dmr
model: ai/qwen3
agents:
root:
model: claude # coordinator uses Claude
sub_agents: [coder, helper]
coder:
model: gpt # coder uses GPT-5
helper:
model: local # helper runs locally for free
```

View File

@@ -0,0 +1,107 @@
---
title: "OVHcloud"
description: "Use OVHcloud AI Endpoints models with docker-agent."
keywords: docker agent, ai agents, model providers, llm, ovhcloud
weight: 240
---
_Use OVHcloud AI Endpoints models with docker-agent._
## Overview
[OVHcloud AI Endpoints](https://endpoints.ai.cloud.ovh.net/) serves open-weight
models through an OpenAI-compatible API, hosted in the EU. docker-agent includes
built-in support for OVHcloud as an alias provider.
## Setup
1. Create an access token from the
[OVHcloud AI Endpoints portal](https://endpoints.ai.cloud.ovh.net/).
2. Set the environment variable:
```bash
export OVH_AI_ENDPOINTS_ACCESS_TOKEN=your-access-token
```
## Usage
### Inline Syntax
```yaml
agents:
root:
model: ovhcloud/Qwen3.5-397B-A17B
description: Assistant using OVHcloud
instruction: You are a helpful assistant.
```
### Named Model
```yaml
models:
ovhcloud_model:
provider: ovhcloud
model: Qwen3.5-397B-A17B
temperature: 0.7
max_tokens: 8192
agents:
root:
model: ovhcloud_model
description: Assistant using OVHcloud
instruction: You are a helpful assistant.
```
## Available Models
OVHcloud hosts a rotating catalogue of open-weight models. Check the
[AI Endpoints catalogue](https://endpoints.ai.cloud.ovh.net/) for current model
IDs, context limits, and free-tier availability.
| Model | Description |
| --- | --- |
| `Qwen3.5-397B-A17B` | Large Qwen3.5 MoE — strong general, coding, and reasoning |
| `Qwen3-32B` | Mid-size Qwen3 — fast, tool-calling, reasoning |
| `Qwen3.6-27B` | Compact Qwen3.6 — fast and efficient |
| `Qwen3.5-9B` | Small Qwen3.5 — lightweight, free-tier friendly |
| `Qwen3-Coder-30B-A3B-Instruct` | Qwen3 Coder MoE — optimised for code generation |
| `Meta-Llama-3_3-70B-Instruct` | Llama 3.3 70B — reliable general-purpose chat |
| `Mistral-Small-3.2-24B-Instruct-2506` | Compact, fast, tool-calling |
> Model IDs are case-sensitive and must be passed exactly as the catalogue lists
> them.
## How It Works
OVHcloud is implemented as a built-in alias in docker-agent:
- **API Type:** OpenAI-compatible (`openai_chatcompletions`)
- **Base URL:** `https://oai.endpoints.kepler.ai.cloud.ovh.net/v1`
- **Token Variable:** `OVH_AI_ENDPOINTS_ACCESS_TOKEN`
docker-agent automatically coalesces consecutive system messages into one for
OVHcloud, because some OVHcloud models return an empty stream when a request
carries more than one system message.
## Free tier
OVHcloud offers rate-limited free access to several models. Under heavy
rate-limiting the endpoint may return an empty response; docker-agent surfaces
this as a warning rather than failing. For sustained use, an access token with a
paid plan avoids the free-tier request-rate cap.
## Example: Code Assistant
```yaml
agents:
coder:
model: ovhcloud/Qwen3.5-397B-A17B
description: Code assistant using Qwen3.5
instruction: |
You are an expert programmer.
Write clean, well-documented code and follow language best practices.
toolsets:
- type: filesystem
- type: shell
- type: think
```

View File

@@ -0,0 +1,100 @@
---
title: "Together AI"
description: "Use Together AI models with docker-agent."
keywords: docker agent, ai agents, model providers, llm, together ai
weight: 250
---
_Use Together AI models with docker-agent._
## Overview
[Together AI](https://www.together.ai/) is one of the largest hosts of open
models, serving Llama, Qwen, DeepSeek, Kimi, GLM and others through an
OpenAI-compatible API. docker-agent includes built-in support for Together AI as
an alias provider.
## Setup
1. Create an API key from the [Together AI settings](https://api.together.ai/settings/api-keys).
2. Set the environment variable:
```bash
export TOGETHER_API_KEY=your-api-key
```
## Usage
### Inline Syntax
The simplest way to use Together AI:
```yaml
agents:
root:
model: together/meta-llama/Llama-3.3-70B-Instruct-Turbo
description: Assistant using Together AI
instruction: You are a helpful assistant.
```
### Named Model
For more control over parameters:
```yaml
models:
together_model:
provider: together
model: meta-llama/Llama-3.3-70B-Instruct-Turbo
temperature: 0.7
max_tokens: 8192
agents:
root:
model: together_model
description: Assistant using Together AI
instruction: You are a helpful assistant.
```
## Available Models
Together AI serves a broad, changing catalog of open-weight models. Check the
[Together AI model library](https://docs.together.ai/docs/serverless-models) for
current model IDs, context limits, and pricing.
| Model | Description |
| --- | --- |
| `meta-llama/Llama-3.3-70B-Instruct-Turbo` | Llama 3.3 70B, general-purpose chat and tool calling |
| `Qwen/Qwen3-235B-A22B-Instruct-2507-tput` | Qwen3 235B mixture-of-experts instruct model |
| `deepseek-ai/DeepSeek-V3` | DeepSeek-V3, strong coding and reasoning |
> Model IDs are case-sensitive and must be passed exactly as the catalogue lists
> them.
## How It Works
Together AI is implemented as a built-in alias in docker-agent:
- **API Type:** OpenAI-compatible (`openai_chatcompletions`)
- **Base URL:** `https://api.together.xyz/v1`
- **Token Variable:** `TOGETHER_API_KEY`
Because Together AI fronts open-weight models whose chat templates may reject
more than one leading system message, docker-agent coalesces its per-source
system messages into a single one for this provider.
## Example: Code Assistant
```yaml
agents:
coder:
model: together/Qwen/Qwen3-235B-A22B-Instruct-2507-tput
description: Code assistant using Qwen3 on Together AI
instruction: |
You are an expert programmer.
Write clean, well-documented code and follow language best practices.
toolsets:
- type: filesystem
- type: shell
- type: think
```

View File

@@ -0,0 +1,105 @@
---
title: "Vercel AI Gateway"
description: "Use Vercel AI Gateway models with docker-agent."
keywords: docker agent, ai agents, model providers, llm, vercel ai gateway
weight: 260
---
_Use Vercel AI Gateway models with docker-agent._
## Overview
[Vercel AI Gateway](https://vercel.com/docs/ai-gateway) is a single, unified
OpenAI-compatible endpoint that routes to models from OpenAI, Anthropic, Google,
xAI and more at list price with no markup, plus provider routing and failover.
It lets you reach many providers with one API key. docker-agent includes
built-in support for Vercel AI Gateway as an alias provider.
## Setup
1. Create an API key from the [Vercel AI Gateway dashboard](https://vercel.com/docs/ai-gateway).
2. Set the environment variable:
```bash
export AI_GATEWAY_API_KEY=your-api-key
```
## Usage
Vercel AI Gateway model IDs use a `creator/model` form (for example
`openai/gpt-5` or `anthropic/claude-sonnet-4.5`); the gateway routes each
request to the underlying provider.
### Inline Syntax
The simplest way to use Vercel AI Gateway:
```yaml
agents:
root:
model: vercel/openai/gpt-5
description: Assistant using Vercel AI Gateway
instruction: You are a helpful assistant.
```
### Named Model
For more control over parameters:
```yaml
models:
vercel_model:
provider: vercel
model: openai/gpt-5
temperature: 0.7
max_tokens: 8192
agents:
root:
model: vercel_model
description: Assistant using Vercel AI Gateway
instruction: You are a helpful assistant.
```
## Available Models
Vercel AI Gateway exposes models from many providers behind one endpoint. Check
the [Vercel AI Gateway documentation](https://vercel.com/docs/ai-gateway) for
the current model list, IDs, and pricing.
| Model | Description |
| --- | --- |
| `openai/gpt-5` | OpenAI GPT-5 routed through the gateway |
| `anthropic/claude-sonnet-4.5` | Anthropic Claude Sonnet routed through the gateway |
| `google/gemini-2.5-flash` | Google Gemini routed through the gateway |
> Model IDs are case-sensitive and must be passed exactly as the gateway lists
> them, including the `creator/` prefix.
## How It Works
Vercel AI Gateway is implemented as a built-in alias in docker-agent:
- **API Type:** OpenAI-compatible (`openai_chatcompletions`)
- **Base URL:** `https://ai-gateway.vercel.sh/v1`
- **Token Variable:** `AI_GATEWAY_API_KEY`
Because the gateway can route to open-weight models with strict chat templates,
docker-agent coalesces consecutive system messages into a single leading one for
this provider.
## Example: Code Assistant
```yaml
agents:
coder:
model: vercel/anthropic/claude-sonnet-4.5
description: Code assistant via Vercel AI Gateway
instruction: |
You are an expert programmer.
Write clean, well-documented code and follow language best practices.
toolsets:
- type: filesystem
- type: shell
- type: think
```

View File

@@ -0,0 +1,98 @@
---
title: "xAI (Grok)"
description: "Use xAI's Grok models with docker-agent."
keywords: docker agent, ai agents, model providers, llm, xai (grok)
weight: 270
---
_Use xAI's Grok models with docker-agent._
## Overview
xAI provides the Grok family of models through an OpenAI-compatible API. docker-agent includes built-in support for xAI as an alias provider.
## Setup
1. Get an API key from [xAI Console](https://console.x.ai/)
2. Set the environment variable:
```bash
export XAI_API_KEY=your-api-key
```
## Usage
### Inline Syntax
The simplest way to use xAI:
```yaml
agents:
root:
model: xai/grok-3
description: Assistant using Grok
instruction: You are a helpful assistant.
```
### Named Model
For more control over parameters:
```yaml
models:
grok:
provider: xai
model: grok-3
temperature: 0.7
max_tokens: 8192
agents:
root:
model: grok
description: Assistant using Grok
instruction: You are a helpful assistant.
```
## Available Models
| Model | Description | Context |
| ------------------ | ---------------------------------- | ------- |
| `grok-3` | Latest and most capable Grok model | 131K |
| `grok-3-fast` | Faster variant with lower latency | 131K |
| `grok-3-mini` | Compact model for simpler tasks | 131K |
| `grok-3-mini-fast` | Fast variant of the mini model | 131K |
| `grok-2` | Previous generation model | 128K |
| `grok-vision` | Vision-capable model | 32K |
Check the [xAI documentation](https://docs.x.ai/docs) for the latest available models.
## Extended Thinking
docker-agent's `thinking_budget` field is **not applied** to xAI models: the underlying OpenAI-compatible client only sends `reasoning_effort` for OpenAI reasoning model names (o-series, gpt-5). Setting `thinking_budget` on a Grok model passes config validation but has no effect on the request.
Grok reasoning models (e.g. `grok-3-mini`) reason on their own without configuration. For non-reasoning models, use the [think tool](../../tools/think/index.md) instead.
## How It Works
xAI is implemented as a built-in alias in docker-agent:
- **API Type:** OpenAI-compatible (`openai_chatcompletions`)
- **Base URL:** `https://api.x.ai/v1`
- **Token Variable:** `XAI_API_KEY`
## Example: Research Assistant
```yaml
agents:
researcher:
model: xai/grok-3
description: Research assistant with real-time knowledge
instruction: |
You are a research assistant using Grok.
Provide well-researched, factual responses.
Cite sources when available.
toolsets:
- type: mcp
ref: docker:duckduckgo
- type: think
```

View File

@@ -0,0 +1,5 @@
---
title: "Built-in Tools"
description: "Built-in toolsets agents can use out of the box."
weight: 40
---

View File

@@ -0,0 +1,40 @@
---
title: "A2A Tool"
description: "Connect to remote agents via the Agent-to-Agent protocol."
keywords: docker agent, ai agents, tools, toolsets, a2a tool
linkTitle: "A2A"
weight: 60
---
_Connect to remote agents via the Agent-to-Agent protocol._
## Overview
The A2A tool connects to a remote agent exposed over the A2A (Agent-to-Agent) protocol. Unlike [`handoff`](../handoff/index.md), which only targets local agents declared in the same config, `a2a` reaches out to an agent running on the network.
## Configuration
```yaml
toolsets:
- type: a2a
url: "http://localhost:8080/a2a"
# Optional: custom tool name (defaults to a sanitized form of the URL / agent card name)
name: research_agent
# Optional: custom HTTP headers (typically for auth)
headers:
Authorization: "Bearer ${env.A2A_TOKEN}"
X-Tenant: "acme"
```
## Properties
| Property | Type | Required | Description |
| ---------- | ---------------- | -------- | -------------------------------------------------------------------------------------------------------- |
| `url` | string | ✓ | A2A server endpoint URL (must include scheme). |
| `name` | string | ✗ | Tool name registered for the remote agent. Defaults to a name derived from the server's agent card. |
| `headers` | map\[string\]string | ✗ | Extra HTTP headers sent with every request (useful for `Authorization`, tenant selection, tracing, \u2026). |
> [!TIP]
> **See also**
>
> For full details on the A2A protocol and serving agents as A2A endpoints, see [A2A Protocol](../../features/a2a/index.md).

View File

@@ -0,0 +1,251 @@
---
title: "API Tool"
description: "Create custom tools that call HTTP APIs."
keywords: docker agent, ai agents, tools, toolsets, api tool
linkTitle: "API"
weight: 240
---
_Create custom tools that call HTTP APIs._
## Overview
The API tool type lets you define custom tools that make HTTP requests to external APIs. This is useful for integrating agents with REST APIs, webhooks, or any HTTP-based service without writing code.
> [!NOTE]
> **When to Use**
>
> - Integrating with REST APIs that don't have an MCP server
> - Simple HTTP operations (GET, POST)
> - Quick prototyping before building a full MCP server
## Configuration
```yaml
agents:
assistant:
model: openai/gpt-4o
description: Assistant with API access
instruction: You can look up weather information.
toolsets:
- type: api
api_config:
name: get_weather
method: GET
endpoint: "https://api.weather.example/v1/current?city=${city}"
instruction: Get current weather for a city
args:
city:
type: string
description: City name to get weather for
required: ["city"]
headers:
Authorization: "Bearer ${env.WEATHER_API_KEY}"
```
## Properties
The `api` toolset accepts the following toolset-level fields in addition to the `api_config` block:
| Property | Type | Required | Description |
| ------------------- | ------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `api_config` | object | ✓ | The HTTP tool definition. See the table below. |
| `timeout` | int | ✗ | HTTP client timeout in seconds (default: `30`). Applies to every call the generated tool makes. |
| `allow_private_ips` | boolean | ✗ | Opt in to dialling **non-public** IP addresses (loopback, RFC1918, link-local — including the cloud-metadata endpoint at `169.254.169.254` — multicast and the unspecified address). Set to `true` only when the configured endpoint legitimately targets internal services. See [Reaching internal services](#reaching-internal-services). |
### `api_config`
| Property | Type | Required | Description |
| --------------- | ------ | -------- | ------------------------------------------------ |
| `name` | string | ✓ | Tool name (how the agent references it) |
| `method` | string | ✓ | HTTP method: `GET` or `POST` |
| `endpoint` | string | ✓ | URL endpoint (supports `${param}` interpolation) |
| `instruction` | string | ✗ | Description shown to the agent |
| `args` | object | ✗ | Parameter definitions (JSON Schema properties) |
| `required` | array | ✗ | List of required parameter names |
| `headers` | object | ✗ | HTTP headers to include. Values support `${env.VAR}` and `${headers.NAME}` placeholders (the latter forwards a header from the caller's incoming request, useful when docker agent is itself exposed as an HTTP server). |
| `output_schema` | object | ✗ | JSON Schema for the response. Used by MCP / Code Mode consumers; tool responses are still returned to the model as raw strings. |
## HTTP Methods
### GET Requests
For GET requests, parameters are interpolated into the URL:
```yaml
toolsets:
- type: api
api_config:
name: search_users
method: GET
endpoint: "https://api.example.com/users?q=${query}&limit=${limit}"
instruction: Search for users by name
args:
query:
type: string
description: Search query
limit:
type: integer
description: Maximum results (default 10)
required: ["query"]
```
### POST Requests
For POST requests, parameters are sent as JSON in the request body:
```yaml
toolsets:
- type: api
api_config:
name: create_task
method: POST
endpoint: "https://api.example.com/tasks"
instruction: Create a new task
args:
title:
type: string
description: Task title
description:
type: string
description: Task description
priority:
type: string
enum: ["low", "medium", "high"]
description: Task priority
required: ["title"]
headers:
Content-Type: "application/json"
Authorization: "Bearer ${env.API_TOKEN}"
```
## URL Interpolation
Use `${param}` syntax to insert parameter values into URLs:
```yaml
endpoint: "https://api.example.com/users/${user_id}/posts/${post_id}"
```
Parameter values are inserted as strings by the template expansion. Add URL encoding in the template when needed (for example, `${encodeURIComponent(city)}`).
## Headers
Headers can include environment variables:
```yaml
headers:
Authorization: "Bearer ${env.API_KEY}"
X-Custom-Header: "static-value"
Content-Type: "application/json"
```
## Output Schema
Optionally document the expected response format:
```yaml
toolsets:
- type: api
api_config:
name: get_user
method: GET
endpoint: "https://api.example.com/users/${id}"
instruction: Get user details by ID
args:
id:
type: string
description: User ID
required: ["id"]
output_schema:
type: object
properties:
id:
type: string
name:
type: string
email:
type: string
created_at:
type: string
```
## Example: GitHub API
```yaml
agents:
github_assistant:
model: openai/gpt-4o
description: Assistant that can query GitHub
instruction: You can look up GitHub repositories and users.
toolsets:
- type: api
api_config:
name: get_repo
method: GET
endpoint: "https://api.github.com/repos/${owner}/${repo}"
instruction: Get information about a GitHub repository
args:
owner:
type: string
description: Repository owner (user or org)
repo:
type: string
description: Repository name
required: ["owner", "repo"]
headers:
Accept: "application/vnd.github.v3+json"
Authorization: "Bearer ${env.GITHUB_TOKEN}"
- type: api
api_config:
name: get_user
method: GET
endpoint: "https://api.github.com/users/${username}"
instruction: Get information about a GitHub user
args:
username:
type: string
description: GitHub username
required: ["username"]
headers:
Accept: "application/vnd.github.v3+json"
```
## Limitations
- Only supports GET and POST methods
- Response body is limited to 1MB
- Default 30-second timeout per request (override with the `timeout` field)
- Only HTTP and HTTPS URLs are supported
- No support for file uploads or multipart forms
- By default, requests to non-public IP ranges (loopback, RFC1918, link-local, the cloud-metadata endpoint, multicast, the unspecified address) are refused at dial time — even when DNS for an otherwise-public host resolves there. Set `allow_private_ips: true` to disable that check.
## Reaching internal services
```yaml
toolsets:
- type: api
timeout: 60
allow_private_ips: true
api_config:
name: get_local_status
method: GET
endpoint: "http://localhost:8080/health"
instruction: Check the local service health
```
> [!WARNING]
> **SSRF**
>
> Setting `allow_private_ips: true` re-exposes the SSRF surface for this tool. Only enable it when the configured `endpoint` is a trusted internal service — a prompt-injected agent cannot redirect the call elsewhere because the endpoint is fixed in config, but redirects from the configured host can still reach unexpected places.
> [!TIP]
> **For Complex APIs**
>
> For APIs that need authentication flows, pagination, or complex request/response handling, consider using an MCP server instead. The API tool is best for simple, stateless HTTP operations.
> [!WARNING]
> **Security**
>
> API keys and tokens in headers are visible in debug logs. Use environment variables (`${env.VAR}`) rather than hardcoding secrets in configuration files.

View File

@@ -0,0 +1,115 @@
---
title: "Background Agents Tool"
description: "Dispatch work to sub-agents concurrently and collect results asynchronously."
keywords: docker agent, ai agents, tools, toolsets, background agents tool
linkTitle: "Background Agents"
weight: 90
---
_Dispatch work to sub-agents concurrently and collect results asynchronously._
## Overview
The background agents tool lets an orchestrator dispatch work to sub-agents concurrently and collect results asynchronously. Unlike [transfer_task](../transfer-task/index.md) (which blocks until the sub-agent finishes), background agent tasks run in parallel — the orchestrator can start several tasks, do other work, and check on them later.
## Available Tools
| Tool | Description |
| ------------------------ | --------------------------------------------------------------- |
| `run_background_agent` | Start a sub-agent task in the background; returns a task ID |
| `list_background_agents` | List all background tasks with their status and runtime |
| `view_background_agent` | View live output or final result of a task by ID |
| `stop_background_agent` | Cancel a running task by ID |
### `run_background_agent` parameters
| Parameter | Type | Required | Description |
| ----------------- | ------ | -------- | --------------------------------------------------------------------------- |
| `agent` | string | ✓ | Name of the sub-agent to run. Must be listed under the caller's `sub_agents`. |
| `task` | string | ✓ | Clear, concise description of the task the sub-agent should achieve. |
| `expected_output` | string | ✗ | Optional description of the result format the caller expects. |
`run_background_agent` returns a **task ID** string. Tools run by the sub-agent are pre-approved, so only dispatch to trusted sub-agents with well-scoped tasks.
### `view_background_agent` and `stop_background_agent` parameters
| Parameter | Type | Required | Description |
| --------- | ------ | -------- | -------------------------------------------------------------- |
| `task_id` | string | ✓ | Task ID returned by `run_background_agent` or `list_background_agents`. |
`list_background_agents` takes no parameters.
## Configuration
```yaml
toolsets:
- type: background_agents
```
No configuration options. Requires the agent to have `sub_agents` configured so the background tasks have agents to dispatch to.
## Example
```yaml
agents:
coordinator:
model: openai/gpt-4o
description: Orchestrates parallel research
instruction: Fan out research tasks and synthesize results.
sub_agents: [researcher]
toolsets:
- type: background_agents
- type: think
researcher:
model: openai/gpt-4o
description: Web researcher
instruction: Research topics thoroughly.
toolsets:
- type: mcp
ref: docker:duckduckgo
```
> [!TIP]
> **When to Use**
>
> Use `background_agents` when your orchestrator needs to fan out work to multiple specialists in parallel — for example, researching several topics simultaneously or running independent code analyses side by side.
## Using Harness Sub-Agents
Background agents work equally well with [harness-backed sub-agents](../../features/harnesses/index.md) — sub-agents driven by external coding CLIs such as Claude Code or Codex. This lets you dispatch multiple independent coding tasks in parallel:
```yaml
agents:
root:
model: anthropic/claude-sonnet-4-5
description: Orchestrator that fans out coding tasks
instruction: |
Dispatch the frontend and backend tasks in parallel,
then collect results and produce a summary.
sub_agents:
- claude-coder
- codex-coder
toolsets:
- type: background_agents
claude-coder:
description: Frontend specialist (Claude Code)
harness:
type: claude-code
effort: medium
codex-coder:
description: Backend specialist (Codex)
harness:
type: codex
```
The orchestrator calls `run_background_agent` for each coding task, then uses `list_background_agents` and `view_background_agent` to collect results when they finish.
> [!NOTE]
> **Harness toolsets are ignored**
>
> Harness agents use the external CLI's own tools — any `toolsets:` configured on the harness agent are silently ignored. See [Coding Harnesses](../../features/harnesses/index.md) for details and caveats.
See [`examples/coding_harness_background_agents.yaml`](https://github.com/docker/docker-agent/blob/main/examples/coding_harness_background_agents.yaml) for a complete configuration.

View File

@@ -0,0 +1,172 @@
---
title: "Fetch Tool"
description: "Read content from HTTP/HTTPS URLs."
keywords: docker agent, ai agents, tools, toolsets, fetch tool
linkTitle: "Fetch"
weight: 50
---
_Read content from HTTP/HTTPS URLs._
## Overview
The fetch tool lets agents retrieve content from one or more HTTP/HTTPS URLs. It is **read-only** — only `GET` requests are supported. The tool respects `robots.txt`, limits response size (1 MB per URL), and can return content as plain text, Markdown (converted from HTML), or raw HTML.
> [!NOTE]
> **GET only**
>
> The fetch tool does **not** support `POST`, `PUT`, `DELETE` or other methods, and does not expose request bodies or per-call custom headers (the toolset can still attach static [credential headers](#custom-headers) to every request). To call REST endpoints with other verbs, use the [API tool](../api/index.md) or an [OpenAPI toolset](../openapi/index.md).
## Configuration
```yaml
toolsets:
- type: fetch
```
### Options
| Property | Type | Default | Description |
| ------------------- | ------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `timeout` | int | `30` | Default request timeout in seconds (overridable per tool call). |
| `allowed_domains` | array[string] | _none_ | Allow-list of hosts the tool may fetch. When set, every URL whose host is **not** in the list is rejected before any network call is made. Mutually exclusive with `blocked_domains`. |
| `blocked_domains` | array[string] | _none_ | Deny-list of hosts the tool must not fetch. URLs whose host matches one of these patterns are rejected before any network call (including `robots.txt`) is made. Mutually exclusive with `allowed_domains`. |
| `allow_private_ips` | boolean | `false` | Opt in to dialling **non-public** IP addresses (loopback, RFC1918, link-local — including the cloud-metadata endpoint at `169.254.169.254` — multicast, and the unspecified address). Required to reach `localhost` / internal services. See [SSRF protection](#ssrf-protection-and-reaching-localhost) below. |
| `headers` | map[string]string | _none_ | Static HTTP headers attached to **every** request the toolset issues (including `robots.txt`). Values support `${env.VAR}` for secrets. Caller-supplied entries override the default `User-Agent` and the format-driven `Accept` header. Headers are stripped on cross-host redirects so credentials never leak to a third-party host. See [Custom headers](#custom-headers) below. |
### Domain matching
Domain patterns in `allowed_domains` and `blocked_domains` use the following rules (case-insensitive):
- **Bare domain** — `example.com` matches the host `example.com` _and_ any subdomain such as `docs.example.com`. It does **not** match unrelated hosts that share a suffix (e.g. `badexample.com`).
- **Leading dot** — `.example.com` matches **only** strict subdomains (`docs.example.com`, `a.b.example.com`), not the apex `example.com`.
- **Wildcard glob** — `*.example.com` is an alias for the leading-dot form; the apex is excluded. The `*` is only valid as a leading `*.` token (entries like `foo.*`, `*.*.example.com`, or a bare `*` are rejected at config-load time).
- **IP literal** — IP addresses are matched exactly (`169.254.169.254`).
- **CIDR range** — `169.254.0.0/16`, `10.0.0.0/8`, `::1/128`, `fc00::/7`. Matches when the URL's host parses as an IP inside the network. Hostname hosts never match a CIDR pattern. Malformed CIDRs are rejected at config-load time.
- **Trailing dots** in FQDN-form URLs (`http://example.com./`) are stripped before matching, so they cannot bypass a deny-list entry.
The lists are mutually exclusive: a single fetch toolset may set either `allowed_domains` or `blocked_domains`, but not both.
When a list is configured, every redirect target is re-checked against the same list. A request to an allowed origin that redirects to a forbidden host is rejected before any data is read from the redirect.
> [!WARNING]
> **Limitations**
>
> Matching is purely string-based on the URL host. It does **not** perform DNS resolution and does **not** normalise alternative IP encodings (decimal `2852039166`, hex `0xa9.0xfe.0xa9.0xfe`, octal, etc. IPv4-mapped IPv6 addresses ARE normalized to their IPv4 form). If you need to deny access to a specific IP, also list its alternative encodings, or block at the network layer.
### Custom Timeout
```yaml
toolsets:
- type: fetch
timeout: 60
```
### Custom headers
Attach static headers — typically credentials — to every request. Values support `${env.VAR}` interpolation so secrets stay out of YAML, and headers are dropped on cross-host redirects so a redirect chain cannot leak them to a third-party host:
```yaml
toolsets:
- type: fetch
allowed_domains:
- docs.internal.example.com
headers:
Authorization: "Bearer ${env.INTERNAL_DOCS_TOKEN}"
X-Internal-Client: "docker-agent"
```
> [!WARNING]
> **Pair credential headers with an allow-list**
>
> When `headers` carries credentials (e.g. `Authorization`), set `allowed_domains` to the specific hosts that should receive them. Stdlib already strips a small allow-list (`Authorization`, `Cookie`, `WWW-Authenticate`) on cross-domain redirects, and the fetch tool additionally strips every operator-supplied header on cross-host redirects — but an allow-list is the strongest guarantee against accidental exfiltration.
### Restrict to specific domains
```yaml
toolsets:
- type: fetch
allowed_domains:
- docker.com # docker.com and *.docker.com
- github.com # github.com and *.github.com
- .githubusercontent.com # only subdomains, e.g. raw.githubusercontent.com
```
### Block sensitive hosts
```yaml
toolsets:
- type: fetch
blocked_domains:
- 169.254.169.254 # cloud metadata endpoint (literal IP)
- 169.254.0.0/16 # entire link-local range (CIDR)
- 10.0.0.0/8 # RFC1918 private range
- "*.internal.example.com" # any subdomain (wildcard)
- internal.example.com # internal corporate hostname
```
> [!NOTE]
> **Already blocked by default**
>
> You do **not** need to add loopback, RFC1918, link-local (incl. `169.254.169.254`), multicast or the unspecified address to `blocked_domains` to be safe — the fetch tool already refuses connections to those ranges at dial time, after DNS resolution. The example above is only useful if you also want to reject those hosts _before_ any network call (and to surface a clearer error message to the agent), or if you have set `allow_private_ips: true` and want to deny a specific subset.
### SSRF protection and reaching localhost
By default, the fetch tool refuses connections to **non-public IP addresses** — even when DNS for an otherwise-public host resolves to one of them (so DNS rebinding is also blocked). The check happens at dial time, after DNS resolution, and rejects:
- **Loopback** — `127.0.0.0/8`, `::1` (this is what blocks `http://localhost/...` and `http://127.0.0.1/...`)
- **RFC1918 private ranges** — `10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16`
- **Link-local** — `169.254.0.0/16` (IPv4, including the cloud-metadata endpoint `169.254.169.254`) and `fe80::/10` (IPv6)
- **Multicast** and the **unspecified** address (`0.0.0.0`, `::`)
- **IPv4-mapped IPv6** — addresses like `::ffff:127.0.0.1` or `::ffff:169.254.169.254` are normalized to their IPv4 form and blocked accordingly
This is the default because LLM-driven fetches are a classic Server-Side Request Forgery (SSRF) vector: a prompt-injected URL can otherwise reach internal services, cloud metadata, or admin interfaces on the host running the agent.
If an agent legitimately needs to call **localhost** or an **internal service**, opt in with `allow_private_ips: true`:
```yaml
toolsets:
- type: fetch
allow_private_ips: true
allowed_domains:
- localhost
- 127.0.0.1
- 10.0.0.0/8 # internal corporate range
```
> [!WARNING]
> **Pair with an allow-list**
>
> Setting `allow_private_ips: true` alone re-exposes the SSRF surface. We strongly recommend combining it with an `allowed_domains` entry that restricts the tool to the specific internal hosts or CIDRs the agent actually needs (e.g. `localhost`, `127.0.0.1`, or your internal CIDR).
>
> **Note:** `allowed_domains` is checked _before_ DNS resolution (string-based on hostname), while the SSRF check happens _after_ DNS resolution (on the resolved IP). This means `allowed_domains` and `blocked_domains` are evaluated independently of `allow_private_ips` and continue to apply. A public hostname in `allowed_domains` that resolves to a private IP will still be blocked unless `allow_private_ips: true` is set.
## Tool Interface
The toolset exposes a single tool, `fetch`, with the following parameters:
| Parameter | Type | Required | Description |
| --------- | -------------- | -------- | ----------------------------------------------------------------------------------------------------------- |
| `urls` | array[string] | ✓ | One or more HTTP/HTTPS URLs to fetch (all via `GET`). |
| `format` | string | ✓ | Output format: `text`, `markdown`, or `html`. HTML responses are converted to text/markdown when requested. |
| `timeout` | integer | ✗ | Per-call request timeout in seconds. Overrides the toolset default. Valid range: `1`–`300`. |
Responses are capped at **1 MB** per URL. Hosts that disallow the agent's user-agent via `robots.txt` are skipped with a clear error.
> [!TIP]
> **Fetch vs. API Tool**
>
> Use `fetch` when the agent needs to read arbitrary public URLs at runtime. Use the [API tool](../api/index.md) to expose specific, structured HTTP endpoints (including non-`GET` verbs) as named tools.
## Domain Filtering
The `allowed_domains`, `blocked_domains`, and `allow_private_ips` options let you control which hosts the fetch tool may reach. The complete reference is in the [Options](#options) table and [Domain matching](#domain-matching) section above.
**Key points:**
- `allowed_domains` — allow-list; only listed hosts (and their subdomains for bare-domain entries) are reachable
- `blocked_domains` — deny-list; mutually exclusive with `allowed_domains` (a config error is thrown if both are set)
- `allow_private_ips` — defaults to `false`; set to `true` to reach loopback / RFC-1918 / link-local addresses
- The same `allow_private_ips` flag is also supported on `api`, `openapi`, `a2a`, and remote `mcp` toolsets
See [`examples/fetch_domain_filtering.yaml`](https://github.com/docker/docker-agent/blob/main/examples/fetch_domain_filtering.yaml) for a complete filtering example, and [`examples/remote_mcp_allow_private_ips.yaml`](https://github.com/docker/docker-agent/blob/main/examples/remote_mcp_allow_private_ips.yaml) for the equivalent pattern on remote MCP toolsets.

View File

@@ -0,0 +1,112 @@
---
title: "Filesystem Tool"
description: "Read, write, list, search, and navigate files and directories."
keywords: docker agent, ai agents, tools, toolsets, filesystem tool
linkTitle: "Filesystem"
weight: 10
---
_Read, write, list, search, and navigate files and directories._
## Overview
The filesystem tool gives agents the ability to explore codebases, read and edit files, create new files, search across files, and navigate directory structures. Paths are resolved relative to the working directory, though agents can also use absolute paths.
## Available Tools
| Tool | Description |
| ---------------------- | ------------------------------------------------------------------------- |
| `read_file` | Read the complete contents of a file |
| `read_multiple_files` | Read several files in one call (more efficient than multiple `read_file`) |
| `write_file` | Create or overwrite a file with new content |
| `edit_file` | Make line-based edits (find-and-replace) in an existing file |
| `list_directory` | List files and directories at a given path |
| `directory_tree` | Recursive tree view of a directory |
| `create_directory` | Create a new directory (creates parent directories as needed) |
| `remove_directory` | Remove an empty directory |
| `search_files_content` | Search for text or regex patterns across files |
## Configuration
```yaml
toolsets:
- type: filesystem
```
### Options
| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `ignore_vcs` | boolean | `true` | When `true` (default), `.git` directories and `.gitignore` patterns are excluded from listings and searches. Set to `false` to include them. |
| `post_edit` | array | `[]` | Commands to run after editing files matching a path pattern |
| `post_edit[].path` | string | — | Glob pattern for files (e.g., `*.go`, `src/**/*.ts`) |
| `post_edit[].cmd` | string | — | Command to run (use `${file}` for the edited file path) |
| `allow_list` | array | `[]` | Directories the tools may access. Empty = unrestricted (default). |
| `deny_list` | array | `[]` | Directories the tools must not access. Takes precedence over `allow_list`. |
### Path access control
By default the filesystem tools are unrestricted: relative paths resolve
from the working directory, but absolute paths and `..` traversals can
reach anywhere the agent process can. Configure `allow_list` and/or
`deny_list` to sandbox the toolset.
Entries in either list are expanded as follows:
- `"."` — the agent's working directory
- `"~"` or `"~/..."` — the user's home directory
- `"$VAR"` / `"${VAR}"` / `"${env.VAR}"` — environment variable expansion
- absolute paths — used as-is
- relative paths — anchored at the working directory
Symlinks are resolved before the containment check, so a symlink inside an
allowed root cannot be used to escape it. When an `allow_list` is set,
each entry is opened as a Go [`*os.Root`](https://pkg.go.dev/os#Root) so
that the kernel's rooted-lookup semantics also reject `..` and symlink
escapes at I/O time, not just at resolve time.
```yaml
toolsets:
- type: filesystem
# Restrict every operation to the working directory and the user's
# home folder, then carve credentials out of the home folder.
allow_list:
- "."
- "~"
deny_list:
- "~/.ssh"
- "~/.aws"
```
When the path supplied by the agent is rejected, the tool returns a
structured error rather than performing any filesystem I/O. This makes the
restriction visible to the model so it can adjust its plan.
### Post-Edit Hooks
Automatically run formatting, linting, or other commands after the agent edits a file. The command fires once per file after each edit operation (`write_file` and `edit_file`). Use `${file}` as a placeholder for the absolute path of the edited file.
```yaml
toolsets:
- type: filesystem
ignore_vcs: false
post_edit:
- path: "*.go"
cmd: "gofmt -w ${file}"
- path: "*.ts"
cmd: "prettier --write ${file}"
- path: "src/**/*.py"
cmd: "black ${file}"
```
| Property | Type | Description |
| --- | --- | --- |
| `path` | string | Glob pattern matched against the file path. `*.go` matches any `.go` file; `src/**/*.ts` matches `.ts` files anywhere under `src/`. |
| `cmd` | string | Shell command to run. `${file}` expands to the absolute path of the just-edited file. |
Post-edit commands run with the same working directory as the agent. If a command exits non-zero, the error is logged and surfaced to the model as a warning, but the edit is not rolled back.
See [`examples/post_edit.yaml`](https://github.com/docker/docker-agent/blob/main/examples/post_edit.yaml) for a complete example.
> [!TIP]
> The filesystem tool resolves paths relative to the working directory. Agents can also use absolute paths.

View File

@@ -0,0 +1,62 @@
---
title: "Handoff Tool"
description: "Hand off the active conversation to another local agent defined in the same config."
keywords: docker agent, ai agents, tools, toolsets, handoff tool
linkTitle: "Handoff"
weight: 70
---
_Hand off the active conversation to another local agent defined in the same config._
## Overview
The `handoff` tool lets an agent transfer control of the **current conversation** to another agent in the **same config file**. Unlike [`transfer_task`](../transfer-task/index.md), which delegates a sub-task and collects the result, `handoff` rewires the session so the receiving agent continues the conversation directly with the user.
This is the core mechanism for **handoffs routing** — a pattern where a router agent classifies the user's request and hands it off to a specialist, which then owns the rest of the session.
> [!NOTE]
> **Local only**
>
> The `handoff` tool only targets agents declared in the **same** config file by their local name. It does **not** open network connections. To delegate to a remote agent over the network, use the [A2A toolset](../a2a/index.md) instead.
## Configuration
The tool is enabled implicitly when an agent declares a non-empty `handoffs:` list. You do **not** add `- type: handoff` under `toolsets:` — it is not a toolset type.
```yaml
agents:
router:
model: openai/gpt-4o
description: Routes questions to the right specialist
instruction: |
Classify the user's question and hand off to the most appropriate
specialist. If unsure, ask a clarifying question first.
handoffs: [billing, support]
billing:
model: openai/gpt-4o
description: Billing specialist
instruction: Answer billing questions.
support:
model: openai/gpt-4o
description: Technical support specialist
instruction: Help with technical issues.
```
The router agent automatically gets a `handoff` tool it can call to switch the conversation to `billing` or `support`.
## Tool Interface
The `handoff` tool takes a single parameter:
| Parameter | Type | Required | Description |
| --------- | ------ | -------- | ----------------------------------------------------------------- |
| `agent` | string | ✓ | The local name of the agent to hand off the conversation to. |
Only names listed in the current agent's `handoffs:` field are valid targets.
> [!TIP]
> **See also**
>
> For sub-task delegation (caller stays in control, waits for the result), see [Transfer Task](../transfer-task/index.md). For remote agent connections over the network, see the [A2A toolset](../a2a/index.md). For the broader pattern, see [Handoffs Routing](../../concepts/multi-agent/index.md#handoffs-routing).

View File

@@ -0,0 +1,226 @@
---
title: "LSP Tool"
description: "Connect to Language Server Protocol servers for code intelligence."
keywords: docker agent, ai agents, tools, toolsets, lsp tool
linkTitle: "LSP"
weight: 220
---
_Connect to Language Server Protocol servers for code intelligence._
## Overview
The LSP tool connects your agent to any Language Server Protocol (LSP) server, providing comprehensive code intelligence capabilities like go-to-definition, find references, diagnostics, and more.
> [!NOTE]
> **What is LSP?**
>
> The [Language Server Protocol](https://microsoft.github.io/language-server-protocol/) is a standard for providing language features like autocomplete, go-to-definition, and diagnostics. Most programming languages have LSP servers available.
## Available Tools
The LSP toolset provides these tools to the agent:
| Tool | Description | Read-Only |
| ----------------------- | --------------------------------------------- | --------- |
| `lsp_workspace` | Get workspace info and available capabilities | ✓ |
| `lsp_hover` | Get type info and documentation for a symbol | ✓ |
| `lsp_definition` | Find where a symbol is defined | ✓ |
| `lsp_references` | Find all references to a symbol | ✓ |
| `lsp_document_symbols` | List all symbols in a file | ✓ |
| `lsp_workspace_symbols` | Search symbols across the workspace | ✓ |
| `lsp_diagnostics` | Get errors and warnings for a file | ✓ |
| `lsp_code_actions` | Get available quick fixes and refactorings | ✓ |
| `lsp_rename` | Rename a symbol across the workspace | ✗ |
| `lsp_format` | Format a file | ✗ |
| `lsp_call_hierarchy` | Find incoming/outgoing calls | ✓ |
| `lsp_type_hierarchy` | Find supertypes/subtypes | ✓ |
| `lsp_implementations` | Find interface implementations | ✓ |
| `lsp_signature_help` | Get function signature at call site | ✓ |
| `lsp_inlay_hints` | Get type annotations and parameter names | ✓ |
## Configuration
```yaml
agents:
developer:
model: anthropic/claude-sonnet-4-5
description: Code developer with LSP support
instruction: You are a software developer.
toolsets:
- type: lsp
command: gopls
args: []
file_types: [".go"]
- type: filesystem
- type: shell
```
## Properties
| Property | Type | Required | Description |
| ------------- | ------ | -------- | ---------------------------------------------------------------------------------------------------------------------------- |
| `command` | string | ✓ | LSP server executable command |
| `args` | array | ✗ | Command-line arguments for the LSP server |
| `env` | object | ✗ | Environment variables for the LSP process |
| `file_types` | array | ✗ | File extensions this LSP handles (e.g., `[".go", ".mod"]`) |
| `working_dir` | string | ✗ | Working directory for the LSP server process. Relative paths are resolved against the agent's working directory. Defaults to the agent's working directory when omitted. |
| `version` | string | ✗ | Package reference for [auto-installing](../../configuration/tools/index.md#auto-installing-tools) the command binary |
## Common LSP Servers
Here are configurations for popular languages:
### Go (gopls)
```yaml
toolsets:
- type: lsp
command: gopls
version: "golang/tools@v0.21.0" # optional: auto-install if not in PATH
file_types: [".go"]
```
If your Go module lives in a subdirectory (e.g. a monorepo where `go.mod` is under `./backend`), set `working_dir` so `gopls` is started from the module root:
```yaml
toolsets:
- type: lsp
command: gopls
file_types: [".go"]
working_dir: ./backend # gopls must be started from the module root
```
### TypeScript/JavaScript (typescript-language-server)
```yaml
toolsets:
- type: lsp
command: typescript-language-server
args: ["--stdio"]
file_types: [".ts", ".tsx", ".js", ".jsx"]
```
### Python (pylsp)
```yaml
toolsets:
- type: lsp
command: pylsp
file_types: [".py"]
```
### Rust (rust-analyzer)
```yaml
toolsets:
- type: lsp
command: rust-analyzer
file_types: [".rs"]
```
### C/C++ (clangd)
```yaml
toolsets:
- type: lsp
command: clangd
file_types: [".c", ".cpp", ".h", ".hpp"]
```
## Multiple LSP Servers
You can configure multiple LSP servers for different file types:
```yaml
agents:
polyglot:
model: anthropic/claude-sonnet-4-5
description: Multi-language developer
instruction: You are a full-stack developer.
toolsets:
- type: lsp
command: gopls
file_types: [".go"]
- type: lsp
command: typescript-language-server
args: ["--stdio"]
file_types: [".ts", ".tsx", ".js", ".jsx"]
- type: lsp
command: pylsp
file_types: [".py"]
- type: filesystem
- type: shell
```
## Workflow Instructions
The LSP tool includes built-in instructions that guide the agent on how to use it effectively. The agent learns to:
1. Start with `lsp_workspace` to understand available capabilities
2. Use `lsp_workspace_symbols` to find relevant code
3. Use `lsp_references` before modifying any symbol
4. Check `lsp_diagnostics` after every code change
5. Apply `lsp_format` after edits are complete
> [!TIP]
> **Best Practice**
>
> Always include the `filesystem` tool alongside LSP. The agent needs filesystem access to read and write code files, while LSP provides intelligence about the code.
## Capability Detection
Not all LSP servers support all features. During the `initialize` handshake, docker-agent reads the server's `ServerCapabilities` and **filters out the `lsp_*` tools the server does not advertise**. The model never sees, for example, `lsp_inlay_hints` against a server that doesn't support it, so it can't waste a turn calling a tool that would only fail.
The agent uses `lsp_workspace` to discover what's available:
```text
Workspace Information:
- Root: /path/to/project
- Server: gopls v0.14.0
- File types: .go
Available Capabilities:
- Hover: Yes
- Go to Definition: Yes
- Find References: Yes
- Rename: Yes
- Code Actions: Yes
- Formatting: Yes
- Call Hierarchy: Yes
- Type Hierarchy: Yes
...
```
## Auto-Restart and Lifecycle
LSP toolsets are managed by the same supervisor as MCP toolsets, so a crashed `gopls` (or any other language server) is reconnected automatically with exponential backoff. Use the [`lifecycle`](../../configuration/tools/index.md#toolset-lifecycle) block to tune the policy per toolset — for example, mark `gopls` as `strict` if your CI flow requires it to be available, or use `/toolset-restart gopls` from the TUI to force a reconnect when the server gets stuck.
```yaml
toolsets:
- type: lsp
command: gopls
file_types: [".go"]
lifecycle:
profile: resilient # default: auto-restart on crash with exponential backoff
```
## Position Format
All LSP tools use **1-based** line and character positions:
- Line 1 is the first line of the file
- Character 1 is the first character on a line
```json
{
"file": "/path/to/file.go",
"line": 42,
"character": 15
}
```
> [!TIP]
> **Auto-Installation**
>
> docker-agent can automatically download and install LSP servers if they are not found in your PATH. Use the `version` property to specify a package, or let docker-agent auto-detect it from the command name. See [Auto-Installing Tools](../../configuration/tools/index.md#auto-installing-tools) for details.

View File

@@ -0,0 +1,112 @@
---
title: "MCP Catalog Tool"
description: "Let the agent discover and activate remote MCP servers from the Docker MCP Catalog on demand."
keywords: docker agent, ai agents, tools, toolsets, mcp catalog tool
linkTitle: "MCP Catalog"
weight: 120
---
_Let the agent discover and activate remote MCP servers from the Docker MCP Catalog on demand._
## Overview
The `mcp_catalog` toolset gives an agent access to a curated subset of the [Docker MCP Catalog](https://hub.docker.com/search?q=&type=mcp) — every server in this subset is reachable over the **streamable-http** transport, so docker-agent can talk to it directly without the MCP gateway or a local subprocess.
Servers are **not** active by default. Instead, the toolset exposes a small set of meta-tools the agent uses to search, enable, and disable servers as a turn unfolds. Tools from un-enabled servers stay hidden, so the prompt is not flooded with hundreds of tool definitions the agent will never use.
> [!NOTE]
> **When to use it**
>
> Use `mcp_catalog` when you want the agent to _decide at runtime_ which third-party services it needs (Notion, Stripe, Brave Search, …) instead of pinning that decision in YAML up front. For a fixed set of servers, declare each one with [`type: mcp`](../../configuration/tools/index.md#mcp-tools) directly — the catalog adds an extra layer of meta-tools that pure `type: mcp` entries do not need.
## Configuration
```yaml
toolsets:
- type: mcp_catalog
```
The catalog is embedded in the docker-agent binary and refreshed with each release. By default every server in the embedded subset is offered.
### Restricting the offered servers
Two optional lists narrow what the toolset offers, so an agent sees a focused, predictable menu instead of the full catalog:
- **`allowed_servers`** — when non-empty, **only** these catalog server ids are searchable and enableable; every other entry is hidden.
- **`blocked_servers`** — removes individual ids from the offered set. It is applied **after** `allowed_servers`, so a server listed in both is blocked (block wins over allow).
Both take server ids (the `id` field returned by `search_remote_mcp_servers`). An empty or omitted list disables that filter.
```yaml
toolsets:
- type: mcp_catalog
allowed_servers:
- docker-docs
- microsoft-learn
- hugging-face
blocked_servers:
- gitmcp
```
## Meta-Tools
Up to five tools are exposed to the model. The disable / reset-auth pair only appears once at least one server is enabled, so the meta-tool surface stays minimal until the agent activates something.
| Tool | When visible | Description |
| ------------------------------- | ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| `search_remote_mcp_servers` | Always | Case-insensitive fuzzy search over id, title, description, category and tags. Returns id, auth requirements (`oauth` / `none`) and URL. |
| `enable_remote_mcp_server` | Always | Activate a server by id. **Blocks** until the connection (and any required OAuth handshake) completes; on success the server's tools are immediately live and the model continues with the user's original request in the same turn. |
| `list_remote_mcp_servers` | Always | Show currently enabled servers and their connection state. |
| `disable_remote_mcp_server` | After first enable | Stop a server and remove its tools from the active set. |
| `reset_remote_mcp_server_auth` | After first enable | Drop persisted OAuth credentials so the next enable triggers a fresh authorization flow. No-op for `none` servers. |
### Workflow
1. The agent calls `search_remote_mcp_servers` with a keyword matching the user's intent (`"notion"`, `"stripe"`, `"docs"`, `"browser"`, `"grafana"`, …).
2. It picks a matching server id and calls `enable_remote_mcp_server`. **`enable` blocks** until the MCP handshake (and any required OAuth flow) completes:
- on success the server's tools are available **in the same turn** — the agent goes straight to the user's original request, no re-ask required;
- on failure (user dismissed the authorization dialog, server refused) the tool returns an error result naming the specific reason so the agent can recover instead of pretending the server is connected.
3. It uses the newly activated tools as it would any other.
4. When done, it calls `disable_remote_mcp_server` to remove the server from the active set.
## Authentication
The catalog only includes servers docker-agent can authenticate itself, so there are two auth flavours:
- **`oauth`** — `enable_remote_mcp_server` surfaces an authorization URL through the elicitation pipeline (the same one used by YAML-declared remote MCP toolsets) and blocks until the user either authorizes or cancels. Once the user authorizes, tokens are persisted in the OS keyring and re-used on subsequent runs. Use `reset_remote_mcp_server_auth` to wipe them. If the user dismisses the dialog, `enable` returns an error result naming the decline so the agent can ask whether to retry.
- **`none`** — No authentication. The server is reachable as soon as it is enabled.
Servers that require a caller-provided API key are intentionally excluded from the catalog. To use one, declare it explicitly with [`type: mcp`](../../configuration/tools/index.md#mcp-tools) and supply the key via an environment variable.
## Example
```yaml
agents:
root:
model: anthropic/claude-sonnet-4-5
description: Agent that can on-demand connect to remote MCP servers from the Docker MCP Catalog.
instruction: |
You can discover and activate remote MCP servers on demand.
Use search_remote_mcp_servers to find a server matching the
user's intent, then enable_remote_mcp_server to activate it.
Be conservative: enable only the servers you actually need for
the task at hand. Disable a server with disable_remote_mcp_server
once you are done with it.
toolsets:
- type: mcp_catalog
```
A complete, runnable configuration lives in [`examples/mcp_catalog.yaml`](https://github.com/docker/docker-agent/blob/main/examples/mcp_catalog.yaml). A curated, allow/block-listed variant lives in [`examples/mcp_catalog_filtered.yaml`](https://github.com/docker/docker-agent/blob/main/examples/mcp_catalog_filtered.yaml).
## Notes and Limitations
- **Streamable-http only.** The catalog deliberately excludes servers that require a local subprocess or the MCP gateway — declare those with [`type: mcp`](../../configuration/tools/index.md#mcp-tools) instead.
- **Catalog membership changes between releases.** The set of available servers is updated with each docker-agent release as integrations are added or removed. Servers present in one release may not appear in the next.
- **Blocking enable.** DNS, TCP, MCP handshake and any OAuth flow happen synchronously inside `enable_remote_mcp_server` so the agent gets a deterministic result in the same turn. On startup, however, the runtime probes tools non-interactively (`mcp.WithoutInteractivePrompts`); OAuth-pending servers fail fast there and are silently deferred to the next interactive turn — including the sidebar-only tool-count pass, where a dialog would be impossible.
- **No prompt discovery.** MCP prompt lookups (`/prompts`) walk YAML-declared `mcp` toolsets directly; prompts exposed by servers activated through the catalog are not surfaced. Tools — the primary interface — work fine.
- **Frozen at build time.** The list of servers is embedded in the binary. New entries land with each docker-agent release.
> [!TIP]
> **Pair with permissions**
>
> Because the agent decides which third-party services to talk to, this toolset works best with explicit [permissions](../../configuration/permissions/index.md) on the surrounding tools (filesystem writes, shell commands) so a misrouted server cannot exfiltrate data unnoticed.

View File

@@ -0,0 +1,287 @@
---
title: "MCP Tool"
description: "Extend agents with external tools via the Model Context Protocol."
keywords: docker agent, ai agents, tools, toolsets, mcp tool
linkTitle: "MCP"
weight: 130
aliases:
- /ai/docker-agent/integrations/mcp/
---
_Extend agents with external tools via the Model Context Protocol (MCP)._
## Overview
The `mcp` toolset connects your agent to any MCP server — a process or remote service that exposes tools, resources, and prompts over the [Model Context Protocol](https://modelcontextprotocol.io/). Three flavours are supported:
| Flavour | Transport | Best for |
| --- | --- | --- |
| **Docker MCP** | Container via the [MCP Gateway](https://github.com/docker/mcp-gateway) | Curated, sandboxed servers from the [Docker MCP Catalog](https://hub.docker.com/u/mcp) |
| **Local stdio** | Subprocess over stdin/stdout | Custom or community MCP servers run from a binary or `npx`/`pip` package |
| **Remote** | Streamable HTTP or SSE | Cloud services with hosted MCP endpoints (Linear, Notion, Atlassian, …) |
> [!NOTE]
> **What is MCP?**
>
> The [Model Context Protocol](https://modelcontextprotocol.io/) is an open standard for connecting AI tools. Docker Agent can both _use_ MCP servers (this page) and _expose_ agents as MCP servers — see [MCP Mode](../../features/mcp-mode/index.md).
## Docker MCP (Recommended)
Run MCP servers as secure Docker containers via the MCP Gateway. The `ref: docker:<name>` syntax pulls a curated definition from the Docker MCP Catalog:
```yaml
toolsets:
- type: mcp
ref: docker:duckduckgo # web search
- type: mcp
ref: docker:github-official # GitHub integration
tools: ["list_issues", "create_issue"]
```
Browse available servers at the [Docker MCP Catalog](https://hub.docker.com/u/mcp).
| Property | Type | Description |
| ------------- | ------ | ---------------------------------------------------------------- |
| `ref` | string | Docker MCP reference (`docker:name`) or a name from the [reusable `mcps:`](../../configuration/overview/index.md#reusable-mcp-servers-mcps) block. |
| `tools` | array | Optional whitelist — only expose these tools to the model. |
| `instruction` | string | Custom instructions injected into the agent's context. |
| `config` | any | MCP server-specific configuration passed during initialization. |
| `working_dir` | string | Working directory for the MCP gateway subprocess. Only applies when the catalog entry runs as a local process (not remote). Relative paths are resolved against the agent's working directory. Supports `${env.VAR}` (canonical), plus `~` and shell-style `$VAR`/`${VAR}` expansion ([details](../../configuration/overview/index.md#variable-expansion-in-config-fields)). |
## Local MCP (stdio)
Run MCP servers as local processes communicating over stdin/stdout:
```yaml
toolsets:
- type: mcp
command: python
args: ["-m", "mcp_server"]
tools: ["search", "fetch"]
env:
API_KEY: value
```
| Property | Type | Description |
| ------------- | ------ | ----------- |
| `command` | string | Command to execute the MCP server. |
| `args` | array | Command arguments. |
| `tools` | array | Optional whitelist — only expose these tools. |
| `env` | object | Environment variables (key-value pairs). |
| `working_dir` | string | Working directory for the MCP server process. Relative paths are resolved against the agent's working directory. Defaults to the agent's working directory when omitted. Supports `${env.VAR}` (canonical), plus `~` and shell-style `$VAR`/`${VAR}` expansion ([details](../../configuration/overview/index.md#variable-expansion-in-config-fields)). |
| `instruction` | string | Custom instructions injected into the agent's context. |
| `version` | string | Package reference for [auto-installing](../../configuration/tools/index.md#auto-installing-tools) the command binary. |
> [!TIP]
> **Auto-installation**
>
> If the `command` is not in your `PATH`, docker-agent looks it up in the [aqua registry](https://github.com/aquaproj/aqua-registry) and installs it for you. Use `version: "false"` to opt out, or set `DOCKER_AGENT_AUTO_INSTALL=false` globally. See [Auto-Installing Tools](../../configuration/tools/index.md#auto-installing-tools).
## Remote MCP (Streamable HTTP / SSE)
Connect to MCP servers over the network. OAuth flows (including [Dynamic Client Registration](https://datatracker.ietf.org/doc/html/rfc7591)) are handled automatically — docker-agent opens your browser when authentication is required and caches tokens for subsequent sessions. Tokens are refreshed silently when they expire or are revoked server-side; if a silent refresh is not possible, the OAuth prompt reappears on the next message.
```yaml
toolsets:
- type: mcp
remote:
url: "https://mcp.linear.app/mcp"
transport_type: "streamable" # or "sse" for legacy servers
headers:
Authorization: "Bearer ${env.LINEAR_TOKEN}"
# Optional: allow OAuth helper requests to reach private/internal IPs.
allow_private_ips: false
tools: ["search_issues", "create_issue"]
```
| Property | Type | Description |
| ----------------------- | ------- | ----------- |
| `remote.url` | string | Base URL of the MCP server. |
| `remote.transport_type` | string | `streamable` or `sse`. |
| `remote.headers` | object | HTTP headers (typically for static auth tokens). |
| `remote.oauth` | object | Explicit OAuth client credentials for servers that don't support DCR. See [Remote MCP Servers](../../features/remote-mcp/index.md#oauth-for-servers-without-dynamic-client-registration). |
| `allow_private_ips` | boolean | Permit remote MCP OAuth helper requests to dial non-public IP addresses. Use only for trusted internal servers. |
For a curated list of public remote MCP endpoints (Linear, GitHub, Vercel, Notion, …) and full OAuth configuration details, see [Remote MCP Servers](../../features/remote-mcp/index.md).
## Embedded Resources
MCP tool results can include embedded resources — images, PDFs, and text files returned directly in the tool response. Docker Agent preserves these as attachments and forwards them to the model as native content blocks:
- **Anthropic** — images become `image` blocks in the `tool_result`; PDFs and other documents become `document` blocks.
- **OpenAI** — images are forwarded as `input_image` data URIs; PDFs as `input_file` data URIs in the tool result content.
- **Bedrock** and **Gemini** — receive equivalent provider-native representations.
No configuration is required. When an MCP server returns an embedded resource alongside its text output, the resource is automatically attached and sent to the model on the next turn. This is useful for MCP servers that generate charts, export PDFs, or return binary data as part of their responses.
## Reusable Definitions (`mcps:`)
Repeated MCP server configurations can be hoisted into the top-level `mcps:` section and referenced by name with `{type: mcp, ref: <name>}`:
```yaml
mcps:
github:
remote:
url: https://api.githubcopilot.com/mcp
transport_type: sse
playwright:
command: npx
args: ["-y", "@modelcontextprotocol/server-playwright"]
agents:
root:
model: openai/gpt-5
toolsets:
- type: mcp
ref: github
- type: mcp
ref: playwright
```
See [Reusable MCP Servers](../../configuration/overview/index.md#reusable-mcp-servers-mcps) for the full reference.
## Common Options
These properties apply to every MCP toolset regardless of flavour:
### Tool filtering
```yaml
toolsets:
- type: mcp
ref: docker:github-official
tools: ["list_issues", "create_issue", "get_pull_request"]
```
Whitelisting tools improves model accuracy — fewer choices means less confusion.
### Deferred loading
Skip the toolset's startup cost until its tools are actually called:
```yaml
toolsets:
- type: mcp
ref: docker:github-official
defer: true
# Or defer specific tools within a toolset:
- type: mcp
ref: docker:slack
defer: ["list_channels", "search_messages"]
```
### Custom instructions
```yaml
toolsets:
- type: mcp
ref: docker:github-official
instruction: |
Use these tools to manage GitHub issues.
Always check for existing issues before creating new ones.
Label new issues with 'triage' by default.
```
### TOON-encoded outputs
Re-encode verbose JSON outputs as the compact [TOON](https://github.com/alpkeskin/gotoon) format to save context budget. Typically yields 30–60% smaller payloads on list/search tools.
`toon` is a regex string that is matched against tool names. Any tool whose name matches the pattern has its JSON output transparently re-encoded as TOON before it is shown to the model. The re-encoding reduces schema verbosity, which is especially useful when a model struggles with large or repetitive tool output.
```yaml
toolsets:
- type: mcp
ref: docker:github-official
toon: ".*" # toonify every tool from this server
- type: mcp
command: my-server
toon: "list_.*,get_.*" # only toonify list_/get_ tools
```
The value is a comma-separated list of regexes (or a single regex). A tool name must match at least one pattern to be re-encoded. Setting `toon: ".*"` re-encodes all tools from that toolset.
See [`examples/github-toon.yaml`](https://github.com/docker/docker-agent/blob/main/examples/github-toon.yaml) for a practical example using the GitHub MCP server.
### Per-toolset model routing
Process tool results from this toolset with a different (typically cheaper / faster) model. The override is one-shot — subsequent turns return to the agent's primary model:
```yaml
toolsets:
- type: mcp
ref: docker:github-official
model: openai/gpt-4o-mini
```
See [Per-Toolset Model Routing](../../configuration/tools/index.md#per-toolset-model-routing).
### Lifecycle (auto-restart, profiles)
Local stdio and remote MCP servers are supervised: crashed servers reconnect automatically with exponential backoff. **Remote** MCP servers (Streamable HTTP / SSE) also reconnect after idle/clean connection closes — services like Notion and Linear periodically close idle connections, and docker-agent reconnects transparently. Tune the policy with the `lifecycle` block:
```yaml
toolsets:
- type: mcp
ref: docker:duckduckgo
lifecycle:
profile: resilient # default; auto-restart with backoff
- type: mcp
command: docker
args: ["mcp", "gateway"]
lifecycle:
profile: strict # fail-fast: required, no retries
```
See [Toolset Lifecycle](../../configuration/tools/index.md#toolset-lifecycle) for all profiles and tuning knobs, and [`/toolset-restart`](../../features/tui/index.md) to force a reconnect from the TUI.
## Combined Example
```yaml
mcps:
github:
remote:
url: https://api.githubcopilot.com/mcp
transport_type: sse
agents:
root:
model: anthropic/claude-sonnet-4-5
description: Full-featured developer assistant
instruction: You are an expert developer.
toolsets:
# Docker MCP catalog entry
- type: mcp
ref: docker:duckduckgo
# Reusable definition from the top-level mcps: block
- type: mcp
ref: github
tools: ["list_issues", "create_issue"]
toon: "list_.*"
# Local stdio server with auto-install
- type: mcp
command: gopls
version: "golang/tools@v0.21.0"
args: ["mcp"]
# Remote MCP with OAuth (handled automatically)
- type: mcp
remote:
url: "https://mcp.linear.app/mcp"
transport_type: "streamable"
instruction: Use Linear for issue tracking.
```
> [!WARNING]
> **Toolset order matters**
>
> If multiple toolsets provide a tool with the same name, the first one wins: the duplicate from the later toolset is ignored and a warning identifies both toolsets. Order your toolsets intentionally. To keep both tools callable, give the MCP toolset a unique `name:` (its tools are then exposed as `<name>_<tool>`) or restrict the overlapping toolset with its `tools:` filter.
## See Also
- [Tool Configuration](../../configuration/tools/index.md) — full reference for every toolset type, plus shared options (lifecycle, TOON, model routing, …).
- [Reusable MCP Servers](../../configuration/overview/index.md#reusable-mcp-servers-mcps) — the top-level `mcps:` block.
- [Remote MCP Servers](../../features/remote-mcp/index.md) — catalog of public remote MCP endpoints + OAuth recipes.
- [MCP Mode](../../features/mcp-mode/index.md) — expose your own agents as MCP tools to Claude Desktop, Claude Code, etc.
- [Auto-Installing Tools](../../configuration/tools/index.md#auto-installing-tools) — automatic installation of MCP server binaries.

View File

@@ -0,0 +1,58 @@
---
title: "Memory Tool"
description: "Persistent key-value storage backed by SQLite for cross-session recall."
keywords: docker agent, ai agents, tools, toolsets, memory tool
linkTitle: "Memory"
weight: 100
---
_Persistent key-value storage backed by SQLite for cross-session recall._
## Overview
The memory tool provides persistent key-value storage backed by SQLite. Data survives across sessions, allowing agents to remember facts, user preferences, project context, and past decisions. Memories can be organized with categories and searched by keyword.
By default, the database is stored at `~/.cagent/memory/<config-name>/memory.db`, where `<config-name>` is derived from the loaded configuration (typically the YAML file name) and falls back to `default` when unavailable. When the agent is loaded from an OCI reference (e.g. `docker/my-agent:latest`), characters that are reserved in filesystem paths (such as `:`) are sanitised in the `<config-name>` segment — the agent's display name elsewhere is unchanged. Agents declared in the same configuration share this database by default; set an explicit `path` per toolset to isolate them.
## Available Tools
| Tool | Description |
| ----------------- | -------------------------------------------------------------------------------- |
| `add_memory` | Store a new memory with optional category |
| `get_memories` | Retrieve all stored memories |
| `delete_memory` | Delete a specific memory by ID |
| `search_memories` | Search memories by keywords and/or category (more efficient than `get_memories`) |
| `update_memory` | Update an existing memory's content and/or category by ID |
## Configuration
```yaml
toolsets:
- type: memory
```
### Options
| Property | Type | Default | Description |
| -------- | ------ | ----------------------------------------- | -------------------------------- |
| `path` | string | `~/.cagent/memory/<config-name>/memory.db` | Path to the SQLite database file |
### Custom Database Path
```yaml
toolsets:
- type: memory
path: ./agent_memory.db
```
## Categories
Memories support an optional `category` field for organization and filtering. Common categories include:
- `preference` — User preferences and settings
- `fact` — Factual information about the project or user
- `project` — Project-specific context
- `decision` — Past decisions and their rationale
> [!TIP]
> Memory is especially useful for long-running assistants that need to recall information across conversations — like coding preferences, project conventions, or context discovered during previous sessions.

View File

@@ -0,0 +1,72 @@
---
title: "Model Picker Tool"
description: "Let the agent pick between several models per turn."
keywords: docker agent, ai agents, tools, toolsets, model picker tool
linkTitle: "Model Picker"
weight: 200
---
_Let the agent pick between several models per turn._
## Overview
The model picker tool gives an agent the ability to dynamically choose which model to use for each turn of the conversation. This is useful when you want the agent to route different types of requests to different models — for example, using a fast, inexpensive model for simple queries and a more capable model for complex reasoning tasks.
## Configuration
```yaml
toolsets:
- type: model_picker
models:
- openai/gpt-5-mini
- anthropic/claude-sonnet-4-5
- openai/gpt-5
```
### Options
| Property | Type | Required | Description |
| -------- | -------------- | -------- | ------------------------------------------------------------ |
| `models` | array[string] | ✓ | List of model references the agent can choose from. Use `provider/model` format. |
## How It Works
When the model picker toolset is enabled, the agent gets two tools: `change_model` to switch to one of the configured models, and `revert_model` to return to its default model. The agent decides which model to use based on the complexity of the task, cost considerations, or other factors you describe in its instruction.
## Example
```yaml
agents:
root:
model: openai/gpt-5-mini # Default model
instruction: |
You are a helpful assistant. For simple questions, use gpt-5-mini.
For complex reasoning or coding tasks, switch to claude-sonnet-4-5 or gpt-5.
toolsets:
- type: model_picker
models:
- openai/gpt-5-mini
- anthropic/claude-sonnet-4-5
- openai/gpt-5
```
> [!TIP]
> **Cost optimization**
>
> The model picker tool is particularly useful for cost optimization: let the agent use a cheap model by default and only escalate to expensive models when necessary.
## Tool Interface
The toolset exposes two tools:
### `change_model`
| Parameter | Type | Required | Description |
| --------- | ------ | -------- | --------------------------------------------------------------------------- |
| `model` | string | ✓ | The model to switch to. Must be one of the configured models. |
### `revert_model`
Takes no parameters. Reverts the agent to its original/default model.
The switch takes effect immediately: the next inference call — including the remainder of the current agentic loop — uses the new model.

View File

@@ -0,0 +1,94 @@
---
title: "Open URL Tool"
description: "Open a fixed URL in the user's default browser."
keywords: docker agent, ai agents, tools, toolsets, open url tool
linkTitle: "Open URL"
weight: 40
---
_Open a fixed URL in the user's default browser._
## Overview
The `open_url` toolset exposes a single, argument-less tool that opens a URL
baked into the toolset definition in the user's default browser. The model
never supplies the URL — it just calls the tool by name. Launching the browser
is cross-platform: docker-agent uses `open` on macOS, `xdg-open` on Linux, and
`rundll32` on Windows.
> [!NOTE]
> **When to Use**
>
> - Letting an agent open a dashboard, documentation page, or deep link on demand
> - Deep-linking into a desktop app via a custom URI scheme (e.g. `docker-desktop://`)
> - Any "take me there" action where the destination is fixed and known up front
## Configuration
```yaml
agents:
assistant:
model: openai/gpt-4o
description: Assistant that can open the dashboard
instruction: When the user asks to see the dashboard, call open_dashboard.
toolsets:
- type: open_url
name: open_dashboard
url: https://example.com/dashboard
```
## Properties
| Property | Type | Required | Description |
| -------- | ------ | -------- | ---------------------------------------------------------------------------------------------------- |
| `url` | string | ✓ | URL to open. Supports `${env.VAR}` interpolation. Any scheme the OS can dispatch is allowed. |
| `name` | string | ✗ | Tool name the agent references. Defaults to `open_url`. Use a descriptive name when configuring several. |
## Multiple URLs
Add one toolset entry per destination, each with its own `name`:
```yaml
toolsets:
- type: open_url
name: open_dashboard
url: https://example.com/dashboard
- type: open_url
name: open_docs
url: https://docs.example.com/${env.DOCS_VERSION}
```
## URL Interpolation
The `url` field supports `${env.VAR}` placeholders, expanded at call time
against the runtime environment:
```yaml
toolsets:
- type: open_url
name: open_docs
url: https://docs.example.com/${env.DOCS_VERSION}
```
## Custom URI Schemes
Any scheme the operating system knows how to dispatch works, including deep
links into desktop applications:
```yaml
toolsets:
- type: open_url
name: open_in_docker_desktop
url: docker-desktop://dashboard/apps
```
## Limitations
- The URL must include a scheme (e.g. `https://`); bare paths are rejected.
- URLs that look like a command-line flag (starting with `-`) are refused to
prevent argument injection into the platform `open` helper.
- The tool opens the URL on the **host** running docker-agent; in headless or
remote environments where no browser/launcher is available, the call fails
gracefully and reports the error to the agent.
See [`examples/open_url.yaml`](https://github.com/docker/docker-agent/blob/main/examples/open_url.yaml) for a complete configuration.

View File

@@ -0,0 +1,82 @@
---
title: "OpenAPI Tool"
description: "Automatically generate tools from an OpenAPI specification."
keywords: docker agent, ai agents, tools, toolsets, openapi tool
linkTitle: "OpenAPI"
weight: 230
---
_Automatically generate tools from an OpenAPI specification._
## Overview
The OpenAPI tool fetches an OpenAPI 3.x specification from a URL and creates one tool per API operation. Each endpoint's parameters, request body, and description are translated into a callable tool that the agent can invoke directly.
## Configuration
```yaml
toolsets:
- type: openapi
url: "https://petstore3.swagger.io/api/v3/openapi.json"
```
### With custom headers
Pass custom headers to every HTTP request made by the generated tools (for example, for authentication):
```yaml
toolsets:
- type: openapi
url: "https://api.example.com/openapi.json"
headers:
Authorization: "Bearer ${env.API_TOKEN}"
X-Custom-Header: "my-value"
```
### Custom timeout
Override the default 30-second HTTP timeout (applies both to fetching the spec and to the generated tool calls):
```yaml
toolsets:
- type: openapi
url: "https://api.example.com/openapi.json"
timeout: 60
```
### Reaching internal services
By default the OpenAPI tool refuses connections to non-public IP addresses, blocking SSRF attempts even when DNS resolves an otherwise-public host to an internal range. Opt in with `allow_private_ips` when the spec or its `servers` entries legitimately target localhost or your internal network:
```yaml
toolsets:
- type: openapi
url: "http://localhost:8080/openapi.json"
allow_private_ips: true
```
## Properties
| Property | Type | Required | Description |
| ------------------- | ----------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `url` | string | ✓ | URL of the OpenAPI specification (JSON format). Supports `${env.VAR}` interpolation. |
| `headers` | map[string]string | ✗ | Custom HTTP headers sent with every request — both the spec fetch and every generated tool call. Values support `${env.VAR}` and `${headers.NAME}` placeholders (the latter forwards a header from the caller's incoming request when docker agent is exposed as a server). |
| `timeout` | int | ✗ | HTTP client timeout in seconds (default: `30`). Applies to both the spec fetch and the generated tools' requests. |
| `allow_private_ips` | boolean | ✗ | Opt in to dialling **non-public** IP addresses (loopback, RFC1918, link-local — including the cloud-metadata endpoint at `169.254.169.254` — multicast and the unspecified address). Set to `true` only when the spec or its servers legitimately target internal services. By default such addresses are refused at dial time, after DNS resolution, so DNS rebinding cannot bypass the check. |
## How it works
1. The spec is fetched from the configured `url` at startup.
2. Each operation (GET, POST, PUT, …) becomes a separate tool named after its `operationId` (or `method_path` when no `operationId` is set).
3. Path and query parameters are exposed as tool parameters. Request body properties are prefixed with `body_`.
4. Read-only operations (GET, HEAD, OPTIONS) are annotated accordingly.
5. Responses are returned as text; errors include the HTTP status code.
## Limits
- The OpenAPI spec must be **10 MB or less**.
- Individual API responses are truncated at **1 MB**.
## Example
See the full [Pet Store example](https://github.com/docker/docker-agent/blob/main/examples/openapi-petstore.yaml) for a working agent configuration.

View File

@@ -0,0 +1,132 @@
---
title: "Plan Tool"
description: "Shared persistent scratchpad for multi-agent collaboration."
keywords: docker agent, ai agents, tools, toolsets, plan tool
linkTitle: "Plan"
weight: 150
---
_Shared persistent scratchpad for multi-agent collaboration._
## Overview
The plan tool gives agents a shared, persistent scratchpad of named documents. Any agent in a multi-agent config that loads the `plan` toolset can read and write the same plans, and those plans survive across sessions. This makes it straightforward to wire a planner agent that sketches work and one or more executor agents that consume it without any custom tool wiring.
Plans are stored as JSON files in the docker-agent data directory (`~/.cagent/plans/` by default). All agents that share a process serialize on a single mutex so concurrent reads and writes are safe. Writes are atomic (temp file + rename), so a reader never observes partial content.
## Configuration
```yaml
toolsets:
- type: plan
```
No additional options are required. All agents that include `type: plan` in their toolsets share the same plans.
## Available Tools
| Tool | Description |
| ----------------------- | ------------------------------------------------------------------------------------------------- |
| `write_plan` | Create or update a shared plan by name. Replaces the entire plan content — read it first to preserve what you want to keep. Each write bumps the revision number. |
| `read_plan` | Read a shared plan by name, including its title, content, author, status, revision number, and last-updated timestamp. |
| `list_plans` | List all shared plans with their name, title, author, status, revision, and last-updated timestamp. |
| `delete_plan` | Delete a shared plan by name. |
| `update_plan_from_file` | Create or update a plan, taking the new content from a file on disk instead of inline. Use it with `export_plan_to_file` to edit a large plan without re-sending its whole body. |
| `export_plan_to_file` | Write a plan's content to a file. The content goes to disk and is **not** returned as tool output, so materialising a plan costs no tokens. |
| `set_plan_status` | Set a plan's free-form status without rewriting its body. The plan must already exist. |
| `get_plan_status` | Read a plan's status and current revision without fetching its body. |
### Cheap edits with file-based revisions
Re-sending a whole plan on every revision is expensive. The file-based tools let
an agent edit a plan without paying input-token cost for its body:
1. `export_plan_to_file` writes the current plan content to a path. The content
is written to disk and is **not** returned.
2. The agent edits that file in place with its filesystem tools.
3. `update_plan_from_file` commits the file's new contents as the next revision.
### Free-form status
Each plan carries a free-form `status` string. There is no fixed vocabulary:
define your own in the system prompt (e.g. `idle`, `in-progress`, `blocked`,
`done`, `canceled`). Read and write it independently of the body with
`get_plan_status` and `set_plan_status`, or pass `status` to `write_plan` and
`update_plan_from_file`. The TUI surfaces the status next to the plan title.
### Optimistic locking
When several sessions edit the same plan, concurrent writes could silently
overwrite each other. Every read returns a `revision` number; pass the value you
last read as `last_known_revision` to `write_plan`, `update_plan_from_file`,
`set_plan_status`, or `delete_plan`. If the plan changed since (its current
revision no longer matches), the write is rejected with a version-conflict
error and the caller should re-read the plan and retry. Omit
`last_known_revision` to write unconditionally (last writer wins).
### Plan Names
Plan names must match the pattern `[a-z0-9][a-z0-9_-]*` (lowercase letters, digits, `-`, `_`). This is enforced structurally so two different inputs can never collapse onto the same file and path-traversal is impossible by construction.
### Plan Fields
Each plan document contains:
| Field | Description |
| ---------- | --------------------------------------------------------- |
| `name` | The plan's unique slug name |
| `title` | A short human-readable title (optional) |
| `content` | The full Markdown or free-form plan text |
| `author` | Free-form label identifying who last wrote the plan |
| `status` | Free-form lifecycle label (optional), e.g. `in-progress` |
| `revision` | Monotonically increasing version counter, bumped on every write |
| `updatedAt`| ISO 8601 timestamp of the last write |
## Example
Two agents collaborate on a shared plan — the architect drafts it and the builder refines it:
```yaml
agents:
root:
model: anthropic/claude-sonnet-4-5
description: Coordinator
instruction: |
Route work between the architect and the builder.
handoffs: [architect, builder]
architect:
model: anthropic/claude-sonnet-4-5
description: Drafts high-level plans
instruction: |
Use list_plans and read_plan to inspect existing plans, then write_plan
to create or revise one. Always read before writing. When done, hand off
to the builder.
toolsets:
- type: plan
handoffs: [builder]
builder:
model: openai/gpt-4o
description: Adds implementation steps to plans
instruction: |
Read the architect's plan with read_plan, then use write_plan to append
concrete implementation steps. Always read before writing. When done,
hand off back to root.
toolsets:
- type: plan
handoffs: [root]
```
See [`examples/shared_plan.yaml`](https://github.com/docker/docker-agent/blob/main/examples/shared_plan.yaml) for a complete working example.
## Error Handling
- `read_plan` returns a distinct "not found" error when a plan does not exist, as opposed to any other I/O error, so callers can tell "plan missing" from "plan unreadable."
- `list_plans` skips corrupt entries but reports them in a `warnings` field so an agent can detect and recover from a bad state (e.g., by calling `delete_plan`).
- `delete_plan` can remove a corrupt plan to recover from a bad state.
> [!TIP]
> **Plan vs. Todo vs. Tasks**
>
> Use **plan** for shared, free-form documents that multiple agents collaborate on (design docs, requirements, work items). Use [todo](../todo/index.md) for lightweight in-session task lists. Use [tasks](../tasks/index.md) for a structured, persistent task database with priorities and dependencies.

View File

@@ -0,0 +1,264 @@
---
title: "RAG Tool"
description: "Give your agents access to document knowledge bases with background indexing, multiple retrieval strategies, and hybrid search."
keywords: docker agent, ai agents, tools, toolsets, rag tool
linkTitle: "RAG"
weight: 110
aliases:
- /ai/docker-agent/rag/
---
_Give your agents access to document knowledge bases with background indexing, multiple retrieval strategies, and hybrid search._
## Overview
The `rag` toolset lets agents search through your documents to find relevant information before responding. Knowledge bases are declared once at the top of the config under `rag:` and then referenced from any agent via `type: rag, ref: <name>`. docker-agent supports:
- **Background indexing** — Files are indexed automatically and re-indexed on change
- **Multiple strategies** — Semantic embeddings, BM25 keyword search, and LLM-enhanced search
- **Hybrid search** — Combine strategies with result fusion for best results
- **Reranking** — Re-score results with specialized models for improved relevance
## Quick Start
```yaml
rag:
my_docs:
tool:
description: "Technical documentation"
docs: [./documents, ./some-doc.md]
strategies:
- type: chunked-embeddings
embedding_model: openai/text-embedding-3-small
database: ./docs.db
vector_dimensions: 1536
agents:
root:
model: openai/gpt-4o
instruction: |
You have access to a knowledge base. Use it to answer questions.
toolsets:
- type: rag
ref: my_docs
```
## Retrieval Strategies
### Chunked Embeddings (Semantic Search)
Uses embedding models to find semantically similar content. Best for understanding intent, synonyms, and paraphrasing.
```yaml
strategies:
- type: chunked-embeddings
embedding_model: openai/text-embedding-3-small
database: ./vector.db
vector_dimensions: 1536
similarity_metric: cosine_similarity
threshold: 0.5
limit: 10
embedding_batch_size: 50
chunking:
size: 1000
overlap: 100
```
### Semantic Embeddings (LLM-Enhanced)
Uses an LLM to generate semantic summaries of each chunk before embedding, capturing meaning and intent. Best for code search and understanding implementations.
```yaml
strategies:
- type: semantic-embeddings
embedding_model: openai/text-embedding-3-small
vector_dimensions: 1536
chat_model: openai/gpt-4o-mini
database: ./semantic.db
ast_context: true # include AST metadata
chunking:
size: 1000
code_aware: true # AST-aware chunking
```
> [!NOTE]
> **Trade-offs**
>
> Semantic embeddings provide higher quality retrieval but slower indexing (LLM call per chunk) and additional API costs.
### BM25 (Keyword Search)
Traditional keyword matching using the BM25 algorithm. Best for exact terms, technical jargon, and code identifiers.
```yaml
strategies:
- type: bm25
database: ./bm25.db
k1: 1.5 # term frequency saturation
b: 0.75 # length normalization
threshold: 0.3
limit: 10
chunking:
size: 1000
overlap: 100
```
## Hybrid Search
Combine multiple strategies for best results. Strategies run in parallel and results are fused together:
```yaml
rag:
hybrid:
docs: [./docs]
strategies:
- type: chunked-embeddings
embedding_model: openai/text-embedding-3-small
database: ./vector.db
vector_dimensions: 1536
limit: 20
chunking: { size: 1000, overlap: 100 }
- type: bm25
database: ./bm25.db
limit: 15
chunking: { size: 1000, overlap: 100 }
results:
fusion:
strategy: rrf # Reciprocal Rank Fusion
k: 60
deduplicate: true
limit: 5
```
## Fusion Strategies
| Strategy | Best For | Description |
| ---------- | --------------------------------- | ------------------------------------------------------------------ |
| `rrf` | General use (recommended) | Reciprocal Rank Fusion — rank-based, no score normalization needed |
| `weighted` | Known performance characteristics | Weight strategies differently (e.g., embeddings: 0.7, BM25: 0.3) |
| `max` | Same scoring scale | Takes the maximum score from any strategy |
## Reranking
Re-score retrieved documents with a specialized model to improve relevance:
```yaml
results:
reranking:
model: openai/gpt-4o-mini
top_k: 10 # only rerank top 10
threshold: 0.3 # minimum score after reranking
criteria: |
Prioritize official documentation over blog posts.
Prefer recent information and practical examples.
limit: 5
```
Supported reranking providers: **DMR** (native `/rerank` endpoint), **OpenAI**, **Anthropic**, **Gemini**.
## Code-Aware Chunking
For source code, enable AST-based chunking to keep functions and methods intact:
```yaml
chunking:
size: 2000
code_aware: true # Uses tree-sitter for AST-based chunking
```
> [!NOTE]
> **Language Support**
>
> Currently supports Go (`.go`) files. More languages will be added. Falls back to plain text chunking for unsupported file types.
## Debugging RAG
Enable debug logging to see retrieval details:
```bash
$ docker agent run config.yaml --debug --log-file debug.log
```
Look for log tags: `[RAG Manager]`, `[Chunked-Embeddings Strategy]`, `[BM25 Strategy]`, `[RRF Fusion]`, `[Reranker]`.
**Permanent model errors abort early.** If the embedding model, semantic-LLM model, or reranking model returns a permanent error (HTTP 400, 401, 404, or 429 — invalid config, bad auth, unknown model, or rate limit), docker-agent treats the model configuration as invalid and stops immediately rather than retrying doomed requests:
- **Indexing** — the entire indexing run is aborted after the first permanent failure (including 429). The error is surfaced in the logs so you know immediately if a model name or API key is wrong, rather than silently producing incomplete results.
- **Reranking** — a permanent error (including 429) permanently disables the reranker for the lifetime of the manager. Subsequent queries fall back to un-reranked results. Only transient errors (5xx, timeouts) fall back and retry on the next query.
> [!TIP]
> **Examples**
>
> See the [RAG examples](https://github.com/docker/docker-agent/tree/main/examples/rag) in the GitHub repo for complete, runnable configurations.
## Configuration Reference
### Top-Level RAG Fields
| Field | Type | Default | Description |
| ------------- | -------- | ------- | -------------------------------------------------------------- |
| `docs` | []string | — | Document paths/directories (shared across strategies) |
| `description` | string | — | Human-readable description of this RAG source |
| `respect_vcs` | boolean | `true` | Respect `.gitignore` files when indexing documents |
| `strategies` | []object | — | Array of retrieval strategy configurations |
| `results` | object | — | Post-processing: fusion, reranking, deduplication, final limit |
### Chunked-Embeddings Strategy
| Field | Type | Default | Description |
| --------------------------- | ------ | ------------------- | ------------------------------------------------------------ |
| `embedding_model` | string | — | **Required.** Embedding model reference |
| `database` | string | — | Path to local SQLite database |
| `vector_dimensions` | int | — | Embedding dimensions (e.g., 1536 for text-embedding-3-small) |
| `similarity_metric` | string | `cosine_similarity` | Similarity metric |
| `threshold` | float | `0.5` | Minimum similarity score (0–1) |
| `limit` | int | `5` | Max results from this strategy |
| `embedding_batch_size` | int | `50` | Chunks per embedding request |
| `max_embedding_concurrency` | int | `3` | Max concurrent embedding requests |
| `chunking.size` | int | `1500` | Chunk size in characters (`4000` when `code_aware` is set) |
| `chunking.overlap` | int | `75` | Overlap between chunks in characters |
| `chunking.code_aware` | bool | `false` | AST-based chunking (Go files only) |
### Semantic-Embeddings Strategy
| Field | Type | Default | Description |
| -------------------------- | ------ | ---------- | ------------------------------------------------------------------ |
| `embedding_model` | string | — | **Required.** Embedding model reference |
| `chat_model` | string | — | **Required.** LLM for generating semantic summaries |
| `vector_dimensions` | int | — | **Required.** Embedding dimensions |
| `database` | string | — | Path to local SQLite database |
| `semantic_prompt` | string | (built-in) | Custom prompt template (`${path}`, `${content}`, `${ast_context}`) |
| `ast_context` | bool | `false` | Include tree-sitter AST metadata in prompts |
| `threshold` | float | `0.5` | Minimum similarity score (0–1) |
| `limit` | int | `5` | Max results |
| `max_indexing_concurrency` | int | `3` | Max concurrent file indexing |
| `chunking.size` | int | `1500` | Chunk size in characters (`4000` when `code_aware` is set) |
| `chunking.overlap` | int | `75` | Overlap between chunks |
| `chunking.code_aware` | bool | `false` | AST-based chunking |
### BM25 Strategy
| Field | Type | Default | Description |
| ------------------ | ------ | ------- | ----------------------------------------------- |
| `database` | string | — | Path to local SQLite database |
| `k1` | float | `1.5` | Term frequency saturation (1.2–2.0 recommended) |
| `b` | float | `0.75` | Length normalization (0–1) |
| `threshold` | float | `0.0` | Minimum BM25 score |
| `limit` | int | `5` | Max results |
| `chunking.size` | int | `1500` | Chunk size in characters |
| `chunking.overlap` | int | `75` | Overlap between chunks |
### Results (Post-Processing)
| Field | Type | Default | Description |
| --------------------- | ------ | ------- | ----------------------------------------------------------- |
| `fusion.strategy` | string | `rrf` | Fusion method: `rrf`, `weighted`, or `max` |
| `fusion.k` | int | `60` | RRF rank constant |
| `deduplicate` | bool | `true` | Remove duplicate results |
| `limit` | int | `15` | Final number of results |
| `include_score` | bool | `false` | Include relevance scores in results |
| `return_full_content` | bool | `false` | Return full document content instead of just matched chunks |
| `reranking.model` | string | — | Reranking model reference |
| `reranking.top_k` | int | (`limit`) | Only rerank top K results. Defaults to the results `limit` when set. |
| `reranking.threshold` | float | `0.5` | Minimum relevance score after reranking |
| `reranking.criteria` | string | — | Custom relevance guidance for the reranking model |

View File

@@ -0,0 +1,63 @@
---
title: "Script Tool"
description: "Define custom shell scripts as named tools with typed parameters."
keywords: docker agent, ai agents, tools, toolsets, script tool
linkTitle: "Script"
weight: 30
---
_Define custom shell scripts as named tools with typed parameters._
## Overview
The script tool lets you define custom shell scripts as named tools. Unlike the generic [shell tool](../shell/index.md) where the agent writes the command, script tools execute predefined commands — ideal for exposing safe, well-scoped operations with descriptive names.
## Configuration
### Simple Scripts
```yaml
toolsets:
- type: script
shell:
run_tests:
cmd: task test
description: Run the project test suite
lint:
cmd: task lint
description: Run the linter
```
### Scripts with Parameters
Use `${param}` interpolation and JSON Schema to define typed arguments:
```yaml
toolsets:
- type: script
shell:
deploy:
cmd: ./scripts/deploy.sh ${env}
description: Deploy to an environment
args:
env:
type: string
enum: [staging, production]
required: [env]
```
## Properties
| Property | Type | Description |
| --------------------------------- | ------ | ---------------------------------------------------------- |
| `shell.<name>.cmd` | string | Shell command to execute (supports `${arg}` interpolation) |
| `shell.<name>.description` | string | Description shown to the model |
| `shell.<name>.args` | object | Parameter definitions (JSON Schema properties) |
| `shell.<name>.required` | array | Required parameter names |
| `shell.<name>.env` | object | Environment variables for this script |
| `shell.<name>.working_dir` | string | Working directory for script execution |
> [!TIP]
> **Script vs. Shell**
>
> Use the [shell tool](../shell/index.md) when the agent needs to run arbitrary commands. Use the script tool when you want to expose specific, predefined operations with clear names and typed parameters — giving the agent less freedom but more safety.

View File

@@ -0,0 +1,66 @@
---
title: "Session Context Tool"
description: "Reference a previous session as context in the current one."
keywords: docker agent, ai agents, tools, toolsets, session context tool
linkTitle: "Session Context"
weight: 210
---
_Reference a previous session as context, without manual export/import._
## Overview
The `session_context` toolset lets an agent discover earlier sessions and pull one in as context for the current session. It removes the manual workaround of exporting a conversation to HTML and re-attaching it with an `@` mention.
The tool surface is two read-only tools:
| Tool | Description |
| --------------- | ------------------------------------------------------------------------------------------------------------ |
| `list_sessions` | List previous sessions (most recent first) with id, title, creation time and message count. |
| `read_session` | Return the transcript of a previous session, by id or by a relative reference like `-1`. |
The session the agent is currently running in is never listed by `list_sessions` and cannot be read by `read_session` (a circular reference returns an error).
## Configuration
```yaml
toolsets:
- type: session_context
```
No configuration options. Both tools are read-only and operate against the same session store the runtime already uses for persistence.
Restrict the toolset to a subset of tools the standard way:
```yaml
# An agent that may browse but never pull a full transcript into context.
toolsets:
- type: session_context
tools:
- list_sessions
```
## Selecting a session
`read_session` accepts either form:
- A concrete id returned by `list_sessions`, e.g. `read_session("a1b2c3...")`.
- A relative reference: `-1` is the most recent session, `-2` the second most recent, and so on. Relative references resolve against the same ordering `list_sessions` uses (most recent first), excluding sub-sessions.
## Transcript size
A long session could overflow the current context window, so `read_session` caps the rendered transcript. When a transcript is larger than the budget, the oldest messages are dropped (the most recent are usually the most useful for continuing work) and a note records how many were omitted:
```text
[12 earlier message(s) omitted to fit the context budget; showing the most recent 8]
```
## Notes
- `list_sessions` defaults to 20 sessions and is capped at 100; pass `limit` to request fewer.
- `read_session` returns an error when the session is not found, when the reference cannot be resolved, or when it points at the current session.
- Both tools are read-only: they never modify, branch, or delete sessions.
## Example
See [`examples/session_context.yaml`](https://github.com/docker/docker-agent/blob/main/examples/session_context.yaml) for a complete working example.

View File

@@ -0,0 +1,126 @@
---
title: "Session Plan Tool"
description: "Per-session plan tracker for the draft, review, execute workflow."
keywords: docker agent, ai agents, tools, toolsets, session plan tool
linkTitle: "Session Plan"
weight: 160
---
_Per-session plan tracker for the "draft, review, execute" workflow._
## Overview
The `session_plan` toolset gives one agent a place to write a plan for the current session, signal that the plan is ready, and let the host route the next turn to an executing agent.
Different from the [`plan` toolset](../plan/index.md) — `plan` is for shared, named plans multiple agents collaborate on over many sessions. `session_plan` is for one ephemeral plan per session, scoped to that session by ID.
Plans live as Markdown files under:
```text
~/.cagent/session_plans/<session-id>.md
```
The tool surface is three tools:
| Tool | Description |
| -------------------- | ---------------------------------------------------------------------------------------------------- |
| `write_session_plan` | Create or replace this session's plan as markdown. There's exactly one plan per session. |
| `read_session_plan` | Read the plan written for the current session and return it as markdown. |
| `exit_plan_mode` | Signal that the plan is ready for review. Does not switch agents on its own. |
## Configuration
```yaml
toolsets:
- type: session_plan
```
No configuration options. The plan path is derived from the session ID; the agent does not name plans.
Restrict the toolset to a subset of tools the standard way:
```yaml
# An agent that consumes a plan but should not be able to (re)write or finalize one.
toolsets:
- type: session_plan
tools:
- read_session_plan
```
## When to call exit_plan_mode
Call `exit_plan_mode` once the plan is complete and you do not intend to change it on the next turn. It validates that a plan exists for the session and returns a "ready for review" tool result. It does **not** switch agents or solicit user approval on its own — the host application owns the next-turn routing (for example, by reading the tool result, by a UI affordance the user toggles, or by a `handoff` declared on the agent).
This separation keeps the tool reusable across UIs: a CLI that prints tool results inline, a chat UI with a plan-mode toggle, and a server that auto-routes the next turn through a `handoff` can all consume the same signal without one stepping on another.
## Storage and cleanup
- Plans are markdown files written atomically (temp + rename), so concurrent readers — in this process or another — never observe a partial write.
- A best-effort sweep on first use of the toolset removes plan files older than 30 days under the plans directory. Stranded plans for long-gone sessions do not accumulate.
- The session ID identifies the file directly. There is no in-process mutex or revision counter, because two sessions cannot map to the same path.
## Events
A `session_plan_updated` event is emitted whenever `write_session_plan` succeeds:
```json
{
"type": "session_plan_updated",
"session_id": "...",
"path": "/Users/.../.cagent/session_plans/<session-id>.md",
"content": "# my plan\n...",
"agent_name": "planner"
}
```
Embedders that render the plan inline can subscribe and update without re-reading the file.
## Example
A two-agent workflow: `root` executes, `planner` plans. `/plan` hands off to the planner; `exit_plan_mode` signals "ready", and the host decides what happens next.
```yaml
agents:
root:
model: anthropic/claude-sonnet-4-5
description: Executes approved plans
instruction: |
You execute plans the planner has handed off. When you see a message
that a plan has been approved, read it with read_session_plan and work
through its steps in order.
toolsets:
- type: session_plan
tools:
- read_session_plan
- type: filesystem
- type: shell
commands:
plan:
description: "Switch to the planner"
agent: planner
planner:
model: anthropic/claude-sonnet-4-5
description: Investigates and writes plans for review
instruction: |
Investigate the user's request, then write the plan with
write_session_plan. Iterate with the user until the plan is complete,
then call exit_plan_mode to mark it ready for review.
toolsets:
- type: session_plan
- type: filesystem
readonly: true
- type: user_prompt
```
See [`examples/session_plan.yaml`](https://github.com/docker/docker-agent/blob/main/examples/session_plan.yaml) for a complete working example.
## Error Handling
- `read_session_plan` and `exit_plan_mode` return a "no plan written yet" error when called before `write_session_plan`.
- `write_session_plan` validates the session ID and refuses to write anything that could escape the plans directory; in practice the runtime generates UUIDs so this only triggers if an embedder supplies a hand-crafted ID.
> [!TIP]
> **session_plan vs. plan vs. todo vs. tasks**
>
> Use **session_plan** when one agent drafts an approach for the user to review before another agent executes it (ephemeral, one per session). Use [plan](../plan/index.md) for shared, named plans multiple agents collaborate on over many sessions. Use [todo](../todo/index.md) for lightweight in-session task lists. Use [tasks](../tasks/index.md) for a structured, persistent task database with priorities and dependencies.

View File

@@ -0,0 +1,128 @@
---
title: "Shell Tool"
description: "Execute arbitrary shell commands in the user's environment."
keywords: docker agent, ai agents, tools, toolsets, shell tool
linkTitle: "Shell"
weight: 20
---
_Execute arbitrary shell commands in the user's environment._
## Overview
The shell tool allows agents to execute arbitrary shell commands. This is one of the most powerful tools — it lets agents run builds, install dependencies, query APIs, and interact with the system. Each call runs in a fresh, isolated shell session — no state persists between calls.
Commands have a default 30-second timeout and require user confirmation unless `--yolo` is used.
## Configuration
```yaml
toolsets:
- type: shell
```
### Options
| Property | Type | Description |
| -------------- | ------- | --------------------------------------------------------------------------------------------------- |
| `env` | object | Environment variables to set for all shell commands |
| `safer` | boolean | Detect destructive shell commands and force confirmation regardless of `--yolo` or permission rules (see [Safer mode](#safer-mode)). Default `false`. |
| `sudo_askpass` | boolean | Opt in to prompting for a `sudo` password (see [Sudo support](#sudo-support)). Default `false`. |
### Custom Environment Variables
```yaml
toolsets:
- type: shell
env:
MY_VAR: "value"
PATH: "${env.PATH}:/custom/bin"
```
### Safer mode
Set `safer: true` to enable destructive-command detection for the shell toolset:
```yaml
toolsets:
- type: shell
safer: true
```
This auto-registers the [`safer_shell`](../../configuration/hooks/index.md#built-in-hooks) builtin
under `pre_tool_use` with `preempt_yolo: true` so the entry fires
before `Decide()` / `--yolo`. Three behaviors:
- **Destructive matches** (`rm -rf <path>`, `docker volume rm`, `mkfs`, `dd if=… of=/dev/<disk>`, …) get a forced user confirmation carrying a `blast_radius` classification (`low` / `medium` / `high` / `unknown`) and a `category` tag. The TUI confirmation dialog renders the blast radius with a color badge.
- **Known-safe reads** (`ls`, `cat`, `git status`, `git diff`, `docker ps`, `docker logs`, `kubectl get`, …) flow through silently — they're treated as no-opinion and follow the regular approval pipeline (`--yolo`, permission rules, read-only hint).
- **Everything else** asks with `blast_radius=unknown`. Safer mode is conservative by default: unrecognised commands surface to the user before `--yolo` or permission allow-rules can auto-approve them.
The verdict cannot be bypassed by `--yolo` or by a `permission_request` hook that returns `allow` — the `preempt_yolo` lane runs before both. Compound shell (`a && b`, `a; b`, `a | b`) is never matched against the safe allowlist; any destructive segment falls through to ask. The full taxonomy lives in [`pkg/hooks/builtins/safety_patterns.json`](https://github.com/docker/docker-agent/blob/main/pkg/hooks/builtins/safety_patterns.json).
See [`examples/shell_safer.yaml`](https://github.com/docker/docker-agent/blob/main/examples/shell_safer.yaml) for a full example. Under the hood, `safer: true` is a sugar that appends one entry under `hooks.pre_tool_use` with `preempt_yolo: true`; writing the entry by hand achieves the same thing.
### Sudo support
By default a shell command has no controlling terminal, so a `sudo` command that needs a password hangs until it times out (the agent usually gives up and falls back to printing manual instructions).
Set `sudo_askpass: true` to enable a sudo privilege escalation flow:
```yaml
toolsets:
- type: shell
sudo_askpass: true
```
When enabled, `sudo` commands prompt you for your password through the host UI (the input is masked). The password is handed to `sudo` over a private, per-session socket via the standard `SUDO_ASKPASS` mechanism — it is never written to the command line, the logs, or stored by the agent.
The bridge environment variables (`SUDO_ASKPASS`, `CAGENT_ASKPASS_SOCKET`, `CAGENT_ASKPASS_TOKEN`) are added only to commands that invoke `sudo`, but within such a command they are visible to every child process, not just `sudo`. They carry a socket path and a session token, not the password; the socket lives in a `0700` directory, so only your own user can reach it.
Notes and limitations:
- Unix only. The flag has no effect on Windows.
- Interactive UI only. In headless / non-interactive runs the prompt is declined automatically and `sudo` fails as before.
- Only a bare `sudo ...` invocation in a POSIX shell (`sh`, `bash`, `zsh`, ...) is handled. `sudo` called by absolute path (`/usr/bin/sudo`), via `env sudo`, from inside a nested script, or under a non-POSIX shell (e.g. `fish`) is not intercepted and behaves as before.
- Caching is `sudo`'s own. Because each shell tool call runs in a fresh shell with no controlling terminal, `sudo`'s credential cache does not persist across separate tool calls: you are prompted once per shell command that uses `sudo`. Within a single command, multiple `sudo` calls (e.g. `sudo a && sudo b`) usually share one prompt, subject to `sudo`'s own timestamp configuration.
- The prompt must be answered within the command's timeout; raise the `timeout` parameter for `sudo` commands that may wait on input. Background jobs (`run_background_job`) are wired too, but their prompt only works while the originating turn is still active.
- Prompts are serialized: if a single command runs two `sudo` calls in parallel (e.g. `sudo a & sudo b`), the second waits for the first prompt to be answered rather than opening two dialogs at once.
## Available Tools
The shell toolset exposes five tools:
| Tool Name | Description |
| ---------------------- | ---------------------------------------------------------------------------------------------- |
| `shell` | Run a command synchronously and return its combined output when it finishes. |
| `run_background_job` | Start a command asynchronously and return a job ID immediately. Use for servers/watchers/etc. |
| `list_background_jobs` | List all background jobs with their status, runtime, and metadata. |
| `view_background_job` | View the buffered output and status of a specific background job by ID. |
| `stop_background_job` | Stop a running background job. Child processes are terminated too. |
Background job output is captured up to 10 MB per job. All background jobs are automatically terminated when the agent session ends.
### `shell` parameters
| Parameter | Type | Required | Description |
| --------- | ------- | -------- | ------------------------------------------------------------------------- |
| `cmd` | string | ✓ | The shell command to execute. |
| `cwd` | string | ✗ | Working directory to run the command in (default: `.`). |
| `timeout` | integer | ✗ | Per-call execution timeout in seconds (default: `30`). |
### `run_background_job` parameters
| Parameter | Type | Required | Description |
| --------- | ------ | -------- | ----------------------------------------------------------------------- |
| `cmd` | string | ✓ | The shell command to execute in the background. |
| `cwd` | string | ✗ | Working directory to run the command in (default: `.`). |
`view_background_job` and `stop_background_job` each take a single required `job_id` string returned by `run_background_job` or `list_background_jobs`.
> [!WARNING]
> **Safety**
>
> The shell tool gives agents full access to the system shell. Always set `max_iterations` on agents that use the shell tool to prevent infinite loops. A value of 20–50 is typical for development agents. Use [Sandbox Mode](../../configuration/sandbox/index.md) for additional isolation.
> [!NOTE]
> **Tool Confirmation**
>
> By default, docker-agent asks for user confirmation before executing shell commands. Use `--yolo` to auto-approve all tool calls.

View File

@@ -0,0 +1,58 @@
---
title: "Tasks Tool"
description: "Persistent task database with priorities and dependencies, shared across sessions."
keywords: docker agent, ai agents, tools, toolsets, tasks tool
linkTitle: "Tasks"
weight: 180
---
_Persistent task database with priorities and dependencies, shared across sessions._
## Overview
The tasks tool provides a persistent task database that survives across agent sessions. Unlike the [Todo tool](../todo/index.md), which maintains an in-memory task list for the current session only, the tasks tool stores tasks in a JSON file on disk so they can be accessed and updated across multiple sessions. Tasks support priorities and dependencies — a task is _blocked_ until every task it depends on is `done`.
## Configuration
```yaml
toolsets:
- type: tasks
path: ./tasks.json # Optional: custom database path
```
### Options
| Property | Type | Default | Description |
| -------- | ------ | ------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| `path` | string | `tasks.json` | Path to the JSON task database. Relative paths resolve against the agent config directory (or `--working-dir` when set). |
## Available Tools
The tasks toolset exposes these tools:
| Tool Name | Description |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------ |
| `create_task` | Create a new task with a title, description (or markdown file path), optional priority, and optional dependencies. |
| `get_task` | Get full details of a single task by ID, including its effective status (`blocked` if any dependency is not `done`). |
| `update_task` | Update a task's title, description, priority, status, or dependency list. |
| `delete_task` | Delete a task by ID. Also removes it from other tasks' dependency lists. |
| `list_tasks` | List tasks sorted by priority (critical first) with blocked tasks last. Optionally filter by status or priority. |
| `next_task` | Return the highest-priority actionable task — one that is not blocked and not done. Great for "what should I work on?". |
| `add_dependency` | Add a dependency: a task is blocked until the task it depends on is `done`. |
| `remove_dependency` | Remove a dependency from a task. |
## Example
```yaml
agents:
root:
model: openai/gpt-4o
toolsets:
- type: tasks
path: ./project-tasks.json
```
> [!TIP]
> **Tasks vs. Todo**
>
> Use the **tasks** tool when you need persistence across sessions, priorities, or dependencies (e.g., long-running projects, recurring work). Use the [todo tool](../todo/index.md) for ephemeral, session-scoped task lists.

View File

@@ -0,0 +1,29 @@
---
title: "Think Tool"
description: "Step-by-step reasoning scratchpad for planning and decision-making."
keywords: docker agent, ai agents, tools, toolsets, think tool
linkTitle: "Think"
weight: 140
---
_Step-by-step reasoning scratchpad for planning and decision-making._
## Overview
The think tool is a reasoning scratchpad that lets agents think step-by-step before acting. The agent can write its thoughts without producing visible output to the user — ideal for planning complex tasks, breaking down problems, and reasoning through multi-step solutions.
This is a lightweight tool with no side effects. It is most useful for models that lack built-in reasoning or thinking capabilities (e.g., smaller or older models). For models that already support native thinking — such as Claude with extended thinking, OpenAI o-series, or Gemini with a thinking budget — this tool is unnecessary since the model can reason internally.
## Configuration
```yaml
toolsets:
- type: think
```
No configuration options.
> [!TIP]
> **When to use**
>
> Use the think tool with models that don't have native reasoning capabilities. If your model already supports a [thinking budget](../../configuration/models/index.md#thinking-budget), you likely don't need this tool.

View File

@@ -0,0 +1,53 @@
---
title: "Todo Tool"
description: "Task list management for complex multi-step workflows."
keywords: docker agent, ai agents, tools, toolsets, todo tool
linkTitle: "Todo"
weight: 170
---
_Task list management for complex multi-step workflows._
## Overview
The todo tool provides task list management. Agents can create, update, list, and track progress on tasks with status tracking (pending, in-progress, completed). Useful for complex multi-step workflows where the agent needs to stay organized and ensure all steps are completed.
## Available Tools
| Tool | Description |
| -------------- | ---------------------------------------- |
| `create_todo` | Create a new task |
| `create_todos` | Create multiple tasks at once |
| `update_todos` | Update status of one or more tasks |
| `list_todos` | List all current tasks with their status |
### Task Statuses
| Status | Description |
| ------------- | ---------------------------- |
| `pending` | Task has not been started |
| `in-progress` | Task is currently being done |
| `completed` | Task is finished |
## Configuration
```yaml
toolsets:
- type: todo
```
### Options
| Property | Type | Default | Description |
| -------- | ------- | ------- | ----------------------------------------------------------------------- |
| `shared` | boolean | `false` | When `true`, todos are shared across all agents in a multi-agent config |
### Shared Todos
In multi-agent setups, enable shared todos so all agents can see and update the same task list:
```yaml
toolsets:
- type: todo
shared: true
```

View File

@@ -0,0 +1,63 @@
---
title: "Transfer Task Tool"
description: "Delegate tasks to sub-agents in multi-agent setups."
keywords: docker agent, ai agents, tools, toolsets, transfer task tool
linkTitle: "Transfer Task"
weight: 80
---
_Delegate tasks to sub-agents in multi-agent setups._
## Overview
The `transfer_task` tool allows an agent to delegate tasks to specialized sub-agents and receive their results. This is the core mechanism for multi-agent orchestration.
**You don't need to add it manually** — it's automatically available when an agent has `sub_agents` configured.
## Configuration
The tool is enabled implicitly when `sub_agents` is set:
```yaml
agents:
coordinator:
model: openai/gpt-4o
description: Coordinates work across specialists
instruction: Analyze requests and delegate to the right specialist.
sub_agents: [developer, researcher]
developer:
model: anthropic/claude-sonnet-4-5
description: Expert software developer
instruction: Write clean, production-ready code.
toolsets:
- type: filesystem
- type: shell
researcher:
model: openai/gpt-4o
description: Web researcher
instruction: Search for information online.
toolsets:
- type: mcp
ref: docker:duckduckgo
```
The coordinator agent automatically gets a `transfer_task` tool that can delegate to `developer` or `researcher`.
## Tool Interface
The `transfer_task` tool takes three parameters:
| Parameter | Type | Required | Description |
| ----------------- | ------ | -------- | ------------------------------------------------------------------------------------------- |
| `agent` | string | ✓ | Name of the sub-agent to delegate to. Must be listed under the caller's `sub_agents`. |
| `task` | string | ✓ | Clear, concise description of the task the sub-agent should achieve. |
| `expected_output` | string | ✓ | Description of the result/format the caller expects back. |
The call blocks until the sub-agent returns its result, which becomes the tool's response. For non-blocking parallel delegation, use [`background_agents`](../background-agents/index.md) instead.
> [!TIP]
> **See also**
>
> For parallel task delegation, see [Background Agents](../background-agents/index.md). For multi-agent patterns, see [Multi-Agent](../../concepts/multi-agent/index.md).

View File

@@ -0,0 +1,185 @@
---
title: "User Prompt Tool"
description: "Ask the user questions and collect interactive input during agent execution."
keywords: docker agent, ai agents, tools, toolsets, user prompt tool
linkTitle: "User Prompt"
weight: 190
---
_Ask the user questions and collect interactive input during agent execution._
## Overview
The user prompt tool allows agents to ask questions and collect input from users during execution. This enables interactive workflows where the agent needs clarification, confirmation, or additional information before proceeding.
> [!NOTE]
> **When to Use**
>
> - When the agent needs clarification before proceeding
> - Collecting credentials or configuration values
> - Presenting choices and getting user decisions
> - Confirming destructive or important actions
## Configuration
```yaml
agents:
assistant:
model: openai/gpt-4o
description: Interactive assistant
instruction: |
You are a helpful assistant. When you need information
from the user, use the user_prompt tool to ask them.
toolsets:
- type: user_prompt
- type: filesystem
- type: shell
```
## Tool Interface
The `user_prompt` tool takes these parameters:
| Parameter | Type | Required | Description |
| --------- | ------ | -------- | -------------------------------------------------------------------------------------------------- |
| `message` | string | ✓ | The question or prompt to display. |
| `title` | string | ✗ | Optional title for the dialog window in the TUI. Defaults to `"Question"` when not provided. |
| `schema` | object | ✗ | JSON Schema defining the expected response structure (object or primitive). |
## Response Format
The tool returns a JSON response:
```json
{
"action": "accept",
"content": {
"field1": "user value",
"field2": true
}
}
```
### Action Values
| Action | Meaning |
| --------- | ------------------------------------------ |
| `accept` | User provided a response (check `content`) |
| `decline` | User declined to answer |
| `cancel` | User cancelled the prompt |
## Schema Examples
### Simple String Input
```json
{
"type": "string",
"title": "API Key",
"description": "Enter your API key"
}
```
### Multiple Choice
```json
{
"type": "string",
"enum": ["development", "staging", "production"],
"title": "Environment",
"description": "Select the target environment"
}
```
### Boolean Confirmation
```json
{
"type": "boolean",
"title": "Confirm",
"description": "Are you sure you want to proceed?"
}
```
### Object with Multiple Fields
```json
{
"type": "object",
"properties": {
"username": {
"type": "string",
"description": "Your username"
},
"password": {
"type": "string",
"description": "Your password"
},
"remember": {
"type": "boolean",
"description": "Remember credentials"
}
},
"required": ["username", "password"]
}
```
### Number Input
```json
{
"type": "integer",
"title": "Port Number",
"description": "Enter the port number (1024-65535)",
"minimum": 1024,
"maximum": 65535
}
```
## Example Usage
Here's how an agent might use the user prompt tool:
```text
Agent: I need to deploy this application. Let me ask which environment to target.
[Calls user_prompt with message: "Which environment should I deploy to?"
and schema with enum: ["development", "staging", "production"]]
User selects: "staging"
Agent: Great, I'll deploy to staging. Let me confirm this action.
[Calls user_prompt with message: "Deploy to staging? This will replace the current version."
and schema with type: "boolean"]
User confirms: true
Agent: Deploying to staging...
```
## UI Presentation
How the prompt appears depends on the interface:
- **TUI**: Displays an interactive dialog with appropriate input controls
- **CLI (exec mode)**: Prints the prompt and reads from stdin
- **API/MCP**: Returns an elicitation request to the client
> [!TIP]
> **Best Practice**
>
> Provide clear, concise messages. Include context about why you're asking and what the information will be used for. Use schemas with descriptions to guide users on expected input format.
## Handling Responses
The agent should handle all possible actions:
- **accept**: Process the `content` and continue
- **decline**: Acknowledge and try an alternative approach or explain what's needed
- **cancel**: Stop the current operation gracefully
> [!WARNING]
> **Context Requirement**
>
> The user prompt tool requires an elicitation handler to be configured. It works in the TUI and CLI modes but may not be available in all contexts (e.g., some MCP client configurations).

Some files were not shown because too many files have changed in this diff Show More