Skip to main content
Glama

run_command

Execute bash commands with timeout and security controls to manage system operations and automate tasks.

Instructions

Executes a given bash command in a shell with optional timeout, ensuring proper handling and security measures.

Before executing the command, please follow these steps:

  1. Directory Verification:

    • If the command will create new directories or files, first use the directory_tree tool to verify the parent directory exists and is the correct location

    • For example, before running "mkdir foo/bar", first use directory_tree to check that "foo" exists and is the intended parent directory

  2. Command Execution:

    • After ensuring proper quoting, execute the command.

    • Capture the output of the command.

Usage notes:

  • The command argument is required.

  • You can specify an optional timeout in milliseconds (up to 600000ms / 10 minutes). If not specified, commands will timeout after 120000ms (2 minutes).

  • It is very helpful if you write a clear, concise description of what this command does in 5-10 words.

  • If the output exceeds 30000 characters, output will be truncated before being returned to you.

  • VERY IMPORTANT: You MUST avoid using search commands like find and grep. Instead use Grep, Glob, or Task to search. You MUST avoid read tools like cat, head, tail, and ls, and use read and directory_tree to read files.

  • If you still need to run grep, STOP. ALWAYS USE ripgrep at rg (or /opt/homebrew/Cellar/ripgrep/14.1.1/bin/rg) first, which all Claude Code users have pre-installed.

  • When issuing multiple commands, use the ';' or '&&' operator to separate them. DO NOT use newlines (newlines are ok in quoted strings). cd /foo/bar && pytest tests

Committing changes with git

When the user asks you to create a new git commit, follow these steps carefully:

  1. You have the capability to call multiple tools in a single response. When multiple independent pieces of information are requested, batch your tool calls together for optimal performance. ALWAYS run the following bash commands in parallel, each using the Bash tool:

    • Run a git status command to see all untracked files.

    • Run a git diff command to see both staged and unstaged changes that will be committed.

    • Run a git log command to see recent commit messages, so that you can follow this repository's commit message style.

  2. Analyze all staged changes (both previously staged and newly added) and draft a commit message. Wrap your analysis process in <commit_analysis> tags:

<commit_analysis>

  • List the files that have been changed or added

  • Summarize the nature of the changes (eg. new feature, enhancement to an existing feature, bug fix, refactoring, test, docs, etc.)

  • Brainstorm the purpose or motivation behind these changes

  • Assess the impact of these changes on the overall project

  • Check for any sensitive information that shouldn't be committed

  • Draft a concise (1-2 sentences) commit message that focuses on the "why" rather than the "what"

  • Ensure your language is clear, concise, and to the point

  • Ensure the message accurately reflects the changes and their purpose (i.e. "add" means a wholly new feature, "update" means an enhancement to an existing feature, "fix" means a bug fix, etc.)

  • Ensure the message is not generic (avoid words like "Update" or "Fix" without context)

  • Review the draft message to ensure it accurately reflects the changes and their purpose </commit_analysis>

  1. You have the capability to call multiple tools in a single response. When multiple independent pieces of information are requested, batch your tool calls together for optimal performance. ALWAYS run the following commands in parallel:

    • Add relevant untracked files to the staging area.

    • Create the commit with a message ending with: 🤖 Generated with Claude Code

    Co-Authored-By: Claude noreply@anthropic.com

    • Run git status to make sure the commit succeeded.

  2. If the commit fails due to pre-commit hook changes, retry the commit ONCE to include these automated changes. If it fails again, it usually means a pre-commit hook is preventing the commit. If the commit succeeds but you notice that files were modified by the pre-commit hook, you MUST amend your commit to include them.

Important notes:

  • Use the git context at the start of this conversation to determine which files are relevant to your commit. Be careful not to stage and commit files (e.g. with git add .) that aren't relevant to your commit.

  • NEVER update the git config

  • DO NOT run additional commands to read or explore code, beyond what is available in the git context

  • DO NOT push to the remote repository

  • IMPORTANT: Never use git commands with the -i flag (like git rebase -i or git add -i) since they require interactive input which is not supported.

  • If there are no changes to commit (i.e., no untracked files and no modifications), do not create an empty commit

  • Ensure your commit message is meaningful and concise. It should explain the purpose of the changes, not just describe them.

  • Return an empty response - the user will see the git output directly

  • In order to ensure good formatting, ALWAYS pass the commit message via a HEREDOC, a la this example:

git commit -m "$(cat <<'EOF' Commit message here.

🤖 Generated with MCP Claude Code

EOF )"

