Skip to main content
Glama

shell_ip

Execute ip commands to manage routing, network devices, interfaces, and tunnels through a secure MCP server with controlled resource usage.

Instructions

Show / manipulate routing, network devices, interfaces and tunnels

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
argsNoCommand arguments

Implementation Reference

  • Schema and configuration for the 'shell.ip' tool, defining the underlying 'ip' command, description, allowed arguments for input validation, and timeout.
    'shell.ip': {
      command: 'ip',
      description: 'Show / manipulate routing, network devices, interfaces and tunnels',
      allowedArgs: [
        'addr',     // show addresses
        'link',     // show network devices
        'route',    // show routing table
        'neigh',    // show neighbor table
        '-br',      // brief output
        '-c',       // color output
        '-s',       // statistics
        '-d',       // details
        '-h',       // human readable
        '--help',
        'show',
        '*'         // allow interface names
      ],
      timeout: 3000
    },
  • Registration logic that dynamically creates MCP tool definitions, including the 'ip' tool from 'shell.ip' config, with name, description, and input schema for args array.
    private processTools(): Tool[] {
      const tools: Tool[] = [];
      const processedNames = new Set<string>();
    
      this.logger.debug('Starting to process tool list', { 
        commandCount: Object.keys(allowedCommands).length 
      });
    
      Object.entries(allowedCommands).forEach(([name, config]) => {
        const toolName = name.replace('shell.', '');
        this.logger.debug('Processing command', { 
          originalName: name,
          toolName,
          isProcessed: processedNames.has(toolName)
        });
    
        if (!processedNames.has(toolName)) {
          processedNames.add(toolName);
          tools.push({
            name: toolName,
            description: config.description,
            inputSchema: {
              type: "object",
              properties: {
                args: {
                  type: "array",
                  items: { type: "string" },
                  description: "Command arguments"
                }
              }
            }
          });
        }
      });
    
      this.logger.debug('Tool list processing completed', { 
        toolCount: tools.length,
        processedNames: Array.from(processedNames)
      });
    
      this.validateToolNames(tools);
      return tools;
    }
  • Primary MCP handler for calling tools like 'ip': resolves full command 'shell.ip', retrieves config, validates args, executes via CommandExecutor, and returns output stream as text content.
    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
        );
      }
    });
  • Core execution handler that spawns the child process for the base command 'ip' with provided arguments, handles security, caching, timeouts, and streams stdout.
    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)
          }
        );
      }
    }
  • Validates input arguments for shell.ip against allowedArgs from config, checks paths, timeouts, ensuring secure execution.
    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