Claude Code 始めました
aiterm-mcp を npm に公開した — AIに「1本の永続ターミナル」を握らせてトークンを削るMCPサーバ

aiterm-mcp っていうMCPサーバをnpmに公開した。AIにターミナルを「1本の永続セッション」として握らせるためのもの。

AIのターミナル作業は、見えないところでトークンが溶ける

AIにサーバー作業をさせるとき、たいていは1コマンドずつ投げる。SSHなら ssh host "コマンド" をその都度。これ、1回ごとに「接続 → 認証 → 実行 → 切断」をフルで繰り返してる。

問題は、毎回ゼロからやり直すから状態が残らないこと。さっき cd したディレクトリも、source した環境も、つないだSSHも、次のコマンドでは消えてる。だからAIは毎回、

  • またSSHでつなぎ直して、
  • またディレクトリに移動して、
  • また環境を読み込んで、

から、ようやく本題のコマンドを打つ。この「やり直しの一式」を、コマンドを打つたびにAIが書いて、その出力を読まされる。本題と関係ない再接続・再認証・再セットアップのテキストが、毎ターン、コンテキストに積み上がる。トークンが、何も生み出さないやり直しに溶けていく。

aitermはここを畳む。端末を1本だけ永続で握って、その中でSSHは一度だけ張る。10コマンド打とうが、ssh を呼ぶのは最初の1回きり。接続も認証もN回から1回に減るcd も環境も最初の1回。以降のコマンドは全部、同じ1本のセッションに素のまま乗る。やり直しの一式が、まるごと消える。

どれくらいか、自分のサーバーで測った。SSHでログインすると、それだけで定型文(システム情報やお知らせ=MOTD)が 約385トークンぶんAIに渡る。1コマンドごとに繋ぎ直す細切れモードだと、これが毎回乗る。10コマンドの作業なら、本題に入る前の定型文だけで約3,800トークン。端末を1本握れば、払うのは最初の1回きり。残りはゼロだ。

読む前に、出力も間引く

トークン節約はもう1段ある。aitermはAIが出力を読む前に間引く。

先に断っておくと、この削減ロジックは自分の発明じゃないrtk(Rust Token Killer) のロジックをそっくり移植させてもらった。rtk は Patrick Szymkowiak 氏が作った、コマンド出力をLLMに渡す前に圧縮するツールだ(Apache-2.0)。aitermはそれを、別バイナリを呼ばずに端末読み取りの中で完結するよう自前で再実装した(ファイルは複製せず、挙動を合わせてある。pytestの要約は rtk 0.42.0 と一致するよう回帰テストで固定した)。

やってることは、

  • 制御文字(色やカーソル移動)を除去
  • 繰り返し行を件数に畳む
  • 長すぎる出力は頭と末尾を残して中略(復元用のヒント付き)
  • git status / git log / grep / pytest みたいな定番コマンドは、コマンド別の要約器で要点だけに

同じく自分のサーバーで、AIが受け取る出力を「素のまま」と「aiterm経由」で測ってみた。

AIが受け取る出力素のままaiterm経由
SSHログインの定型文(MOTD)約385 tok約350 tok
docker ps -a(コンテナ33個)約2,355 tok約2,218 tok
ログ120行(journalctl約4,375 tok約1,696 tok
git log(25件)約473 tok約338 tok

削り具合は中身しだいだ。反復の多いログはごっそり落ちる(120行で−61%)。一方、固有値ばかりの横長な表(コンテナ一覧)は−6%しか縮まない。一律で何%減みたいな魔法じゃなくて、「無駄なところだけ削る」が正直なところ。それでも、再接続の定型文を毎回読まされずに済むぶんと合わせれば、トークンは確実に積もらなくなる。

トークンだけの問題じゃなかった

ここまでは節約の話。でも、1コマンド=1接続の細切れには、もっと笑えない副作用があった。

接続を速いペースで繰り返すと、自分のサーバーの防御が、自分を攻撃者だと判断する

  • ログイン試行を監視するツールが、連続接続をブルートフォースと見なしてBANする
  • 同時接続数やセッション数の上限に引っかかって、新しい接続が蹴られる
  • 最終的にアカウントがロックされる

攻撃者を止めるための仕組みに、作った本人が締め出される。自宅サーバーで実際にやられた。

実は前にも近いことを書いた。サーバーをAIに任せた記録で、監視スクリプトが同時接続の上限を自分で踏んで、自分のSSHを失敗させてた、というのがあった。あのときは「スクリプト」。今度は「AIエージェント」が、コマンドを打つたびに同じ穴に落ちてた。原因は同じ——接続が増えすぎること。

端末を1本に畳むと、これも消える。認証は1回、セッションは1本、増殖しない。だから接続レート制限にもBANにも引っかからない。

設計:握るのは「端末1個」だけ

aitermの考え方はシンプルで、プリミティブは**「ローカル端末を1個握る」だけ**。

最初はSSH用の道具、コンテナ用の道具……と種類ごとに増やそうとしてた。でも際限がない。だから全部やめた。SSHもdocker execも対話シェル(REPL)も専用ツールにせず、「その端末に打ち込む1行」に格下げした。

pty_open()                      # 端末を1本握る
pty_send(id, "ssh 192.168.1.2") # その中で一度だけSSH
pty_send(id, "uname -a")        # 以降は同じセッションに乗る
pty_read(id)                    # 間引かれた出力を読む

だから道具はたった6個(開く・送る・読む・キー送信・閉じる・一覧)。SSHかローカルかコンテナか、という区別をツールの階層に持ち込まない。

裏側はtmux

端末の実体はtmuxのセッション。これのおかげで、

  • MCPサーバやAIクライアントが再起動しても、端末は生き続ける
  • 人間が tmux attach同じ画面を後ろからライブで覗ける(AIが今サーバーで何をやってるか、横で見られる)

正直な注:往復そのものは消せない

盛らずに書いておく。「AIにターミナルを生で直結すれば往復ゼロ」みたいなことはできない。AIは出力を読んでから次の入力を決めるので、「送る → 読む → 決める」のループは原理的に残る。aitermが消したのは、その往復に毎回くっついてた再認証・つなぎ直し・再セットアップのコストと、出力のノイズ。往復の回数じゃなくて、1往復あたりの重さを削ってる。

インストール

クローンもビルドも要らない。

claude mcp add --scope user --transport stdio aiterm -- npx -y aiterm-mcp

Claude Codeを再起動して /mcp でつながってれば完了。ClaudeでもCodexでも、MCPクライアントなら何でも npx -y aiterm-mcp で起動できる。

要件

  • Node.js 18+
  • tmux(apt install tmux / brew install tmux
  • Linux / WSL2 / macOS / Windows ネイティブ対応(Windowsはtmuxが無いので、中でWSLのtmuxへ橋渡しする)

ステータス

v0.4.0、MIT、provenance付きでnpm公開済み。

aiterm-mcp — GitHub

バグ報告・PRも歓迎。