| title |
Tool Hooks |
| description |
Block dangerous commands, lint after edits, and set up your environment |
Tool hooks are experimental. Expect breaking changes while we iterate.
**The easiest way to set up tool hooks is to ask Mux.** Just tell Mux what you want (e.g., "lint
Python files after edits" or "block force pushes") and it will create the hook scripts for you.
Tool hooks let you run your own scripts before and after Mux tool executions.
Prevent force pushes, rm -rf, etc.
Run ruff, eslint, tsc after changes
direnv, nvm, virtualenv
Create .mux/tool_pre to validate commands before they run. Exit non-zero to block:
#!/usr/bin/env bash
# .mux/tool_pre - runs before every tool
if [[ "$MUX_TOOL" == "bash" ]]; then
script="$MUX_TOOL_INPUT_SCRIPT"
if echo "$script" | grep -q 'push.*--force'; then
echo "❌ Force push blocked" >&2
exit 1
fi
if echo "$script" | grep -q 'rm -rf /'; then
echo "❌ Dangerous rm blocked" >&2
exit 1
fi
fi
exit 0 # Allow tool to run
The agent sees your error message and can adjust its approach.
Create .mux/tool_post to run validation after tools complete:
#!/usr/bin/env bash
# .mux/tool_post - runs after every tool
set -euo pipefail
[[ "$MUX_TOOL" == file_edit_* ]] || exit 0
file="${MUX_TOOL_INPUT_FILE_PATH:-}"
[[ -n "$file" ]] || exit 0
case "$file" in
*.py)
ruff check "$file"
;;
*.ts|*.tsx)
npx tsc --noEmit "$file"
;;
esac
Minimal formatter example (Go)
#!/usr/bin/env bash
# .mux/tool_post - runs after every tool
set -euo pipefail
[[ "$MUX_TOOL" == file_edit_* ]] || exit 0
file="${MUX_TOOL_INPUT_FILE_PATH:-}"
[[ "$file" == *.go ]] || exit 0
gofmt -w "$file"
Lint errors appear in hook_output and the agent can fix them.
`hook_output` is only shown in the UI when the hook produces output. For a cleaner experience,
only print output when the hook has an effect—e.g., skip "Formatted: file" messages if the file
was already formatted.
Create .mux/tool_env to configure your shell environment. This file is sourced before every bash tool call:
# .mux/tool_env - sourced before bash commands
# direnv
eval "$(direnv export bash 2>/dev/null)" || true
# nvm
# export NVM_DIR="$HOME/.nvm"
# [ -s "$NVM_DIR/nvm.sh" ] && source "$NVM_DIR/nvm.sh"
# Python virtualenv
# source .venv/bin/activate 2>/dev/null || true
Unlike hooks, `tool_env` doesn't need to be executable—it's sourced, not run. It only affects
`bash` tools.
All hooks receive these environment variables:
| Variable |
Description |
MUX_TOOL |
Tool name: bash, file_edit_replace_string, file_read, etc. |
MUX_WORKSPACE_ID |
Current workspace identifier |
MUX_TOOL_INPUT_PATH |
Path to file containing full tool input (always set) |
Mux flattens the tool input into MUX_TOOL_INPUT_<...> environment variables (see the appendix below). Fields longer than 8KB are omitted—use MUX_TOOL_INPUT_PATH for full access.
Post-hook only (tool_post):
| Variable |
Description |
MUX_TOOL_RESULT_PATH |
Path to file containing full tool result |
Flattened result fields are available as MUX_TOOL_RESULT_<...>. Fields longer than 8KB are omitted—use MUX_TOOL_RESULT_PATH for full access.
| Exit Code |
tool_pre behavior |
tool_post behavior |
0 |
Tool executes normally |
Success, output shown to agent |
| Non-zero |
Tool blocked, error shown to agent |
Failure, error shown in hook_output |
Mux searches for each hook file in this order:
- Project-level:
.mux/<hook>
- User-level:
~/.mux/<hook>
This applies to tool_pre, tool_post, and tool_env.
For SSH workspaces, hooks execute on the remote machine.
Hooks must complete within 10 seconds or they're terminated. Long-running tools (builds, tests) don't count against this—only hook execution time.
Keep hooks fast—if you need longer operations, consider running them asynchronously or in the background.
| Feature |
.mux/tool_pre |
.mux/tool_post |
.mux/tool_env |
| Purpose |
Block dangerous commands |
Lint/validate results |
Environment setup |
| Runs |
Before tool |
After tool |
Sourced in bash shell |
| Applies to |
All tools |
All tools |
bash tool only |
| Use case |
Block force-push |
Run ruff/eslint |
direnv, nvm, virtualenv |
Mux also provides flattened tool input env vars so hook scripts can stay compact.
- Scalars become
MUX_TOOL_INPUT_<FIELD>
- Nested objects become
MUX_TOOL_INPUT_<PARENT>_<CHILD>
- Arrays also include
..._COUNT and per-index variables like ..._<INDEX>
If a value is too large for the environment, it may be omitted (not set). Mux also caps the number of flattened env vars and array elements to keep hook execution reliable.
{/* BEGIN TOOL_HOOK_ENV_VARS */}
advisor (1)
| Env var |
JSON path |
Type |
Description |
MUX_TOOL_INPUT_QUESTION |
question |
string |
— |
agent_report (2)
| Env var |
JSON path |
Type |
Description |
MUX_TOOL_INPUT_REPORT_MARKDOWN |
reportMarkdown |
string |
— |
MUX_TOOL_INPUT_TITLE |
title |
string |
— |
agent_skill_delete (4)
| Env var |
JSON path |
Type |
Description |
MUX_TOOL_INPUT_CONFIRM |
confirm |
boolean |
Must be true to confirm deletion |
MUX_TOOL_INPUT_FILE_PATH |
filePath |
string |
Relative file path within the skill directory to delete. Required when target is 'file' |
MUX_TOOL_INPUT_NAME |
name |
string |
Skill name to delete |
MUX_TOOL_INPUT_TARGET |
target |
enum |
Deletion target: 'file' to delete a specific file, 'skill' to remove the entire skill directory (defaults to file) |
agent_skill_list (1)
| Env var |
JSON path |
Type |
Description |
MUX_TOOL_INPUT_INCLUDE_UNADVERTISED |
includeUnadvertised |
boolean |
When true, includes skills with advertise: false |
agent_skill_read (1)
| Env var |
JSON path |
Type |
Description |
MUX_TOOL_INPUT_NAME |
name |
string |
Skill name (directory name under the skills root) |
agent_skill_read_file (4)
| Env var |
JSON path |
Type |
Description |
MUX_TOOL_INPUT_FILE_PATH |
filePath |
string |
Path to the file within the skill directory (relative) |
MUX_TOOL_INPUT_LIMIT |
limit |
number |
Number of lines to return from offset (optional, returns all if not specified) |
MUX_TOOL_INPUT_NAME |
name |
string |
Skill name (directory name under the skills root) |
MUX_TOOL_INPUT_OFFSET |
offset |
number |
1-based starting line number (optional, defaults to 1) |
agent_skill_write (3)
| Env var |
JSON path |
Type |
Description |
MUX_TOOL_INPUT_CONTENT |
content |
string |
File content to write |
MUX_TOOL_INPUT_FILE_PATH |
filePath |
string |
Relative path within skill directory. Defaults to SKILL.md |
MUX_TOOL_INPUT_NAME |
name |
string |
Skill name (directory name under the global skills root) |
analytics_query (6)
| Env var |
JSON path |
Type |
Description |
MUX_TOOL_INPUT_SQL |
sql |
string |
DuckDB SQL query to execute |
MUX_TOOL_INPUT_TITLE |
title |
string |
Optional chart title |
MUX_TOOL_INPUT_VISUALIZATION |
visualization |
enum |
Optional visualization type for rendering the query result |
MUX_TOOL_INPUT_X_AXIS |
x_axis |
string |
Optional column name for the visualization X axis |
MUX_TOOL_INPUT_Y_AXIS_<INDEX> |
y_axis[<INDEX>] |
string |
Optional column name(s) for the visualization Y axis |
MUX_TOOL_INPUT_Y_AXIS_COUNT |
y_axis.length |
number |
Number of elements in y_axis (Optional column name(s) for the visualization Y axis) |
ask_user_question (8)
| Env var |
JSON path |
Type |
Description |
MUX_TOOL_INPUT_ANSWERS_<KEY> |
answers[<KEY>] |
string |
— |
MUX_TOOL_INPUT_QUESTIONS_<INDEX>_HEADER |
questions[<INDEX>].header |
string |
Short label shown in the UI (keep it concise) |
MUX_TOOL_INPUT_QUESTIONS_<INDEX>_MULTI_SELECT |
questions[<INDEX>].multiSelect |
boolean |
— |
MUX_TOOL_INPUT_QUESTIONS_<INDEX>_OPTIONS_<INDEX>_DESCRIPTION |
questions[<INDEX>].options[<INDEX>].description |
string |
— |
MUX_TOOL_INPUT_QUESTIONS_<INDEX>_OPTIONS_<INDEX>_LABEL |
questions[<INDEX>].options[<INDEX>].label |
string |
— |
MUX_TOOL_INPUT_QUESTIONS_<INDEX>_OPTIONS_COUNT |
questions[<INDEX>].options.length |
number |
Number of elements in questions[<INDEX>].options |
MUX_TOOL_INPUT_QUESTIONS_<INDEX>_QUESTION |
questions[<INDEX>].question |
string |
— |
MUX_TOOL_INPUT_QUESTIONS_COUNT |
questions.length |
number |
Number of elements in questions |
attach_file (3)
| Env var |
JSON path |
Type |
Description |
MUX_TOOL_INPUT_FILENAME |
filename |
string |
Optional filename override to present to the model. |
MUX_TOOL_INPUT_MEDIA_TYPE |
mediaType |
string |
Optional media type override when the filename/extension is ambiguous. |
MUX_TOOL_INPUT_PATH |
path |
string |
The path to the file to attach (absolute or relative) |
bash (5)
| Env var |
JSON path |
Type |
Description |
MUX_TOOL_INPUT_DISPLAY_NAME |
display_name |
string |
Human-readable name for the process (e.g., 'Dev Server', 'TypeCheck Watch'). Required for all bash invocations since any process can be sent to background. |
MUX_TOOL_INPUT_MODEL_INTENT |
model_intent |
string |
Optional. Short user-facing purpose for this command, shown next to the command in collapsed chat. Use a present-participle phrase in plain English, under 100 characters. Do not repeat the command or include duration, because Mux appends those. Examples: 'Running the unit tests', 'Checking repository state', 'Inspecting build output'. |
MUX_TOOL_INPUT_RUN_IN_BACKGROUND |
run_in_background |
boolean |
Run this command in the background without blocking. Use for processes running >5s (dev servers, builds, file watchers). Do NOT use for quick commands (<5s), interactive processes (no stdin support), or processes requiring real-time output (use foreground with larger timeout instead). Returns immediately with a taskId (bash:<processId>) and backgroundProcessId. Read output with task_await (returns only new output since last check). Terminate with task_terminate using the taskId. List active tasks with task_list. Process persists until timeout_secs expires, terminated, or workspace is removed. For long-running tasks like builds or compilations, prefer background mode to continue productive work in parallel. Raw background bash does not automatically wake the parent workspace when it prints output or exits; use task_await when you need output, or wrap the script in a background task/workflow monitor when wake-on-condition behavior is required. Do not call task_await in the same parallel tool-call batch; wait for the returned taskId first. When you actually need the output, read it with task_await; do not poll task_await just because the process is still running. |
MUX_TOOL_INPUT_SCRIPT |
script |
string |
The bash script/command to execute |
MUX_TOOL_INPUT_TIMEOUT_SECS |
timeout_secs |
number |
Timeout in seconds. For foreground: max execution time before kill. For background: max lifetime before auto-termination. Start small and increase on retry; avoid large initial values to keep UX responsive |
bash_background_terminate (1)
| Env var |
JSON path |
Type |
Description |
MUX_TOOL_INPUT_PROCESS_ID |
process_id |
string |
Background process ID to terminate |
bash_output (4)
| Env var |
JSON path |
Type |
Description |
MUX_TOOL_INPUT_FILTER |
filter |
string |
Optional regex to filter output lines. By default, only matching lines are returned. When filter_exclude is true, matching lines are excluded instead. Non-matching lines are permanently discarded and cannot be retrieved later. |
MUX_TOOL_INPUT_FILTER_EXCLUDE |
filter_exclude |
boolean |
When true, lines matching 'filter' are excluded instead of kept. Key behavior: excluded lines do NOT cause early return from timeout - waiting continues until non-excluded output arrives or process exits. Use to avoid busy polling on progress spam (e.g., filter='⏳|waiting|...' with filter_exclude=true lets you set a long timeout and only wake on meaningful output). Requires 'filter' to be set. |
MUX_TOOL_INPUT_PROCESS_ID |
process_id |
string |
The ID of the background process to retrieve output from |
MUX_TOOL_INPUT_TIMEOUT_SECS |
timeout_secs |
number |
Seconds to wait for new output. If no output is immediately available and process is still running, blocks up to this duration. Returns early when output arrives or process exits. Only use long timeouts (>15s) when no other useful work can be done in parallel. |
code_execution (1)
| Env var |
JSON path |
Type |
Description |
MUX_TOOL_INPUT_CODE |
code |
string |
JavaScript code to execute in the PTC sandbox |
complete_goal (2)
| Env var |
JSON path |
Type |
Description |
MUX_TOOL_INPUT_GOAL_ID |
goalId |
string |
Optional optimistic-concurrency token. Pass the goalId returned by get\_goal to ensure the completion is rejected with a typed conflict error if the user clears or replaces the goal mid-stream. |
MUX_TOOL_INPUT_SUMMARY |
summary |
string |
Required 1-2 sentence justification for completing the current goal. |
desktop_click (3)
| Env var |
JSON path |
Type |
Description |
MUX_TOOL_INPUT_BUTTON |
button |
enum |
Optional mouse button to click. Defaults to left. |
MUX_TOOL_INPUT_X |
x |
number |
Target X coordinate in screen pixels. |
MUX_TOOL_INPUT_Y |
y |
number |
Target Y coordinate in screen pixels. |
desktop_double_click (3)
| Env var |
JSON path |
Type |
Description |
MUX_TOOL_INPUT_BUTTON |
button |
enum |
Optional mouse button to double-click. Defaults to left. |
MUX_TOOL_INPUT_X |
x |
number |
Target X coordinate in screen pixels. |
MUX_TOOL_INPUT_Y |
y |
number |
Target Y coordinate in screen pixels. |
desktop_drag (4)
| Env var |
JSON path |
Type |
Description |
MUX_TOOL_INPUT_END_X |
endX |
number |
Ending X coordinate in screen pixels. |
MUX_TOOL_INPUT_END_Y |
endY |
number |
Ending Y coordinate in screen pixels. |
MUX_TOOL_INPUT_START_X |
startX |
number |
Starting X coordinate in screen pixels. |
MUX_TOOL_INPUT_START_Y |
startY |
number |
Starting Y coordinate in screen pixels. |
desktop_key_press (1)
| Env var |
JSON path |
Type |
Description |
MUX_TOOL_INPUT_KEY |
key |
string |
Key or key combination to press on the desktop. |
desktop_move_mouse (2)
| Env var |
JSON path |
Type |
Description |
MUX_TOOL_INPUT_X |
x |
number |
Target X coordinate in screen pixels. |
MUX_TOOL_INPUT_Y |
y |
number |
Target Y coordinate in screen pixels. |
desktop_screenshot (2)
| Env var |
JSON path |
Type |
Description |
MUX_TOOL_INPUT_SCALED_HEIGHT |
scaledHeight |
number |
Optional scaled height hint in pixels for downstream consumers. |
MUX_TOOL_INPUT_SCALED_WIDTH |
scaledWidth |
number |
Optional scaled width hint in pixels for downstream consumers. |
desktop_scroll (4)
| Env var |
JSON path |
Type |
Description |
MUX_TOOL_INPUT_DELTA_X |
deltaX |
number |
Optional horizontal scroll delta in pixels. |
MUX_TOOL_INPUT_DELTA_Y |
deltaY |
number |
Vertical scroll delta in pixels. |
MUX_TOOL_INPUT_X |
x |
number |
Target X coordinate in screen pixels. |
MUX_TOOL_INPUT_Y |
y |
number |
Target Y coordinate in screen pixels. |
desktop_type (1)
| Env var |
JSON path |
Type |
Description |
MUX_TOOL_INPUT_TEXT |
text |
string |
Text to type into the active desktop target. |
file_edit_insert (4)
| Env var |
JSON path |
Type |
Description |
MUX_TOOL_INPUT_CONTENT |
content |
string |
The content to insert |
MUX_TOOL_INPUT_INSERT_AFTER |
insert_after |
string |
Anchor text to insert after. Content will be placed immediately after this substring. |
MUX_TOOL_INPUT_INSERT_BEFORE |
insert_before |
string |
Anchor text to insert before. Content will be placed immediately before this substring. |
MUX_TOOL_INPUT_PATH |
path |
string |
Path to the file to edit (absolute or relative to the current workspace) |
file_edit_replace_lines (7)
| Env var |
JSON path |
Type |
Description |
MUX_TOOL_INPUT_END_LINE |
end_line |
number |
1-indexed end line (inclusive) to replace |
MUX_TOOL_INPUT_EXPECTED_LINES_<INDEX> |
expected_lines[<INDEX>] |
string |
Optional safety check. When provided, the current lines in the specified range must match exactly. |
MUX_TOOL_INPUT_EXPECTED_LINES_COUNT |
expected_lines.length |
number |
Number of elements in expected_lines (Optional safety check. When provided, the current lines in the specified range must match exactly.) |
MUX_TOOL_INPUT_NEW_LINES_<INDEX> |
new_lines[<INDEX>] |
string |
Replacement lines. Provide an empty array to delete the specified range. |
MUX_TOOL_INPUT_NEW_LINES_COUNT |
new_lines.length |
number |
Number of elements in new_lines (Replacement lines. Provide an empty array to delete the specified range.) |
MUX_TOOL_INPUT_PATH |
path |
string |
Path to the file to edit (absolute or relative to the current workspace) |
MUX_TOOL_INPUT_START_LINE |
start_line |
number |
1-indexed start line (inclusive) to replace |
file_edit_replace_string (4)
| Env var |
JSON path |
Type |
Description |
MUX_TOOL_INPUT_NEW_STRING |
new_string |
string |
The replacement text |
MUX_TOOL_INPUT_OLD_STRING |
old_string |
string |
The exact text to replace (must be unique in file if replace_count is 1). Include enough context (indentation, surrounding lines) to make it unique. |
MUX_TOOL_INPUT_PATH |
path |
string |
Path to the file to edit (absolute or relative to the current workspace) |
MUX_TOOL_INPUT_REPLACE_COUNT |
replace_count |
number |
Number of occurrences to replace (default: 1). Use -1 to replace all occurrences. If 1, old_string must be unique in the file. |
file_read (3)
| Env var |
JSON path |
Type |
Description |
MUX_TOOL_INPUT_LIMIT |
limit |
number |
Number of lines to return from offset (optional, returns all if not specified) |
MUX_TOOL_INPUT_OFFSET |
offset |
number |
1-based starting line number (optional, defaults to 1) |
MUX_TOOL_INPUT_PATH |
path |
string |
The path to the file to read (absolute or relative) |
heartbeat (5)
| Env var |
JSON path |
Type |
Description |
MUX_TOOL_INPUT_ACTION |
action |
enum |
Operation to perform: "get" reads the current heartbeat, "set" enables or configures it, and "unset" removes this workspace's heartbeat settings. |
MUX_TOOL_INPUT_CONTEXT_MODE |
contextMode |
enum |
set: context preparation for heartbeat turns: "normal" uses current context, "compact" compacts first, and "reset" appends a reset boundary first. Omit to preserve the current mode. |
MUX_TOOL_INPUT_ENABLED |
enabled |
boolean |
set: whether scheduled heartbeats are enabled. Omit to preserve the current value; when creating new settings, omitted means "enabled". |
MUX_TOOL_INPUT_INTERVAL_MS |
intervalMs |
number |
set: heartbeat interval in milliseconds (300000–86400000). Omit to preserve the current interval or use the global default for new settings. |
MUX_TOOL_INPUT_MESSAGE |
message |
string |
set: optional custom instruction body appended after the fixed idle-workspace lead-in. Pass an empty string to clear the custom message. |
memory (11)
| Env var |
JSON path |
Type |
Description |
MUX_TOOL_INPUT_COMMAND |
command |
enum |
The memory operation to perform. |
MUX_TOOL_INPUT_FILE_TEXT |
file_text |
string |
create: full contents of the new file. |
MUX_TOOL_INPUT_INSERT_LINE |
insert_line |
number |
insert: line number to insert after (0 = top of file). |
MUX_TOOL_INPUT_INSERT_TEXT |
insert_text |
string |
insert: text to insert. |
MUX_TOOL_INPUT_LIMIT |
limit |
number |
view on a file: number of lines to return from offset (optional). |
MUX_TOOL_INPUT_NEW_PATH |
new_path |
string |
rename: new virtual path (same scope). |
MUX_TOOL_INPUT_NEW_STR |
new_str |
string |
str_replace: replacement text. |
MUX_TOOL_INPUT_OFFSET |
offset |
number |
view on a file: 1-based starting line number (optional). |
MUX_TOOL_INPUT_OLD_PATH |
old_path |
string |
rename: current virtual path. |
MUX_TOOL_INPUT_OLD_STR |
old_str |
string |
str_replace: exact text to replace (must be unique in the file). |
MUX_TOOL_INPUT_PATH |
path |
string |
Virtual memory path (e.g. /memories/global/notes.md). Required for every command except rename. |
mux_agents_write (2)
| Env var |
JSON path |
Type |
Description |
MUX_TOOL_INPUT_CONFIRM |
confirm |
boolean |
Must be true to apply the write. The agent should ask the user for confirmation first. |
MUX_TOOL_INPUT_NEW_CONTENT |
newContent |
string |
The full new contents of the AGENTS.md file |
mux_config_read (3)
| Env var |
JSON path |
Type |
Description |
MUX_TOOL_INPUT_FILE |
file |
enum |
Which configuration file to read |
MUX_TOOL_INPUT_PATH_<INDEX> |
path[<INDEX>] |
string |
Optional path segments to read a specific nested value. If omitted, returns the full config. |
MUX_TOOL_INPUT_PATH_COUNT |
path.length |
number |
Number of elements in path (Optional path segments to read a specific nested value. If omitted, returns the full config.) |
mux_config_write (7)
| Env var |
JSON path |
Type |
Description |
MUX_TOOL_INPUT_CONFIRM |
confirm |
boolean |
Must be true to apply the write. Ask the user for confirmation first. |
MUX_TOOL_INPUT_FILE |
file |
enum |
Which configuration file to write |
MUX_TOOL_INPUT_OPERATIONS_<INDEX>_OP |
operations[<INDEX>].op |
literal |
— |
MUX_TOOL_INPUT_OPERATIONS_<INDEX>_PATH_<INDEX> |
operations[<INDEX>].path[<INDEX>] |
string |
— |
MUX_TOOL_INPUT_OPERATIONS_<INDEX>_PATH_COUNT |
operations[<INDEX>].path.length |
number |
Number of elements in operations[<INDEX>].path |
MUX_TOOL_INPUT_OPERATIONS_<INDEX>_VALUE |
operations[<INDEX>].value |
unknown |
— |
MUX_TOOL_INPUT_OPERATIONS_COUNT |
operations.length |
number |
Number of elements in operations (Operations to apply to the config document) |
notify (2)
| Env var |
JSON path |
Type |
Description |
MUX_TOOL_INPUT_MESSAGE |
message |
string |
Optional notification body with more details (max 200 chars). Keep it brief - users may only see a preview. |
MUX_TOOL_INPUT_TITLE |
title |
string |
Short notification title (max 64 chars). Should be concise and actionable. |
review_pane_update (4)
| Env var |
JSON path |
Type |
Description |
MUX_TOOL_INPUT_HUNKS_<INDEX>_COMMENT |
hunks[<INDEX>].comment |
string |
Short note (~1 sentence) telling the user what to look at and why. |
MUX_TOOL_INPUT_HUNKS_<INDEX>_PATH |
hunks[<INDEX>].path |
string |
Filter in path[:range] form, e.g. "src/foo.ts" or "src/foo.ts:42-58". Path is project-relative; use './' or '../' when the path must resolve from the current tool working directory. Range uses new-file line numbers (inclusive). |
MUX_TOOL_INPUT_HUNKS_COUNT |
hunks.length |
number |
Number of elements in hunks (List of hunks to flag for review.) |
MUX_TOOL_INPUT_OPERATION |
operation |
enum |
'replace' overwrites the assisted set; 'add' appends to it. |
set_goal (5)
| Env var |
JSON path |
Type |
Description |
MUX_TOOL_INPUT_BUDGET_CENTS |
budgetCents |
number |
Optional positive budget in cents. Omit/null to apply the effective workspace goal default. |
MUX_TOOL_INPUT_EXPECTED_GOAL_ID |
expectedGoalId |
string |
Optimistic-concurrency token required when replacing an active, paused, or budget-limited goal. Use the goalId from get_goal. |
MUX_TOOL_INPUT_OBJECTIVE |
objective |
string |
Concrete, measurable objective to pursue over automatic goal continuations. |
MUX_TOOL_INPUT_REPLACE_EXISTING_GOAL |
replaceExistingGoal |
boolean |
Set true only when the user explicitly asked to replace the current goal. |
MUX_TOOL_INPUT_TURN_CAP |
turnCap |
number |
Optional positive maximum automatic continuation turns. Omit/null to apply the effective workspace goal default. |
skills_catalog_read (3)
| Env var |
JSON path |
Type |
Description |
MUX_TOOL_INPUT_OWNER |
owner |
string |
GitHub owner from the search result (e.g. 'vercel-labs') |
MUX_TOOL_INPUT_REPO |
repo |
string |
GitHub repository name from the search result (e.g. 'agent-skills') |
MUX_TOOL_INPUT_SKILL_ID |
skillId |
string |
Skill ID from the search result |
skills_catalog_search (2)
| Env var |
JSON path |
Type |
Description |
MUX_TOOL_INPUT_LIMIT |
limit |
number |
Maximum number of results to return (default: 10) |
MUX_TOOL_INPUT_QUERY |
query |
string |
Search query to find skills in the catalog |
task (18)
| Env var |
JSON path |
Type |
Description |
MUX_TOOL_INPUT_AGENT_ID |
agentId |
string |
— |
MUX_TOOL_INPUT_ISOLATION |
isolation |
enum |
Workspace isolation for the sub-agent. "fork" (the default) runs it in an isolated copy of this workspace created from committed state. "none" runs it directly in this workspace's checkout, sharing the working tree (including uncommitted changes) and skipping the fork + init overhead. Use "none" only for read-only analysis (e.g. the explore agent) or when you instruct the sub-agent to avoid editing shared files, since it can otherwise modify the same files concurrently. Omit to fork. |
MUX_TOOL_INPUT_KIND |
kind |
enum |
Task kind. Omit or use "subagent" for the existing child-workspace sub-agent flow; use "workspace" to start a normal full workspace turn. |
MUX_TOOL_INPUT_MODEL |
model |
string |
Optional model override for the sub-agent, parsed with the same alias logic as the UI (an alias or a full 'provider:model' string). Omit this unless the user explicitly instructed a specific model — by default the sub-agent inherits the parent's model. Do not assume any particular model is available. |
MUX_TOOL_INPUT_N |
n |
number |
Optional best-of count. Use n when several agents should try the same prompt independently. Mutually exclusive with variants; omit both for a single task. Only use grouped runs for sub-agents without interfering side effects, such as read-only agents like explore. |
MUX_TOOL_INPUT_PROMPT |
prompt |
string |
— |
MUX_TOOL_INPUT_RUN_IN_BACKGROUND |
run_in_background |
boolean |
— |
MUX_TOOL_INPUT_SUBAGENT_TYPE |
subagent_type |
string |
— |
MUX_TOOL_INPUT_THINKING |
thinking |
string |
Optional thinking/reasoning-level override for the sub-agent. Accepts a level name (off, low, medium, high, xhigh, max) or a numeric index (resolved against the chosen model). Omit this unless the user explicitly instructed a specific thinking level — by default the sub-agent inherits the parent's thinking level. |
MUX_TOOL_INPUT_TITLE |
title |
string |
— |
MUX_TOOL_INPUT_VARIANTS_<INDEX> |
variants[<INDEX>] |
string |
Optional labels for sibling runs of the same prompt template. Use variants when the task should be repeated across labeled lanes such as issue numbers, commit windows, or frontend/backend/tests/docs review lanes. Mutually exclusive with n. When provided, Mux launches one sibling per label and substitutes ${variant} in the prompt. |
MUX_TOOL_INPUT_VARIANTS_COUNT |
variants.length |
number |
Number of elements in variants (Optional labels for sibling runs of the same prompt template. Use variants when the task should be repeated across labeled lanes such as issue numbers, commit windows, or frontend/backend/tests/docs review lanes. Mutually exclusive with n. When provided, Mux launches one sibling per label and substitutes ${variant} in the prompt.) |
MUX_TOOL_INPUT_WORKSPACE_BRANCH_NAME |
workspace.branchName |
string |
— |
MUX_TOOL_INPUT_WORKSPACE_DISPOSABLE |
workspace.disposable |
boolean |
— |
MUX_TOOL_INPUT_WORKSPACE_MODE |
workspace.mode |
enum |
— |
MUX_TOOL_INPUT_WORKSPACE_QUEUE_DISPATCH_MODE |
workspace.queueDispatchMode |
enum |
For kind="workspace" + workspace.mode="existing", choose when a follow-up queued while the workspace is busy should dispatch: "tool-end" after the next tool call, or "turn-end" after the current turn. |
MUX_TOOL_INPUT_WORKSPACE_TRUNK_BRANCH |
workspace.trunkBranch |
string |
— |
MUX_TOOL_INPUT_WORKSPACE_WORKSPACE_ID |
workspace.workspaceId |
string |
— |
task_apply_git_patch (6)
| Env var |
JSON path |
Type |
Description |
MUX_TOOL_INPUT_DRY_RUN |
dry_run |
boolean |
When true, attempt to apply the patch in a temporary git worktree and then discard it (does not modify the current workspace). |
MUX_TOOL_INPUT_EXPECTED_HEAD_SHA |
expected_head_sha |
string |
When provided, refuse to apply unless the target repository HEAD matches this SHA. |
MUX_TOOL_INPUT_FORCE |
force |
boolean |
When true, allow apply even if the patch was previously applied (and skip clean-tree checks). |
MUX_TOOL_INPUT_PROJECT_PATH |
project_path |
string |
When provided, apply only the patch artifact for this project path. |
MUX_TOOL_INPUT_TASK_ID |
task_id |
string |
Child task ID whose patch artifact should be applied |
MUX_TOOL_INPUT_THREE_WAY |
three_way |
boolean |
When true, run git am with --3way |
task_await (6)
| Env var |
JSON path |
Type |
Description |
MUX_TOOL_INPUT_FILTER |
filter |
string |
Optional regex to filter bash task output lines. By default, only matching lines are returned. When filter_exclude is true, matching lines are excluded instead. Non-matching lines are discarded and cannot be retrieved later. |
MUX_TOOL_INPUT_FILTER_EXCLUDE |
filter_exclude |
boolean |
When true, lines matching 'filter' are excluded instead of kept. Requires 'filter' to be set. |
MUX_TOOL_INPUT_MIN_COMPLETED |
min_completed |
number |
Number of awaited tasks that must complete before this call returns. Defaults to 1, so by default task_await returns as soon as the FIRST awaited task completes, letting you act on it while the rest keep running. The result still includes every task complete at that moment plus current status (running/queued) for the rest. Tasks that have not yet completed keep running and remain re-awaitable on a later task_await call. Raise this (e.g. set it to the total number of awaited tasks) when you genuinely need more before proceeding — for example best-of-N synthesis that must compare every candidate. Clamped to the number of awaited tasks; values above that behave like 'wait for all'. |
MUX_TOOL_INPUT_TASK_IDS_<INDEX> |
task_ids[<INDEX>] |
string |
List of task IDs or workflow run IDs to await — use only real IDs returned by prior task, bash, or workflow_run results; never fabricate an ID. task_list can rediscover sub-agent/background bash IDs, but top-level workflow run rediscovery is done by omitting task_ids. When omitted, waits for active descendant tasks and top-level workflow runs of the current workspace, excluding workflow-owned sub-agents/background bash tasks because those results are consumed through parent workflow runs. |
MUX_TOOL_INPUT_TASK_IDS_COUNT |
task_ids.length |
number |
Number of elements in task_ids (List of task IDs or workflow run IDs to await — use only real IDs returned by prior task, bash, or workflow_run results; never fabricate an ID. task_list can rediscover sub-agent/background bash IDs, but top-level workflow run rediscovery is done by omitting task_ids. When omitted, waits for active descendant tasks and top-level workflow runs of the current workspace, excluding workflow-owned sub-agents/background bash tasks because those results are consumed through parent workflow runs.) |
MUX_TOOL_INPUT_TIMEOUT_SECS |
timeout_secs |
number |
Maximum time to wait in seconds for each task. For bash tasks, this waits for NEW output (or process exit). If exceeded, the result returns status=queued|starting|running|awaiting_report (task is still active). Defaults to 600 seconds (10 minutes) if not specified. Set to 0 for a non-blocking status check. |
task_list (3)
| Env var |
JSON path |
Type |
Description |
MUX_TOOL_INPUT_INCLUDE_ARCHIVED |
includeArchived |
boolean |
Whether to include archived child workspace tasks. Defaults to false, hiding archived non-actionable child workspace work. |
MUX_TOOL_INPUT_STATUSES_<INDEX> |
statuses[<INDEX>] |
enum |
Task statuses to include. Defaults to active tasks: queued, starting, running, awaiting_report, pending, backgrounded. Pass ['interrupted', 'failed'] to discover workflow runs that may be resumable via workflow_resume. |
MUX_TOOL_INPUT_STATUSES_COUNT |
statuses.length |
number |
Number of elements in statuses (Task statuses to include. Defaults to active tasks: queued, starting, running, awaiting_report, pending, backgrounded. Pass ['interrupted', 'failed'] to discover workflow runs that may be resumable via workflow_resume.) |
task_terminate (2)
| Env var |
JSON path |
Type |
Description |
MUX_TOOL_INPUT_TASK_IDS_<INDEX> |
task_ids[<INDEX>] |
string |
List of task IDs to terminate. Sub-agent task IDs and bash task IDs must belong to descendants of the current workspace; workflow run IDs (wfr_...) must belong to the current workspace and are interrupted (resumable) rather than destroyed. |
MUX_TOOL_INPUT_TASK_IDS_COUNT |
task_ids.length |
number |
Number of elements in task_ids (List of task IDs to terminate. Sub-agent task IDs and bash task IDs must belong to descendants of the current workspace; workflow run IDs (wfr_...) must belong to the current workspace and are interrupted (resumable) rather than destroyed.) |
todo_write (3)
| Env var |
JSON path |
Type |
Description |
MUX_TOOL_INPUT_TODOS_<INDEX>_CONTENT |
todos[<INDEX>].content |
string |
Task description with tense matching status: past for completed, present progressive for in_progress, imperative for pending |
MUX_TOOL_INPUT_TODOS_<INDEX>_STATUS |
todos[<INDEX>].status |
enum |
Task status |
MUX_TOOL_INPUT_TODOS_COUNT |
todos.length |
number |
Number of elements in todos |
web_fetch (1)
| Env var |
JSON path |
Type |
Description |
MUX_TOOL_INPUT_URL |
url |
string |
The URL to fetch (http or https) |
workflow_resume (3)
| Env var |
JSON path |
Type |
Description |
MUX_TOOL_INPUT_MODE |
mode |
enum |
Defaults to 'resume', which continues interrupted or crash-orphaned runs from durable state and never re-executes completed steps. Use 'retry_from_checkpoint' only for failed runs; it re-executes work after the last checkpoint and is rejected when unsafe. |
MUX_TOOL_INPUT_RUN_ID |
run_id |
string |
Workflow run ID (wfr_...) to resume. Must belong to the current workspace. |
MUX_TOOL_INPUT_RUN_IN_BACKGROUND |
run_in_background |
boolean |
Defaults to false (foreground): waits until the run reaches a terminal status and returns its result. Set true to resume in the background and continue other work; await the runId with task_await when you need the result. |
workflow_run (3)
| Env var |
JSON path |
Type |
Description |
MUX_TOOL_INPUT_ARGS |
args |
unknown |
— |
MUX_TOOL_INPUT_RUN_IN_BACKGROUND |
run_in_background |
boolean |
Defaults to false. Prefer foreground mode for a single workflow; when the returned status is completed, the result is available directly. Set true only when you will start another workflow/task or do independent work while it runs. If workflow_run returns status=running or status=backgrounded, await the returned runId with task_await before using the result. |
MUX_TOOL_INPUT_SCRIPT_PATH |
script_path |
string |
Explicit workflow script path, such as "skill://deep-research/workflow.js" or "./workflows/research.js". |
{/* END TOOL_HOOK_ENV_VARS */}
#!/usr/bin/env bash
# .mux/tool_post
if [[ "$MUX_TOOL" == file_edit_* ]]; then
file="$MUX_TOOL_INPUT_FILE_PATH"
prettier --write --log-level silent "$file" 2>/dev/null || true
fi
#!/usr/bin/env bash
# .mux/tool_post
# Tool results are available via MUX_TOOL_RESULT_* if needed.
echo "$(date '+%H:%M:%S') $MUX_TOOL completed" >> /tmp/mux-tools.log
#!/usr/bin/env python3
# .mux/tool_pre
import os, sys
tool = os.environ.get('MUX_TOOL', '')
script = os.environ.get('MUX_TOOL_INPUT_SCRIPT', '')
if tool == 'bash':
if 'rm -rf /' in script:
print("❌ Blocked dangerous command", file=sys.stderr)
sys.exit(1)
sys.exit(0) # Allow