Skip to main content
Glama
REMnux

REMnux MCP Server

Official
by REMnux

run_tool

Execute malware analysis commands on REMnux to extract strings, analyze files, and investigate suspicious samples using tools like pestr and strings.

Instructions

Execute a command in REMnux. Supports piped commands (e.g., 'oledump.py sample.doc | grep VBA'). String extraction: For PE files use 'pestr'; for non-PE use 'strings' (ASCII) and 'strings -el' (Unicode).

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
commandYesCommand to execute (can include pipes, e.g., 'strings sample.exe | grep -i password')
input_fileNoInput file path (relative to samples dir, or absolute path in local mode) - appended to command
timeoutNoTimeout in seconds (default: 300)

Implementation Reference

  • Main handler function handleRunTool that executes commands in REMnux. Implements path validation, command security checks, discouraged pattern warnings, command execution, output truncation, auto-parsing, and advisory generation.
    export async function handleRunTool(
      deps: HandlerDeps,
      args: RunToolArgs
    ) {
      const startTime = Date.now();
      const { connector, config } = deps;
    
      // Build full command
      let fullCommand = args.command;
      if (args.input_file) {
        // Validate input file path (skip unless --sandbox)
        if (!config.noSandbox) {
          const pathValidation = validateFilePath(args.input_file, config.samplesDir);
          if (!pathValidation.safe) {
            return formatError("run_tool", new REMnuxError(
              pathValidation.error || "Invalid input file path",
              "INVALID_PATH",
              "validation",
              "Use a relative path within the samples directory",
            ), startTime);
          }
        }
        // Append quoted file path to command (single-quotes prevent shell expansion)
        // Escape any single quotes in the path as defense-in-depth (isPathSafe also rejects them)
        const escapedFile = args.input_file.replace(/'/g, "'\\''");
        const resolvedInputFile = (config.mode === "local" && args.input_file.startsWith("/")) ? escapedFile : `${config.samplesDir}/${escapedFile}`;
        fullCommand = `${args.command} '${resolvedInputFile}'`;
      }
    
      // Security: Validate command against blocklist
      const validation = isCommandSafe(fullCommand);
      if (!validation.safe) {
        return formatError("run_tool", new REMnuxError(
          validation.error || "Command blocked",
          "COMMAND_BLOCKED",
          "security",
          "This command is blocked for security reasons. Use an allowed tool instead.",
        ), startTime);
      }
    
      // Check for discouraged patterns BEFORE execution (unless acknowledged)
      // Use regex to ensure --acknowledge-raw is a flag, not part of a filename
      const hasAcknowledge = /(?:^|\s)--acknowledge-raw(?:\s|$)/.test(args.command);
      if (!hasAcknowledge) {
        const discouraged = checkDiscouragedPattern(fullCommand);
        if (discouraged) {
          return formatResponse(
            "run_tool",
            {
              warning: discouraged.warning,
              suggestion: discouraged.suggestion,
              command_blocked: true,
              note: "Command was NOT executed. Use suggested alternatives or add --acknowledge-raw to proceed.",
            },
            startTime
          );
        }
      }
    
      const MAX_STDOUT_RESPONSE = 100 * 1024; // 100KB — fits in LLM context
      const MAX_STDERR_RESPONSE = 50 * 1024;
    
      try {
        const execOptions: { timeout: number; cwd?: string } = {
          timeout: (args.timeout || config.timeout) * 1000,
        };
    
        // Only set cwd when input_file is provided (file-based analysis)
        if (args.input_file) {
          execOptions.cwd = config.samplesDir;
        }
    
        const result = await connector.executeShell(fullCommand, execOptions);
    
        let stdout = result.stdout || "";
        let stderr = result.stderr || "";
    
        stderr = filterStderrNoise(stderr);
    
        let truncated = false;
        const fullStdoutLength = stdout.length;
    
        if (stdout.length > MAX_STDOUT_RESPONSE) {
          stdout = stdout.slice(0, MAX_STDOUT_RESPONSE);
          truncated = true;
        }
        if (stderr.length > MAX_STDERR_RESPONSE) {
          stderr = stderr.slice(0, MAX_STDERR_RESPONSE);
          truncated = true;
        }
    
        // Auto-parse output if command matches a known tool with a parser
        let findings;
        let parsedMetadata;
        const toolName = detectToolName(fullCommand);
        if (toolName && hasParser(toolName) && stdout) {
          const parsed = parseToolOutput(toolName, stdout);
          if (parsed.parsed) {
            findings = parsed.findings;
            parsedMetadata = parsed.metadata;
          }
        }
    
        // Check for advisory (non-blocking guidance)
        const advisory = getCommandAdvisory(fullCommand);
    
        return formatResponse("run_tool", {
          command: fullCommand,
          stdout,
          stderr,
          exit_code: result.exitCode,
          truncated,
          ...(truncated && {
            truncation_notice: "Output exceeded response size limit. Use 'run_tool' with '| head -N' or redirect to a file for full output.",
            full_stdout_length: fullStdoutLength,
          }),
          ...(findings && { findings, parsed_metadata: parsedMetadata }),
          ...(advisory && { advisory }),
        }, startTime);
      } catch (error) {
        return formatError("run_tool", toREMnuxError(error, config.mode), startTime);
      }
    }
  • Zod schema definition for run_tool input validation. Defines three parameters: command (required string), input_file (optional string), and timeout (optional number with default 300).
    export const runToolSchema = z.object({
      command: z.string().describe("Command to execute (can include pipes, e.g., 'strings sample.exe | grep -i password')"),
      input_file: z.string().optional().describe("Input file path (relative to samples dir, or absolute path in local mode) - appended to command"),
      timeout: z.number().optional().describe("Timeout in seconds (default: 300)"),
    });
    export type RunToolArgs = z.infer<typeof runToolSchema>;
  • src/index.ts:94-101 (registration)
    MCP tool registration for run_tool. Registers the tool with name 'run_tool', description, schema validation (runToolSchema.shape), and maps to handleRunTool handler function.
    // Tool: run_tool - Execute a command in REMnux
    server.tool(
      "run_tool",
      "Execute a command in REMnux. Supports piped commands (e.g., 'oledump.py sample.doc | grep VBA'). " +
      "String extraction: For PE files use 'pestr'; for non-PE use 'strings' (ASCII) and 'strings -el' (Unicode).",
      runToolSchema.shape,
      (args) => handleRunTool(deps, args)
    );
  • Security validation function isCommandSafe that checks commands against blocked patterns (null byte injection, catastrophic commands). Called by handleRunTool to prevent dangerous command execution.
    export function isCommandSafe(command: string): { safe: boolean; error?: string } {
      // Reject empty or whitespace-only commands
      if (!command || command.trim() === "") {
        return { safe: false, error: "Empty command" };
      }
    
      // Check against blocked patterns
      for (const { pattern, category } of BLOCKED_PATTERNS) {
        if (pattern.test(command)) {
          return { safe: false, error: `Command blocked: ${category}` };
        }
      }
    
      // Check against dangerous pipe patterns
      for (const { pattern, category } of DANGEROUS_PIPE_PATTERNS) {
        if (pattern.test(command)) {
          return { safe: false, error: `Command blocked: ${category}` };
        }
      }
    
      return { safe: true };
    }
  • Utility function filterStderrNoise that removes common non-actionable warnings from tool stderr output (Volatility progress bars, Python warnings, compatibility notices). Called by handleRunTool to clean stderr before response.
    export function filterStderrNoise(stderr: string): string {
      return stderr
        // Volatility 3 progress bars
        .replace(/^Progress:\s+[\d.]+\s+.*$/gm, "")
        // Python SyntaxWarning / DeprecationWarning lines
        .replace(/^.*(?:SyntaxWarning|DeprecationWarning):.*$/gm, "")
        // Python version compatibility notices
        .replace(/^.*(?:This version of|requires Python).*$/gm, "")
        // Python source context lines (indented code following warnings)
        .replace(/^\s+\^+\s*$/gm, "")
        .trim();
    }
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden of behavioral disclosure. It describes what the tool does (execute commands, support pipes, string extraction techniques) but doesn't mention important behavioral aspects like security implications, error handling, output format, or execution environment constraints that would be crucial for an agent to use it effectively.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is extremely concise with just two sentences, both of which earn their place. The first sentence states the core purpose and key capability (piped commands), while the second provides specific guidance for string extraction scenarios. No wasted words or redundant information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a command execution tool with 3 parameters, 100% schema coverage, but no annotations and no output schema, the description provides adequate basic information but lacks crucial context about what the tool returns, error conditions, security considerations, or execution environment details that would help an agent use it correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already documents all three parameters thoroughly. The description adds some value by providing command examples that illustrate how parameters might be used together, but doesn't add significant semantic meaning beyond what's already in the parameter descriptions.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose with specific verbs ('execute a command in REMnux') and distinguishes it from siblings by focusing on command execution rather than file analysis, extraction, or management. It provides concrete examples that illustrate its unique functionality.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description offers clear context on when to use this tool (for executing commands in REMnux, including piped commands and string extraction), but doesn't explicitly state when not to use it or name specific alternatives among the sibling tools. The examples imply usage scenarios without formal exclusions.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

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/REMnux/remnux-mcp-server'

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