npm package: @iflow-mcp/izumin5210-any-script-mcp
Description: MCP server that exposes arbitrary CLI tools and shell scripts as MCP Tools via YAML configuration
Author: izumin5210
Latest version: 0.3.0

[![MSeeP.ai Security Assessment Badge](https://mseep.net/pr/izumin5210-any-script-mcp-badge.png)](https://mseep.ai/app/izumin5210-any-script-mcp)

# any-script-mcp

An MCP server that exposes arbitrary CLI tools and shell scripts as MCP Tools

[日本語版](./README.ja.md)

## Overview

An MCP server that publishes commands defined in YAML files as MCP Tools. By defining tools in a configuration file, you can execute arbitrary shell scripts from MCP clients.

## Installation

### npx

Claude Code:

```shell-session
$ claude mcp add any-script \
  -s user \
  -- npx any-script-mcp
```

json:

```json
{
  "mcpServers": {
     "any-script": {
       "command": "npx",
       "args": ["any-script-mcp"]
     }
  }
}
```

## Configuration

Create a configuration file at `$XDG_CONFIG_HOME/any-script-mcp/config.yaml` (typically `~/.config/any-script-mcp/config.yaml`).

You can also specify custom configuration file paths using the `ANY_SCRIPT_MCP_CONFIG` environment variable:

```shell-session
# Single configuration file
$ ANY_SCRIPT_MCP_CONFIG=/path/to/custom/config.yaml npx any-script-mcp

# Multiple configuration files (Unix/macOS - separated by colon)
$ ANY_SCRIPT_MCP_CONFIG=/path/to/custom.yaml:$XDG_CONFIG_HOME/any-script-mcp/config.yaml npx any-script-mcp

# Multiple configuration files (Windows - separated by semicolon)
$ ANY_SCRIPT_MCP_CONFIG=C:\path\to\custom.yaml;%APPDATA%\any-script-mcp\config.yaml npx any-script-mcp
```

When multiple configuration files are specified:
- All tools from all files are merged into a single collection
- If the same tool name appears in multiple files, the first occurrence takes precedence
- At least one valid configuration file must be successfully loaded
- This is useful for separating common tools from project-specific or personal customizations

### Testing Your Configuration

You can test your configuration using the MCP Inspector:

```shell-session
$ npx @modelcontextprotocol/inspector npx any-script-mcp
```

This will open a web interface where you can see your registered tools and test them interactively.

### Example Configuration

```yaml
# yaml-language-server: $schema=https://raw.githubusercontent.com/izumin5210/any-script-mcp/main/config.schema.json
tools:
  - name: echo
    description: Echo a message
    inputs:
      message:
        type: string
        description: Message to echo
    run: |
      echo "Received: $INPUTS__MESSAGE"
      
  - name: git_status
    description: Check git status with optional branch
    inputs:
      branch-name:
        type: string
        description: Branch to check out
        required: false
      verbose:
        type: boolean
        description: Show verbose output
        default: false
    run: |
      if [ -n "${INPUTS__BRANCH_NAME:-}" ]; then
        git checkout "$INPUTS__BRANCH_NAME"
      fi
      
      if [ "$INPUTS__VERBOSE" = "true" ]; then
        git status -v
      else
        git status
      fi
      
  # Delegate search to codex CLI. Inspired by https://github.com/yoshiko-pg/o3-search-mcp
  - name: codex-search
    description: AI agent with web search for researching latest information, troubleshooting program errors, discussing complex problems and design decisions, exploring advanced library usage, and investigating upgrade paths. Supports natural language queries.
    inputs:
      prompt:
        type: string
        description: What you want to search, analyze, or discuss with the AI agent
    run: |
      codex exec \
        --model gpt-5 \
        --sandbox workspace-write \
        --config "sandbox_workspace_write.network_access=true" \
        "$INPUTS__PROMPT" \
        --json \
        | jq -sr 'map(select(.msg.type == "agent_message") | .msg.message) | last'
    timeout: 600000  # 10 minutes for complex AI operations
      
  - name: build
    description: Run build process with tests
    run: |
      npm run build
      npm test
    timeout: 180000  # 3 minutes for build and test
```

## Configuration Format

### Tool Definition

Each tool has the following fields:

- `name`: Tool name (alphanumeric, underscore, and hyphen only)
- `description`: Tool description
- `inputs`: Input parameter definitions (object format)
- `run`: Shell script to execute
- `shell`: Shell command to execute the script (optional, default: `"bash -e {0}"`)
- `timeout`: Execution timeout in milliseconds (optional, default: 300000 = 5 minutes)

### Input Parameters

Each input parameter has the following fields:

- `type`: Parameter type (`string`, `number`, `boolean`)
- `description`: Parameter description
- `required`: Whether the parameter is required (default: `true`)
- `default`: Default value (optional)

Input parameters are passed as environment variables to shell scripts in two ways:

#### Individual Environment Variables
Variable names have the `INPUTS__` prefix and are converted to uppercase (hyphens are converted to underscores).

Examples:
- `message` → `$INPUTS__MESSAGE`
- `branch-name` → `$INPUTS__BRANCH_NAME`

#### JSON Format (INPUTS_JSON)
All inputs are also available as a single JSON object in the `INPUTS_JSON` environment variable. This preserves type information, making it easier to work with non-shell interpreters.

Example usage:
```javascript
// Node.js
const inputs = JSON.parse(process.env.INPUTS_JSON);
console.log(inputs.num * 2); // count is a number, not a string
```

### Shell Option

The `shell` option allows you to specify a custom shell or interpreter for executing scripts. The `{0}` placeholder is replaced with the path to the temporary script file.

Default: `"bash -e {0}"`

Examples:

```yaml
# yaml-language-server: $schema=https://raw.githubusercontent.com/izumin5210/any-script-mcp/main/config.schema.json
tools:
  # Python script
  - name: python_analysis
    description: Analyze data with Python
    shell: "python {0}"
    inputs:
      data:
        type: string
        description: Data to analyze
    run: |
      import os
      import json
      
      data = os.environ['INPUTS__DATA']
      # Process data with Python
      result = {"analysis": f"Processed: {data}"}
      print(json.dumps(result))

  # Deno script
  - name: deno_fetch
    description: Fetch data with Deno
    shell: "deno run --allow-net {0}"
    inputs:
      endpoint:
        type: string
        description: API endpoint
    run: |
      const endpoint = Deno.env.get("INPUTS__ENDPOINT");
      const response = await fetch(endpoint);
      console.log(await response.json());
  
  # Using INPUTS_JSON for type preservation
  - name: add_2
    description: add 2 to a number
    shell: "node {0}"
    inputs:
      num:
        type: number
        description: a number to add 2 to
    run: |
      const inputs = JSON.parse(process.env.INPUTS_JSON);
      console.log(inputs.num + 2); // number is a number, not a string
```

#### Advanced Examples - AI Agents with Web Search

```yaml
# yaml-language-server: $schema=https://raw.githubusercontent.com/izumin5210/any-script-mcp/main/config.schema.json
tools:
  - name: gemini-search
    description: AI agent with web search using Gemini 2.5 Flash
    shell: "deno run -N -E {0}"
    inputs:
      query:
        type: string
        description: Query for AI search
        required: true
    run: |
      import { GoogleGenAI } from "npm:@google/genai@^1";
      const inputs = JSON.parse(Deno.env.get("INPUTS_JSON"));
      const ai = new GoogleGenAI({ apiKey: Deno.env.get("GEMINI_API_KEY") });
      const res = await ai.models.generateContent({
        model: "gemini-2.5-flash",
        contents: inputs.query,
        config: {
          tools: [{ googleSearch: {} }],
          systemInstruction: "...",
        },
      });
      console.log(
        res.candidates?.[0]?.content?.parts?.map((p) => p.text ?? "").join(""),
      );

  - name: gpt-5-search
    description: AI agent with web search using GPT-5
    shell: "deno run -N -E {0}"
    inputs:
      query:
        type: string
        description: Query for AI search
        required: true
    run: |
      import OpenAI from "jsr:@openai/openai";
      const inputs = JSON.parse(Deno.env.get("INPUTS_JSON"));
      const client = new OpenAI({ apiKey: Deno.env.get("OPENAI_API_KEY") });
      const res = await client.responses.create({
        model: "gpt-5",
        tools: [{ type: "web_search_preview" }],
        input: inputs.query,
        instructions: "...",
      });
      console.log(res.output_text);
```

## License

MIT