shell_ip
Manage and analyze network routing, devices, and interfaces through whitelisted shell commands. Execute commands securely with controlled resources and timeouts via the Shell-MCP server.
Instructions
Show / manipulate routing, network devices, interfaces and tunnels
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| args | No | Command arguments |
Implementation Reference
- src/config/allowlist.ts:170-188 (registration)Configuration for the 'shell.ip' command, which registers the IP networking tool. This defines the executable 'ip', allowed arguments, description, and execution 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 },
- src/mcp/server.ts:105-147 (registration)Dynamically generates MCP tool definitions (including 'ip' from 'shell.ip') for the listTools request, stripping 'shell.' prefix, setting description and standard inputSchema.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; }
- src/mcp/server.ts:123-136 (schema)Defines the input schema for all shell tools including 'ip': 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" } } } });
- src/mcp/server.ts:172-247 (handler)MCP CallTool handler: for tool name 'ip', maps to 'shell.ip', retrieves config, validates args, executes via executor, collects stdout and returns 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 ); } });
- src/core/executor.ts:31-139 (handler)Executes the underlying 'ip' command by spawning child_process with args, streams stdout, handles caching, timeouts, interrupts, and security validation.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) } ); } }