Skip to main content
Glama
andresthor

Command-Line MCP Server

by andresthor

execute_read_command

Execute read-only Unix/macOS terminal commands like ls, cat, and grep through a controlled interface, returning output and status for safe command execution.

Instructions

Execute a read-only Unix/macOS terminal command (ls, cat, grep, etc.).

Args: command: The read-only command to execute session_id: Optional session ID for permission management

Returns: A dictionary with command output and status

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
commandYes
session_idNo

Implementation Reference

  • The `execute_read_command` tool handler function that validates the command, performs directory permission checks, and calls `_execute_command` if permitted.
    async def execute_read_command(
        command: str, session_id: str | None = None
    ) -> dict[str, Any]:
        """
        Execute a read-only Unix/macOS terminal command (ls, cat, grep, etc.).
    
        Args:
            command: The read-only command to execute
            session_id: Optional session ID for permission management
    
        Returns:
            A dictionary with command output and status
        """
        # For Claude Desktop compatibility, use the fixed session ID when no session ID provided
        if not session_id:
            session_id = self.claude_desktop_session_id
            logger.info(
                f"Using persistent Claude Desktop session for read command: {session_id}"
            )
    
        # Validate command and check directory permissions in one go
        # Get the latest command lists
        command_lists = self.config.get_effective_command_lists()
        allow_separators = self.config.get(
            "security", "allow_command_separators", True
        )
    
        validation = validate_command(
            command,
            command_lists["read"],
            command_lists["write"],
            command_lists["system"],
            command_lists["blocked"],
            command_lists["dangerous_patterns"],
            allow_command_separators=allow_separators,
        )
    
        if not validation["is_valid"]:
            return {
                "success": False,
                "output": "",
                "error": validation["error"],
            }
    
        if validation["command_type"] != "read":
            return {
                "success": False,
                "output": "",
                "error": "This tool only supports read commands. Use execute_command for other command types.",
            }
    
        # Extract directory and check permissions (apply same directory checks as in _execute_command)
        working_dir = extract_directory_from_command(command)
        logger.info(f"Read command - extracted working directory: {working_dir}")
    
        # Check if directory is whitelisted or has session approval
        directory_allowed = False
    
        if working_dir:
            # Check global whitelist first
            if is_directory_whitelisted(working_dir, self.whitelisted_directories):
                directory_allowed = True
                logger.info(
                    f"Read command - directory '{working_dir}' is globally whitelisted"
                )
            # Check session approvals if we have a session ID
            elif session_id and self.session_manager.has_directory_approval(
                session_id, working_dir
            ):
                directory_allowed = True
                logger.info(
                    f"Read command - directory '{working_dir}' is approved for session {session_id}"
                )
            else:
                logger.warning(
                    f"Read command - directory '{working_dir}' is not whitelisted or approved"
                )
                # For Claude Desktop compatibility mode (require_session_id = False)
                require_session_id = self.config.get(
                    "security", "require_session_id", False
                )
                auto_approve_in_desktop = self.config.get_section("security").get(
                    "auto_approve_directories_in_desktop_mode", False
                )
    
                if not require_session_id:
                    # Check if the directory is approved in the persistent desktop session
                    if self.session_manager.has_directory_approval(
                        self.claude_desktop_session_id, working_dir
                    ):
                        directory_allowed = True
                        logger.info(
                            f"Read command - directory '{working_dir}' is approved in persistent desktop session"
                        )
                    elif auto_approve_in_desktop:
                        # Auto-approve directories in desktop mode if configured
                        directory_allowed = True
                        # Also add to persistent session for future requests
                        self.session_manager.approve_directory(
                            self.claude_desktop_session_id, working_dir
                        )
                        logger.warning(
                            f"Read command - auto-approving directory access in desktop mode: {working_dir}"
                        )
                    else:
                        # Only allow whitelisted directories if auto-approve is off
                        directory_allowed = False
                        logger.warning(
                            f"Read command - directory '{working_dir}' is not whitelisted - restricting access"
                        )
        else:
            # If we couldn't extract a directory, default to requiring permission
            logger.warning(
                "Read command - could not extract working directory from command"
            )
            working_dir = os.getcwd()  # Default to current directory
    
            # Check whitelist for current directory
            if is_directory_whitelisted(working_dir, self.whitelisted_directories):
                directory_allowed = True
            elif session_id and self.session_manager.has_directory_approval(
                session_id, working_dir
            ):
                directory_allowed = True
            else:
                # For Claude Desktop compatibility mode
                require_session_id = self.config.get(
                    "security", "require_session_id", False
                )
                auto_approve_in_desktop = self.config.get_section("security").get(
                    "auto_approve_directories_in_desktop_mode", False
                )
    
                if not require_session_id:
                    # Check if the directory is approved in the persistent desktop session
                    if self.session_manager.has_directory_approval(
                        self.claude_desktop_session_id, working_dir
                    ):
                        directory_allowed = True
                        logger.info(
                            f"Read command - directory '{working_dir}' is approved in persistent desktop session"
                        )
                    elif auto_approve_in_desktop:
                        # Auto-approve directories in desktop mode if configured
                        directory_allowed = True
                        # Also add to persistent session for future requests
                        self.session_manager.approve_directory(
                            self.claude_desktop_session_id, working_dir
                        )
                        logger.warning(
                            f"Read command - auto-approving directory access in desktop mode: {working_dir}"
                        )
                    else:
                        # Only allow whitelisted directories if auto-approve is off
                        directory_allowed = False
    
        # If directory is not allowed
        if not directory_allowed:
            # Check if we're in Claude Desktop mode (no session ID or require_session_id=false)
            require_session_id = self.config.get(
                "security", "require_session_id", False
            )
            if not session_id or not require_session_id:
                # Always use the fixed persistent session ID for Claude Desktop
                desktop_session_id = self.claude_desktop_session_id
    
                # Include approval request information for Claude Desktop
                return {
                    "success": False,
                    "output": "",
                    "error": f"Read command - access to directory '{working_dir}' is not allowed. Only whitelisted directories can be accessed.\n"
                    + f"Whitelisted directories include: {', '.join(self.whitelisted_directories)}\n"
                    + "Note: To request access to this directory, use the approve_directory tool with:\n"
                    + f'  approve_directory(directory="{working_dir}", session_id="{desktop_session_id}", remember=True)',
                    "directory": working_dir,
                    "session_id": desktop_session_id,
                    "requires_directory_approval": True,  # Signal that approval is needed
                }
            else:
                # For normal mode, request approval
                return {
                    "success": False,
                    "output": "",
                    "error": f"Read command - directory '{working_dir}' requires approval. Use approve_directory tool with session_id '{session_id}'.",
                    "requires_directory_approval": True,
                    "directory": working_dir,
                    "session_id": session_id,
                }
    
        # Now that we've validated both the command and directory permissions, execute the command
        return await self._execute_command(
            command, command_type="read", session_id=session_id
        )

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/andresthor/cmd-line-mcp'

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