Creating pull requests

Use the gh command via the Bash tool for ALL GitHub-related tasks including working with issues, pull requests, checks, and releases. If given a Github URL use the gh command to get the information needed.

IMPORTANT: When the user asks you to create a pull request, follow these steps carefully:

  1. You have the capability to call multiple tools in a single response. When multiple independent pieces of information are requested, batch your tool calls together for optimal performance. ALWAYS run the following bash commands in parallel using the Bash tool, in order to understand the current state of the branch since it diverged from the main branch:

    • Run a git status command to see all untracked files

    • Run a git diff command to see both staged and unstaged changes that will be committed

    • Check if the current branch tracks a remote branch and is up to date with the remote, so you know if you need to push to the remote

    • Run a git log command and git diff main...HEAD to understand the full commit history for the current branch (from the time it diverged from the main branch)

  2. Analyze all changes that will be included in the pull request, making sure to look at all relevant commits (NOT just the latest commit, but ALL commits that will be included in the pull request!!!), and draft a pull request summary. Wrap your analysis process in <pr_analysis> tags:

<pr_analysis>

  • List the commits since diverging from the main branch

  • Summarize the nature of the changes (eg. new feature, enhancement to an existing feature, bug fix, refactoring, test, docs, etc.)

  • Brainstorm the purpose or motivation behind these changes

  • Assess the impact of these changes on the overall project

  • Do not use tools to explore code, beyond what is available in the git context

  • Check for any sensitive information that shouldn't be committed

  • Draft a concise (1-2 bullet points) pull request summary that focuses on the "why" rather than the "what"

  • Ensure the summary accurately reflects all changes since diverging from the main branch

  • Ensure your language is clear, concise, and to the point

  • Ensure the summary accurately reflects the changes and their purpose (ie. "add" means a wholly new feature, "update" means an enhancement to an existing feature, "fix" means a bug fix, etc.)

  • Ensure the summary is not generic (avoid words like "Update" or "Fix" without context)

  • Review the draft summary to ensure it accurately reflects the changes and their purpose </pr_analysis>

  1. You have the capability to call multiple tools in a single response. When multiple independent pieces of information are requested, batch your tool calls together for optimal performance. ALWAYS run the following commands in parallel:

    • Create new branch if needed

    • Push to remote with -u flag if needed

    • Create PR using gh pr create with the format below. Use a HEREDOC to pass the body to ensure correct formatting.

gh pr create --title "the pr title" --body "$(cat <<'EOF'

Summary

<1-3 bullet points>

Test plan

[Checklist of TODOs for testing the pull request...]

🤖 Generated with Claude Code EOF )"

Important:

  • NEVER update the git config

  • Return the PR URL when you're done, so the user can see it

Other common operations

  • View comments on a Github PR: gh api repos/foo/bar/pulls/123/comments

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
commandYesThe shell command to execute
cwdYesWorking directory where the command should be executed
shell_typeNoType of shell to use (e.g., bash, zsh). Defaults to system default
use_login_shellNoWhether to use a login shell (default: True)

