Skip to main content
Glama

shell_w

Display currently logged-in users and their active processes to monitor system activity through the Shell-MCP server.

Instructions

Show who is logged on and what they are doing

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
argsNoCommand arguments

Implementation Reference

  • Defines the command configuration, description, allowed arguments, and timeout for the 'shell.w' entry, which corresponds to the 'shell_w' MCP tool. This acts as the input validation schema.
    'shell.w': {
      command: 'w',
      description: 'Show who is logged on and what they are doing',
      allowedArgs: ['-h', '-s', '--no-header', '--help'],
      timeout: 2000
    },
  • src/index.ts:27-43 (registration)
    Registers the 'shell_w' tool (from 'shell.w') by dynamically generating tool list with name 'shell_w', description from config, and standard input schema for args array.
    server.setRequestHandler(ListToolsRequestSchema, async () => {
      const tools = Object.entries(allowedCommands).map(([name, config]) => ({
        name: name.replace('shell.', 'shell_'),  // Replace shell. with shell_
        description: config.description,
        inputSchema: {
          type: "object",
          properties: {
            args: {
              type: "array",
              items: { type: "string" },
              description: "Command arguments"
            }
          }
        }
      }));
      return { tools };
    });
  • Generic MCP tool call handler that processes 'shell_w' tool invocations: converts 'shell_w' to 'shell.w', retrieves config and actual command 'w', validates args, executes via executor, and streams stdout as text content.
    server.setRequestHandler(CallToolRequestSchema, async (request) => {
      try {
        const command = String(request.params?.name || '');
        const fullCommand = `shell.${command.replace('shell_', '')}`;  // Replace shell_ back to shell.
        
        if (!(fullCommand in allowedCommands)) {
          return {
            content: [{ type: "text", text: `Unknown command: ${command}` }],
            isError: true
          };
        }
        
        const actualCommand = allowedCommands[fullCommand].command;
        const args = Array.isArray(request.params?.arguments?.args)
          ? request.params.arguments.args.map(String)
          : [];
      
        validator.validateCommand(actualCommand, args);
        const stream = await executor.execute(actualCommand, args);
      
        return {
          content: [{
            type: "text",
            text: await new Promise((resolve, reject) => {
              const chunks: Buffer[] = [];
              stream.stdout.on('data', (chunk: Buffer) => chunks.push(chunk));
              stream.stdout.on('end', () => resolve(Buffer.concat(chunks).toString()));
              stream.stdout.on('error', reject);
            })
          }]
        };
      } catch (error) {
        return {
          content: [{ 
            type: "text", 
            text: `Command execution failed: ${error instanceof Error ? error.message : String(error)}` 
          }],
          isError: true
        };
      }
    });
  • Executes the underlying shell command ('w' for shell_w) by spawning child_process.spawn('w', args), handling security/cache/timeout, and returning stdout stream.
    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
        };
  • Validates command and arguments for shell_w using the 'shell.w' config's allowedArgs, path restrictions, and other security checks.
    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`);
      }
    }

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