🦖

any-script-mcp で Deno や Python で気軽に MCP tool を作る

に公開

LayerX でスタッフソフトウェアエンジニアをしている @izumin5210 です。バクラク事業部の Platform Engineering 部 Enabling チームでいろいろしています。
前回紹介したおもちゃに新機能を追加したので自慢させてください。


3行まとめ

  • any-script-mcpshell オプションが追加され、bash 以外でスクリプトを実行できるようになった
  • 例えば Deno や Python を使うことで、third party package を使い LLM を活用するような Tool を YAML 1枚のまま記述できる
  • 何かおもしろい使い方を探してみてください

前回の記事では、any-script-mcp という MCP server を利用し、シェルスクリプトで気軽に MCP tool を作る方法を紹介しました。

https://zenn.dev/layerx/articles/fdc7174d9a9386

これだけでもそこそこ便利なのですが、より高度な、たとえば LLM を利用するようなツールを実装しようとするとまだ面倒なことがあります。

以下は README や前回の記事でも紹介した、Codex を利用して GPT-5 による情報検索・調査をするための MCP Tool の設定です。
Codex や Claude Code はそのままだと結論以外も出力してしまうため、Tool 呼び出し元のコンテキストを余計に圧迫してしまいます。 それを防ぐために --json をつけた上で jq で結論だけのを抜き出すようにする…などの工夫が必要になります。
また、Codex や Claude Code それぞれのツールの独自の設定やオプションを気にしないといけない場合もあるでしょう。

tools:
  - name: gpt5-search
    description: ...
    inputs:
      prompt:
        type: string
        description: ...
        required: true
    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'

アイディア: GitHub Actions の shell

ところで any-script-mcp のアイディア元になった GitHub Actions には、 shell という設定項目が存在します。 shell を指定すると、run に書いたスクリプトを任意のインタプリタで実行します。デフォルトは bash -e {0} ですが、pythonnode {0} なども指定できます。

- run: |
    import sys
    print(f"Hello from Python {sys.version}")
  shell: python

https://docs.github.com/en/actions/reference/workflows-and-actions/workflow-syntax#defaultsrunshell

YAML 内に複雑なスクリプトを書きすぎるとメンテナンスが厳しくなりますが、簡単な文字列処理や HTTP request であればシェルスクリプトよりも気軽に使えそうです。

これに近い機能を any-script-mcp でも実装してみました。

any-script-mcp の shell

使い方はシンプルです。 GitHub Actions と同じように tool の定義に shell を追加するだけ。

# yaml-language-server: $schema=https://raw.githubusercontent.com/izumin5210/any-script-mcp/main/config.schema.json
tools:
  - name: python_echo
    description: Pythonでテキストを処理する
    shell: "python {0}"
    inputs:
      text:
        type: string
        description: 処理するテキスト
    run: |
      import os
      import json
      
      text = os.environ['INPUTS__TEXT']
      result = {"processed": f"Python が処理しました: {text}"}
      print(json.dumps(result))

入力はシェルスクリプトの時と同じく環境変数で受け取り、結果は標準出力に出せば OK です。
引数は従来の INPUTS__<INPUT_NAME> に加え、型情報を維持するために INPUTS_JSON で JSON 文字列も受け取れるようになっています。

Deno/Python で third party package を使い、LLM を活用する MCP Tool を雑に作る

shell は引数にスクリプトが記述されたファイルを受け取れればだいたい何でも使えます。 なので node なども利用できます。 が、ここでのオススメは deno run です。

  • third party package を直接 import でき、管理用ファイルも省略可能(YAML 1枚のまま戦える)
  • secure by default により最小限の権限に絞ることができる

これらの特徴により、比較的安全かつ雑に LLM を利用するようなコードを記述することができます。

# yaml-language-server: $schema=https://raw.githubusercontent.com/izumin5210/any-script-mcp/main/config.schema.json
tools:
  - 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 client = new OpenAI({ apiKey: Deno.env.get("OPENAI_API_KEY") });
      const inputs = JSON.parse(Deno.env.get("INPUTS_JSON"));
      const res = await client.responses.create({
        model: "gpt-5",
        tools: [{ type: "web_search_preview" }],
        input: inputs.query,
        instructions: "...",
      });
      console.log(res.output_text);

また、最近の Python でも PEP 723 により1枚のスクリプトのままで third party package を利用できるようです。

# yaml-language-server: $schema=https://raw.githubusercontent.com/izumin5210/any-script-mcp/main/config.schema.json
tools:
  - name: list-peps
    description: "..."
    shell: "uv run -s {0}"
    run: |
      # /// script
      # requires-python = ">=3.11"
      # dependencies = [
      #   "requests<3",
      #   "rich",
      # ]
      # ///
      import requests
      from rich.pretty import pprint
      resp = requests.get("https://peps.python.org/api/peps.json")
      data = resp.json()
      pprint([(k, v["title"]) for k, v in data.items()][:10])

まとめ

any-script-mcp の shell によって、気軽に作れる MCP tool の幅は広がるはずです。 シェルスクリプトを頑張る必要もなく、慣れた言語で処理を記述できます。

また、Python や Deno を使うことで気軽さを保ちつつ third party package の恩恵を受けつつ比較的安全なツールを作成できる…はずです。

ぜひ何かおもしろい活用方法を探してみてください!

LayerX

Discussion