Agent with CLI Tools
%%{init: {'look': 'handDrawn', 'theme': 'base', 'themeVariables': {'primaryColor': '#eef2ff', 'primaryBorderColor': '#1e40af', 'primaryTextColor': '#1e293b', 'lineColor': '#1e3a8a', 'edgeLabelBackground': '#ffffff', 'clusterBkg': '#fbfcff', 'clusterBorder': '#2563eb', 'fontFamily': '-apple-system, system-ui, Segoe UI, Roboto, Helvetica, Arial, sans-serif', 'fontSize': '15px'}, 'flowchart': {'nodeSpacing': 50, 'rankSpacing': 58, 'padding': 14, 'htmlLabels': true, 'curve': 'basis'}}}%%
flowchart LR
Q(["Ask about the repo"]) --> A("Agent")
A --> G{"Command on<br/>the allowlist?"}
G == "yes" ==> C("run_command")
C --> A
A --> O(["Answer"])Outcome: the agent can run real shell commands to answer questions about a checkout, but only the commands you listed, and each run is a durable task you can inspect afterwards.
How it works
cli_commands=Trueattaches arun_commandtool. You don't write the wrapper.cli_allowed_commandsis the boundary. Anything outside the list is refused before it executes.- Shell mode is off by default, so the model can't chain commands with pipes or
;. - Every command is its own Conductor task, so you can see exactly what ran and what it returned.
Prerequisites
A Conductor server with an LLM provider, and CONDUCTOR_SERVER_URL set. The commands you allow must be on PATH where the worker runs.
The agent
Save this as agent_cli_tools.py:
"""Agent with CLI tools — a sandboxed shell, restricted to an allowlist.
Derived from the cli_commands support in sdk/python-sdk/src/conductor/ai/agents.
cli_commands=True attaches a run_command tool. cli_allowed_commands is the
allowlist: anything outside it is refused before execution. Shell mode is off,
so the model cannot chain commands with pipes or semicolons.
"""
from conductor.ai.agents import Agent, AgentRuntime
MODEL = "openai/gpt-4o-mini"
agent = Agent(
name="repo_inspector",
model=MODEL,
instructions=(
"You inspect a checked-out repository using shell commands. "
"Use run_command for every fact you report. Never guess."
),
cli_commands=True,
cli_allowed_commands=["git", "ls", "wc", "cat"],
)
if __name__ == "__main__":
with AgentRuntime() as runtime:
result = runtime.run(agent, "How many files are in the current directory?")
result.print_result()
print("execution id:", result.execution_id)
Run it
A verified run made two tool calls, used ls, and reported the file count for the working directory. Open Executions to see each run_command invocation with its arguments and output.
The same example in other SDKs
The agent API is the same shape in every SDK. These are the upstream sources this recipe was derived from — the Java entry is an end-to-end test suite rather than a numbered example, but it exercises the same CliConfig API:
| SDK | Example |
|---|---|
| Python | 16c_credentials_cli_tools.py |
| Java | Suite3CliTools.java |
| TypeScript | 16c-credentials-cli-tools.ts |
| C# | Program.cs |
Production notes
- The allowlist is the blast radius.
gitincludesgit push— list the narrowest set that works. - Run the worker somewhere disposable. Treat the working directory as untrusted output, not a source of truth.
- Leave
allow_shelloff. Enabling it hands the model arbitrary command composition. - Secrets go through
credentials=[...]on the tool, injected for the duration of the call — never into the prompt. - Set a timeout. A hung command otherwise occupies a worker slot indefinitely.