Configuration ↗
noOriginal Documentation
Documentation Index#
Fetch the complete documentation index at: https://docs.langchain.com/llms.txt Use this file to discover all available pages before exploring further.
Configure the Deep Agents CLI with config.toml, hooks, and MCP servers
The CLI stores its configuration in the ~/.deepagents/ directory. The main config files are:
| File | Format | Purpose |
|---|---|---|
config.toml | TOML | Model defaults, provider settings, constructor params, profile overrides, MCP trust store |
hooks.json | JSON | External tool subscriptions to CLI lifecycle events |
.mcp.json | JSON | MCP server definitions (also auto-discovered from project directories) |
Config file#
~/.deepagents/config.toml lets you customize model providers, set defaults, and pass extra parameters to model constructors.
Default and recent model#
[models]
default = "ollama:qwen3:4b" # your intentional long-term preference
recent = "anthropic:claude-sonnet-4-5" # last /model switch (written automatically)[models].default always takes priority over [models].recent. The /model command only writes to [models].recent, so your configured default is never overwritten by mid-session switches. To remove the default, use /model --default --clear or delete the default key from the config file.
Provider configuration#
Each provider is a TOML table under [models.providers]:
[models.providers.<name>]
models = ["gpt-4o"]
api_key_env = "OPENAI_API_KEY"
base_url = "https://api.openai.com/v1"
class_path = "my_package.models:MyChatModel"
enabled = true
[models.providers.<name>.params]
temperature = 0
max_tokens = 4096
[models.providers.<name>.params."gpt-4o"]
temperature = 0.7Keys:
Models listed here bypass the profile-based filtering criteria and always appear in the switcher. This makes it the recommended way to surface models that are excluded because their profile lacks tool_calling support or doesn’t exist yet.
This key is optional. You can always pass any model name directly to /model or --model regardless of whether it appears in the switcher; the provider validates the name at request time.
Model constructor params#
Any provider can use the params table to pass extra arguments to the model constructor:
[models.providers.ollama.params]
temperature = 0
num_ctx = 8192Per-model overrides#
If a specific model needs different params, add a model-keyed sub-table under params to override individual values without duplicating the entire provider config:
[models.providers.ollama]
models = ["qwen3:4b", "llama3"]
[models.providers.ollama.params]
temperature = 0
num_ctx = 8192
[models.providers.ollama.params."qwen3:4b"]
temperature = 0.5
num_ctx = 4000With this configuration:
ollama:qwen3:4bgets{temperature: 0.5, num_ctx: 4000}— model overrides win.ollama:llama3gets{temperature: 0, num_ctx: 8192}— no override, provider-level params only.
The merge is shallow: any key present in the model sub-table replaces the same key from the provider-level params, while keys only at the provider level are preserved.
CLI overrides with --model-params#
For one-off adjustments without editing the config file, pass a JSON object via --model-params at launch or mid-session with the /model command:
deepagents --model ollama:llama3 --model-params '{"temperature": 0.9, "num_ctx": 16384}'
# In non-interactive mode
deepagents -n "Summarize this repo" --model ollama:llama3 --model-params '{"temperature": 0}'/model --model-params '{"temperature": 0.9}' ollama:llama3
/model --model-params '{"num_ctx": 16384}' # opens selector, applies params to chosen modelThese take the highest priority, overriding values from config file params. Mid-session params are applied for the current session only and are not persisted. --model-params cannot be combined with --default.
Profile overrides#
(Advanced)
Override fields in the model’s runtime profile to change how the CLI interprets model capabilities. The most common use case is lowering max_input_tokens to trigger auto-summarization earlier — useful for testing or for constraining context usage:
# Apply to all models from this provider
[models.providers.anthropic.profile]
max_input_tokens = 4096Per-model sub-tables work the same way as params — the model-level value wins on conflict:
[models.providers.anthropic.profile]
max_input_tokens = 4096
# This model gets a higher limit
[models.providers.anthropic.profile."claude-sonnet-4-5"]
max_input_tokens = 8192Profile overrides are merged into the model’s profile after creation. Any feature that reads the profile — context-limit display in the status bar, auto-summarization thresholds, capability checks — will see the overridden values.
CLI profile overrides with --profile-override#
(Advanced)
To override model profile fields at runtime without editing the config file, pass a JSON object via --profile-override:
deepagents --profile-override '{"max_input_tokens": 4096}'
# Combine with --model
deepagents --model anthropic:claude-sonnet-4-5 --profile-override '{"max_input_tokens": 4096}'
# In non-interactive mode
deepagents -n "Summarize this repo" --profile-override '{"max_input_tokens": 4096}'These are merged on top of config file profile overrides (CLI wins). The priority chain is: model default < config.toml profile < CLI --profile-override.
--profile-override values persist across mid-session /model hot-swaps — switching models re-applies the override to the new model.
Custom base URL#
Some provider packages accept a base_url to override the default endpoint. For example, langchain-ollama defaults to http://localhost:11434 via the underlying ollama client. To point it elsewhere, set base_url in your configuration:
[models.providers.ollama]
base_url = "http://your-host-here:port"Refer to your provider’s reference documentation for compatibility information and additional considerations.
Compatible APIs#
For providers that expose APIs that are wire-compatible with OpenAI or Anthropic, you can use the existing langchain-openai or langchain-anthropic packages by pointing base_url at the provider’s endpoint:
[models.providers.openai]
base_url = "https://api.example.com/v1"
api_key_env = "EXAMPLE_API_KEY"
models = ["my-model"][models.providers.anthropic]
base_url = "https://api.example.com"
api_key_env = "EXAMPLE_API_KEY"
models = ["my-model"]Any features added on top of the official spec by the provider will not be captured. If the provider offers a dedicated LangChain integration package, prefer that instead.
Adding models to the interactive switcher#
Some providers (e.g. langchain-ollama) don’t bundle model profile data (see Provider reference for full listing). When this is the case, the interactive /model switcher won’t list models for that provider. You can fill in the gap by defining a models list in your config file for the provider:
[models.providers.ollama]
models = ["llama3", "mistral", "codellama"]The /model switcher will now include an Ollama section with these models listed.
This is entirely optional. You can always switch to any model by specifying its full name directly:
/model ollama:llama3Arbitrary providers#
You can use any LangChain BaseChatModel subclass using class_path. The CLI imports and instantiates the class directly — no built-in provider package required.
[models.providers.my_custom]
class_path = "my_package.models:MyChatModel"
api_key_env = "MY_API_KEY"
base_url = "https://my-endpoint.example.com"
[models.providers.my_custom.params]
temperature = 0
max_tokens = 4096api_key_env and base_url are optional. class_path providers are expected to handle their own authentication internally — useful when your model uses custom auth (JWT tokens, proprietary headers, mTLS, etc.) rather than a standard API key:
[models.providers.xyz]
class_path = "abc.integrations.deepagents:DeepAgentsXYZChat"
models = ["abc-xyz-1"]
[models.providers.xyz.params]
bypass_auth = true
temperature = 0With this config, switch to the model with /model xyz:abc-xyz-1 or --model xyz:abc-xyz-1.
Deep Agents requires tool calling support. If your custom model supports tool calling but the CLI doesn’t know about it, declare it in the provider profile:
[models.providers.xyz.profile]
tool_calling = true
max_input_tokens = 128000Set max_input_tokens to what your model supports to enable accurate context length tracking and auto-summarization.
The provider package must be installed in the same Python environment as deepagents-cli:
# If deepagents-cli was installed with uv tool:
uv tool install deepagents-cli --with my_packageWhen you switch to my_custom:my-model-v1 (via /model or --model), the model name (my-model-v1) is passed as the model kwarg:
MyChatModel(model="my-model-v1", base_url="...", api_key="...", temperature=0, max_tokens=4096)
class_path executes arbitrary Python code from your config file. This has the same trust model as pyproject.toml build scripts — you control your own machine.
Your provider package may optionally provide model profiles at a _PROFILES dict in <package>.data._profiles in lieu of defining them under the models key. See LangChain model profiles for more info.
External editor#
Press Ctrl+X or type /editor to compose prompts in an external editor. The CLI checks $VISUAL, then $EDITOR, then falls back to vi (macOS/Linux) or notepad (Windows). GUI editors (VS Code, Cursor, Zed, Sublime Text, Windsurf) automatically receive a --wait flag so the CLI blocks until you close the file.
# Set in your shell profile (~/.zshrc, ~/.bashrc, etc.)
export VISUAL="code" # GUI editor (--wait auto-injected)
export EDITOR="nvim" # Terminal fallbackHooks#
Hooks let external programs react to CLI lifecycle events. Configure commands in ~/.deepagents/hooks.json and the CLI pipes a JSON payload to each matching command’s stdin whenever an event fires.
Hooks run fire-and-forget in a background thread — they never block the CLI and failures are logged without interrupting your session.
Setup#
Create ~/.deepagents/hooks.json:
{
"hooks": [
{
"command": ["bash", "-c", "cat >> ~/deepagents-events.log"],
"events": ["session.start", "session.end"]
}
]
}Now every time a session starts or ends, the CLI appends the event payload to ~/deepagents-events.log.
Hook configuration#
The config file contains a single hooks array. Each entry has:
| Field | Type | Required | Description |
|---|---|---|---|
command | list[str] | Yes | Command and arguments to run (no shell expansion — use ["bash", "-c", "..."] if needed) |
events | list[str] | No | Event names to subscribe to. Omit or leave empty to receive all events |
{
"hooks": [
{
"command": ["python3", "my_handler.py"],
"events": ["session.start", "task.complete"]
},
{
"command": ["bash", "log_everything.sh"]
}
]
}The second hook above has no events filter, so it receives every event the CLI emits.
Payload format#
Each hook command receives a JSON object on stdin with an "event" key plus event-specific fields:
{
"event": "session.start",
"thread_id": "abc123"
}Events reference#
session.start#
Fired when an agent session begins (both interactive and non-interactive modes).
| Field | Type | Description |
|---|---|---|
thread_id | string | The session thread identifier |
session.end#
Fired when a session exits.
| Field | Type | Description |
|---|---|---|
thread_id | string | The session thread identifier |
user.prompt#
Fired in interactive mode when the user submits a chat message.
No additional fields.
input.required#
Fired when the agent requires human input (human-in-the-loop interrupt).
No additional fields.
permission.request#
Fired before the approval dialog when one or more tool calls need user permission.
| Field | Type | Description |
|---|---|---|
tool_names | list[str] | Names of the tools requesting approval |
tool.error#
Fired when a tool call returns an error.
| Field | Type | Description |
|---|---|---|
tool_names | list[str] | Names of the tool(s) that errored |
task.complete#
Fired when the agent finishes its current task (the streaming loop ends without further interrupts).
| Field | Type | Description |
|---|---|---|
thread_id | string | The session thread identifier |
context.compact#
Fired before the CLI compacts (summarizes) the conversation context.
No additional fields.
Execution model#
- Background thread: Hook subprocesses run in a thread via
asyncio.to_threadso the main event loop is never blocked. - Concurrent dispatch: When multiple hooks match an event, they run concurrently in a thread pool.
- 5-second timeout: Each command has a 5-second timeout. Commands that exceed this are killed.
- Fire-and-forget: Errors are caught per-hook and logged at debug/warning level. A failing hook never crashes or stalls the CLI.
- Lazy loading: The config file is read once on the first event dispatch and cached for the rest of the session.
- No shell expansion: Commands are executed directly (not through a shell). Wrap in
["bash", "-c", "..."]if you need shell features like pipes or variable expansion.
Hook examples#
Log all events to a file#
{
"hooks": [
{
"command": ["bash", "-c", "jq -c . >> ~/.deepagents/hook-events.jsonl"],
"events": []
}
]
}Desktop notification on task completion (macOS)#
{
"hooks": [
{
"command": [
"bash", "-c",
"osascript -e 'display notification \"Agent finished\" with title \"Deep Agents\"'"
],
"events": ["task.complete"]
}
]
}Python handler#
Write a handler script that reads the JSON payload from stdin:
import json
import sys
payload = json.load(sys.stdin)
event = payload["event"]
if event == "session.start":
print(f"Session started: {payload['thread_id']}", file=sys.stderr)
elif event == "permission.request":
print(f"Approval needed for: {payload['tool_names']}", file=sys.stderr){
"hooks": [
{
"command": ["python3", "my_handler.py"],
"events": ["session.start", "permission.request"]
}
]
}Security considerations#
Hooks follow the same trust model as Git hooks or shell aliases — any user who can write to ~/.deepagents/hooks.json can execute arbitrary commands. This is by design:
- No command injection: Payload data flows only to stdin as JSON, never to command-line arguments.
json.dumpshandles escaping. - No shell by default: Commands run with
shell=False, preventing shell injection. - Malformed config: Invalid JSON or unexpected types produce logged warnings, not security issues.
Only add hooks from sources you trust. A hook has the same permissions as your user account.
Edit this page on GitHub or file an issue.
Connect these docs to Claude, VSCode, and more via MCP for real-time answers.