Skip to content

Run command

run_command

RunCommandTool — execute shell commands with timeout and output truncation.

Registered as run_command via the axm.tools entry point.

RunCommandTool

Execute shell commands with timeout and output truncation.

Runs commands via subprocess.run with configurable timeout, output size caps, and a blocked-command safety list. Registered as run_command via axm.tools entry point.

Source code in packages/axm-edit/src/axm_edit/tools/run_command.py
Python
class RunCommandTool:
    """Execute shell commands with timeout and output truncation.

    Runs commands via ``subprocess.run`` with configurable timeout,
    output size caps, and a blocked-command safety list.
    Registered as ``run_command`` via axm.tools entry point.
    """

    agent_hint: str = (
        "Run a shell command in cwd. No cd/pipes/&&."
        " Returns stdout+stderr truncated to 4K chars."
        " Use for tests, builds, scripts."
    )

    @property
    def name(self) -> str:
        """Tool name used for MCP registration."""
        return "run_command"

    def execute(
        self,
        *,
        command: str | None = None,
        path: str = ".",
        cwd: str | None = None,
        timeout: int = _DEFAULT_TIMEOUT,
        **kwargs: object,
    ) -> ToolResult:
        """Execute a shell command.

        Args:
            command: Shell command string (required).
            path: Project root directory (default ".").
            cwd: Working directory, relative to root (optional).
            timeout: Timeout in seconds (default 30).

        Returns:
            ToolResult with stdout, stderr, exit_code, and timed_out.
        """
        root_str = path
        cwd_rel = cwd

        # ── Validate inputs ──────────────────────────────────────────
        if not command or not command.strip():
            return ToolResult(
                success=False,
                error="Missing required argument: command",
            )

        if _is_blocked(command):
            return ToolResult(
                success=False,
                error="Blocked command: this command is not allowed",
            )

        root = Path(root_str).resolve()
        if not root.is_dir():
            return ToolResult(
                success=False,
                error=f"Root is not a directory: {root_str}",
            )

        # Resolve cwd within the sandbox
        if cwd_rel:
            resolved_cwd = _resolve_safe(root, cwd_rel)
            if resolved_cwd is None:
                return ToolResult(
                    success=False,
                    error=f"cwd escapes project root: {cwd_rel}",
                )
            if not resolved_cwd.is_dir():
                return ToolResult(
                    success=False,
                    error=f"cwd is not a directory: {cwd_rel}",
                )
            work_dir = resolved_cwd
        else:
            work_dir = root

        # ── Execute ──────────────────────────────────────────────────
        return _run(command, work_dir, timeout)
name property

Tool name used for MCP registration.

execute(*, command=None, path='.', cwd=None, timeout=_DEFAULT_TIMEOUT, **kwargs)

Execute a shell command.

Parameters:

Name Type Description Default
command str | None

Shell command string (required).

None
path str

Project root directory (default ".").

'.'
cwd str | None

Working directory, relative to root (optional).

None
timeout int

Timeout in seconds (default 30).

_DEFAULT_TIMEOUT

Returns:

Type Description
ToolResult

ToolResult with stdout, stderr, exit_code, and timed_out.

Source code in packages/axm-edit/src/axm_edit/tools/run_command.py
Python
def execute(
    self,
    *,
    command: str | None = None,
    path: str = ".",
    cwd: str | None = None,
    timeout: int = _DEFAULT_TIMEOUT,
    **kwargs: object,
) -> ToolResult:
    """Execute a shell command.

    Args:
        command: Shell command string (required).
        path: Project root directory (default ".").
        cwd: Working directory, relative to root (optional).
        timeout: Timeout in seconds (default 30).

    Returns:
        ToolResult with stdout, stderr, exit_code, and timed_out.
    """
    root_str = path
    cwd_rel = cwd

    # ── Validate inputs ──────────────────────────────────────────
    if not command or not command.strip():
        return ToolResult(
            success=False,
            error="Missing required argument: command",
        )

    if _is_blocked(command):
        return ToolResult(
            success=False,
            error="Blocked command: this command is not allowed",
        )

    root = Path(root_str).resolve()
    if not root.is_dir():
        return ToolResult(
            success=False,
            error=f"Root is not a directory: {root_str}",
        )

    # Resolve cwd within the sandbox
    if cwd_rel:
        resolved_cwd = _resolve_safe(root, cwd_rel)
        if resolved_cwd is None:
            return ToolResult(
                success=False,
                error=f"cwd escapes project root: {cwd_rel}",
            )
        if not resolved_cwd.is_dir():
            return ToolResult(
                success=False,
                error=f"cwd is not a directory: {cwd_rel}",
            )
        work_dir = resolved_cwd
    else:
        work_dir = root

    # ── Execute ──────────────────────────────────────────────────
    return _run(command, work_dir, timeout)