Skip to main content
Glama

shell_ps

Display running process status on a system to monitor and manage active applications through the Shell-MCP server's secure command execution.

Instructions

Show process status

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
argsNoCommand arguments

Implementation Reference

  • Configuration entry that registers the 'shell.ps' tool (tool name 'ps') with its command 'ps', description, allowed arguments, and execution timeout.
    'shell.ps': {
      command: 'ps',
      description: 'Show process status',
      allowedArgs: ['-e', '-f', '-u', '--help'],
      timeout: 5000
    },
  • MCP CallToolRequestSchema handler that processes tool calls for all shell tools including 'ps'. Maps tool name to full command 'shell.ps', retrieves config, validates, executes via CommandExecutor, and returns output.
    this.server.setRequestHandler(CallToolRequestSchema, async (request, extra: unknown) => {
      const ext = extra as Extra;
      if (!request.params?.name) {
        throw new ToolError('MISSING_COMMAND', 'Command name is required');
      }
      
      const command = String(request.params.name);
      const fullCommand = command.startsWith('shell.') ? command : `shell.${command}`;
      
      if (!(fullCommand in allowedCommands)) {
        throw new ToolError('COMMAND_NOT_FOUND', 'Command not found', { command });
      }
      
      const config = allowedCommands[fullCommand];
      const args = Array.isArray(request.params.arguments?.args)
        ? request.params.arguments.args.map(String)
        : [];
    
      const context: CommandContext = {
        requestId: ext.id || 'unknown',
        command,
        args,
        timeout: config.timeout,
        workDir: config.workDir,
        env: config.env
      };
    
      this.logger.info('Starting command execution', context);
    
      try {
        this.validator.validateCommand(command, args);
        
        this.logger.debug('Command validation passed', {
          ...context,
          config
        });
    
        const stream = await this.executor.execute(command, args, {
          timeout: config.timeout,
          cwd: config.workDir,
          env: config.env
        });
    
        ext.onCancel?.(() => {
          this.logger.info('Received cancel request', context);
          this.executor.interrupt();
        });
    
        const output = await this.collectOutput(stream);
    
        this.logger.info('Command execution completed', {
          ...context,
          outputLength: output.length
        });
    
        return {
          content: [{
            type: "text",
            text: output
          }]
        };
    
      } catch (error) {
        this.logger.error('Command execution failed', {
          ...context,
          error: error instanceof Error ? error.message : String(error),
          stack: error instanceof Error ? error.stack : undefined
        });
        
        throw new ToolError(
          'EXECUTION_FAILED',
          `Command execution failed: ${error instanceof Error ? error.message : String(error)}`,
          context
        );
      }
    });
  • CommandExecutor.execute method that spawns the actual 'ps' child process (after removing 'shell.' prefix), handles streaming output, errors, timeouts, and caching.
    async execute(
      command: string,
      args: string[] = [],
      options: ExecuteOptions = {}
    ): Promise<{ stdout: Readable }> {
      const commandKey = `${command} ${args.join(' ')}`;
      
      try {
        // Check security
        await this.securityChecker.validateCommand(command, args, options);
    
        // Check cache
        const cached = this.cache.get(commandKey);
        if (cached) {
          this.logger.debug('Using cached command result', { command, args });
          return this.createStreamFromCache(cached);
        }
    
        // Remove 'shell.' prefix for execution
        const baseCommand = command.replace('shell.', '');
    
        // Execute command
        this.logger.debug('Starting command execution', { command, args, options });
        const childProcess = spawn(baseCommand, args, {
          stdio: ['ignore', 'pipe', 'pipe'],
          timeout: options.timeout,
          cwd: options.cwd,
          env: {
            ...process.env,
            ...options.env
          },
          signal: options.signal
        });
    
        this.currentProcess = childProcess;
    
        // Error handling
        childProcess.on('error', (error: Error) => {
          this.logger.error('Command execution error', {
            command,
            args,
            error: error.message
          });
          throw new ToolError(
            'PROCESS_ERROR',
            'Command execution error',
            { command, args, error: error.message }
          );
        });
    
        // Timeout handling
        if (options.timeout) {
          setTimeout(() => {
            if (childProcess.exitCode === null) {
              this.logger.warn('Command execution timeout', {
                command,
                args,
                timeout: options.timeout
              });
              childProcess.kill();
              throw new ToolError(
                'TIMEOUT',
                'Command execution timeout',
                { command, args, timeout: options.timeout }
              );
            }
          }, options.timeout);
        }
    
        if (!childProcess.stdout) {
          throw new ToolError(
            'STREAM_ERROR',
            'Unable to get command output stream',
            { command, args }
          );
        }
    
        // Monitor process status
        childProcess.on('exit', (code, signal) => {
          this.logger.debug('Command execution completed', {
            command,
            args,
            exitCode: code,
            signal
          });
        });
    
        return {
          stdout: childProcess.stdout
        };
    
      } catch (error) {
        this.logger.error('Command execution failed', {
          command,
          args,
          error: error instanceof Error ? error.message : String(error)
        });
        
        throw new ToolError(
          'EXECUTION_ERROR',
          'Command execution failed',
          { 
            command, 
            args, 
            error: error instanceof Error ? error.message : String(error)
          }
        );
      }
    }
  • CommandValidator.validateCommand method that checks if 'ps' command and its arguments are allowed per the allowlist configuration.
    validateCommand(
      command: string, 
      args: string[] = [], 
      options: CommandOptions = {}
    ): void {
      console.log('Validating command:', {
        command,
        args,
        baseCommand: command.replace('shell.', ''),
        fullCommand: `shell.${command.replace('shell.', '')}`,
        config: allowedCommands[`shell.${command.replace('shell.', '')}`]
      });
    
      const baseCommand = command.replace('shell.', '');
      
      if (!(`shell.${baseCommand}` in allowedCommands)) {
        throw new Error(`Command not allowed: ${command}`);
      }
      
      const config = allowedCommands[`shell.${baseCommand}`];
      
      const allowedArgs = config.allowedArgs || [];
      
      console.log('Checking args:', {
        allowedArgs,
        hasWildcard: allowedArgs.includes('*')
      });
    
      args.forEach(arg => {
        if (arg.startsWith('-')) {
          if (!allowedArgs.includes(arg)) {
            console.log('Invalid option:', arg);
            throw new Error(`Invalid argument: ${arg}`);
          }
        }
        else if (!allowedArgs.includes('*')) {
          console.log('Path not allowed:', arg);
          throw new Error(`Invalid argument: ${arg}`);
        } else {
          // 檢查路徑參數
          this.validatePath(arg);
        }
      });
      
      // 檢查超時設定
      if (options.timeout && options.timeout > securityConfig.defaultTimeout) {
        throw new Error(`Timeout exceeds maximum allowed value`);
      }
    }
  • Dynamic generation of input schema for the 'ps' tool (and all shell tools) as an object with 'args' array of strings.
    tools.push({
      name: toolName,
      description: config.description,
      inputSchema: {
        type: "object",
        properties: {
          args: {
            type: "array",
            items: { type: "string" },
            description: "Command arguments"
          }
        }
      }
    });

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/kevinwatt/shell-mcp'

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