Implementation Reference

  • Core handler logic for the run_command tool: extracts parameters, checks permissions, executes via BashSessionExecutor, and formats results.
    @override async def call( self, ctx: MCPContext, **params: Unpack[RunCommandToolParams], ) -> str: """Execute the tool with the given parameters. Args: ctx: MCP context **params: Tool parameters Returns: Tool result """ tool_ctx = await self.prepare_tool_context(ctx) # Extract parameters command = params["command"] session_id = params.get("session_id") time_out = params.get("time_out", 30) is_input = params.get("is_input", False) blocking = params.get("blocking", False) await tool_ctx.info( f"Executing command: {command} ( session_id={session_id}, is_input={is_input}, blocking={blocking} )" ) # Check if command is allowed (skip for input to running processes) if not is_input and not self.command_executor.is_command_allowed(command): await tool_ctx.error(f"Command not allowed: {command}") return f"Error: Command not allowed: {command}" try: # Execute command using BashSessionExecutor with enhanced parameters result = await self.command_executor.execute_command( command=command, timeout=time_out, session_id=session_id, is_input=is_input, blocking=blocking, ) except RuntimeError as e: await tool_ctx.error(f"Session execution failed: {str(e)}") return f"Error: Session execution failed: {str(e)}" # Format the result using the new enhanced formatting if result.is_success: # Use the enhanced agent observation format for better user experience return result.to_agent_observation() else: # For failed or running commands, provide comprehensive output return result.format_output() @override
  • TypedDict defining the input parameters and annotations for the run_command tool.
    class RunCommandToolParams(TypedDict): """Parameters for the RunCommandTool. Attributes: command: The shell command to execute session_id: Optional session ID for persistent shell sessions time_out: Timeout in seconds for session-based commands with no output changes is_input: Whether this is input to a running process rather than a new command blocking: Whether to run in blocking mode, ignoring no-change timeout """ command: Command session_id: SessionID time_out: TimeOut is_input: IsInput blocking: Blocking
  • The register method defines and registers the run_command tool function using @mcp_server.tool decorator, which serves as the MCP entrypoint.
    def register(self, mcp_server: FastMCP) -> None: """Register this run command tool with the MCP server. Creates a wrapper function with explicitly defined parameters that match the tool's parameter schema and registers it with the MCP server. Args: mcp_server: The FastMCP server instance """ tool_self = self # Create a reference to self for use in the closure @mcp_server.tool(name=self.name, description=self.description) @handle_connection_errors async def run_command( command: Command, session_id: SessionID, time_out: TimeOut, is_input: IsInput, blocking: Blocking, ) -> str: ctx = get_context() return await tool_self.call( ctx, command=command, session_id=session_id, time_out=time_out, is_input=is_input, blocking=blocking, )
  • Registers all shell tools, including run_command, by creating instances via get_shell_tools and calling ToolRegistry.register_tools (which invokes each tool's register method).
    def register_shell_tools( mcp_server: FastMCP, permission_manager: PermissionManager, ) -> list[BaseTool]: """Register all shell tools with the MCP server. Args: mcp_server: The FastMCP server instance permission_manager: Permission manager for access control Returns: List of registered tools """ tools = get_shell_tools(permission_manager) ToolRegistry.register_tools(mcp_server, tools) return tools
  • Supporting executor that performs the actual command execution using BashSession or subprocess, called by the run_command handler.
    async def execute_command( self, command: str, env: dict[str, str] | None = None, timeout: float | None = 60.0, session_id: str = "", is_input: bool = False, blocking: bool = False, ) -> CommandResult: """Execute a shell command with safety checks. Args: command: The command to execute env: Optional environment variables timeout: Optional timeout in seconds session_id: Optional session ID for persistent execution is_input: Whether this is input to a running process blocking: Whether to run in blocking mode Returns: CommandResult containing execution results """ self._log( f"Executing command: {command} (is_input={is_input}, blocking={blocking})" ) # Check if the command is allowed (skip for input to running processes) if not is_input and not self.is_command_allowed(command): return CommandResult( return_code=1, error_message=f"Command not allowed: {command}", session_id=session_id, command=command, ) # Handle subprocess mode when session_id is explicitly None if not session_id: return await self._execute_subprocess_mode(command, env, timeout) # Default working directory for new sessions only # Existing sessions maintain their current working directory # Users should use 'cd' commands to navigate within sessions default_work_dir = os.path.expanduser("~") # Generate default session ID if none provided effective_session_id = session_id if effective_session_id == "": import uuid effective_session_id = f"default_{uuid.uuid4().hex[:8]}" self._log(f"Generated default session ID: {effective_session_id}") # Use session-based execution try: # Get existing session or create new one session = self.session_manager.get_session(effective_session_id) if session is None: # Use faster timeouts and polling for tests if self.fast_test_mode: timeout_seconds = ( 10 # Faster timeout for tests but not too aggressive ) poll_interval = 0.2 # Faster polling for tests but still reasonable else: timeout_seconds = 30 # Default timeout poll_interval = 0.5 # Default polling session = self.session_manager.get_or_create_session( session_id=effective_session_id, work_dir=default_work_dir, no_change_timeout_seconds=timeout_seconds, poll_interval=poll_interval, ) # Set environment variables if provided (only for new commands, not input) if env and not is_input: for key, value in env.items(): env_result = session.execute(f'export {key}="{value}"') if env_result.return_code != 0: self._log(f"Failed to set environment variable {key}") # Execute the command with enhanced parameters result = session.execute( command=command, is_input=is_input, blocking=blocking, timeout=timeout ) # Add session_id to the result result.session_id = effective_session_id return result except Exception as e: self._log(f"Session execution error: {str(e)}") return CommandResult( return_code=1, error_message=f"Error executing command in session: {str(e)}", session_id=effective_session_id, command=command, ) async def _execute_subprocess_mode(

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/SDGLBL/mcp-claude-code'

If you have feedback or need assistance with the MCP directory API, please join our Discord server