Skip to content

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=True attaches a run_command tool. You don't write the wrapper.
  • cli_allowed_commands is 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

python agent_cli_tools.py

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. git includes git 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_shell off. 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.