any-script-mcp で Deno や Python で気軽に MCP tool を作る
LayerX でスタッフソフトウェアエンジニアをしている @izumin5210 です。バクラク事業部の Platform Engineering 部 Enabling チームでいろいろしています。
前回紹介したおもちゃに新機能を追加したので自慢させてください。
3行まとめ
-
any-script-mcp に
shellオプションが追加され、bash 以外でスクリプトを実行できるようになった - 例えば Deno や Python を使うことで、third party package を使い LLM を活用するような Tool を YAML 1枚のまま記述できる
- 何かおもしろい使い方を探してみてください
前回の記事では、any-script-mcp という MCP server を利用し、シェルスクリプトで気軽に MCP tool を作る方法を紹介しました。
これだけでもそこそこ便利なのですが、より高度な、たとえば 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} ですが、python や node {0} なども指定できます。
- run: |
import sys
print(f"Hello from Python {sys.version}")
shell: python
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 の恩恵を受けつつ比較的安全なツールを作成できる…はずです。
ぜひ何かおもしろい活用方法を探してみてください!
Discussion