はじめに
Emacs内での大規模言語モデル(LLM)との連携は、ここ数年で劇的な進化を遂げてきました。
思い返せば2022年11月、初めて copilot.el によるAI autocompletion を体験したときの感動は鮮明に覚えています。
その後間もなくChatGPT が登場し、ChatGPT.el 等でEmacs内でのAIとの対話ができるようになりましたが、当時は機能も少なく、EmacsでLLMを使うメリットはあまり感じられませんでした。
ブレークスルーとなったのは、東京Emacs勉強会 サマーフェスティバル2024 のTomoya さんの発表で ellama を知ったことでした。 ellamaでは従来の対話形式だけでなく、様々な関数を通じてLLMを利用でき、Emacs が何倍も強力な開発環境へと変貌しました。
そして最近では gptel が llm module として doom emacs に正式に組み込まれ、ツール連携やプロンプト/コンテクストエンジニアリング1が非常にスムーズになりました。
これとmcp.el と組み合わせることで、ある種 LLM in emacs のplateau を感じている今、タイミング的にちょうど良いかなと思い、この記事を書くことにしました。 gptel, mcp.el の設定方法や使う理由、補足としてmcp.el を用いる際のNixOS におけるエラー処理の方法などを紹介します。
gantt title 私のEmacs 内LLM 遍歴 todayMarker off dateFormat YYYY-MM axisFormat %Y-%m section 自動補完 copilot.el :2022-11, 32M section 黎明期 ChatGPT.el :2023-03, 17M org-ai :2023-12, 8M section 革命 ellama/llm :2024-08, 9M section 開拓期 ai-org-chat :2024-11, 2M ob-llm :2025-01, 2M elisa :2025-03, 1M copilot-chat.el :2025-03, 2M section 成熟期 gptel+mcp.el :2025-05, 2M claude-code.el :2025-06, 1M
- copilot-emacs/copilot.el: An unofficial Copilot plugin for Emacs.
- joshcho/ChatGPT.el: ChatGPT in Emacs
- rksm/org-ai: Emacs as your personal AI assistant
- s-kostyaev/ellama: Ellama is a tool for interacting with large language models from Emacs.
- ahyatt/llm: A package abstracting llm capabilities for emacs.
- ultronozm/ai-org-chat.el
- jiyans/ob-llm
- s-kostyaev/elisa: ELISA (Emacs Lisp Information System Assistant) is a system designed to provide informative answers to user queries by leveraging a Retrieval Augmented Generation (RAG) approach.
- chep/copilot-chat.el: Chat with Github copilot in Emacs !
- karthink/gptel: A simple LLM client for Emacs
- lizqwerscott/mcp.el: An Mcp client inside Emacs
- stevemolitor/claude-code.el: Claude Code Emacs integration
gptel
とは
gptelは、Emacsのためのシンプルかつ強力な大規模言語モデル(LLM)クライアントです。 Emacs内のあらゆる場所から自由な形式でLLMと対話できる環境を提供します。 このデモを見ると使用イメージが分かりやすいです。
mcp.el
とは
mcp.elは、AIと外部ツールの連携を標準化するオープンプロトコル「Model Context Protocol (MCP)」をEmacsに導入するパッケージです 。 これにより、 gptelのようなLLMクライアントが、Web検索、ファイルアクセス、GitHubリポジトリ操作といった様々な機能を備えたMCPサーバーと統一的に通信できるようになり、LLMの能力を大幅に拡張します。 mcp.elは、これらの外部ツール(サーバー)の起動や管理をEmacs内から一元的に行うハブとして機能します。
私が gptel & mcp.el
を使う理由
gptel
は doom emacs のmodule に組み込まれており、ほぼカスタマイズなしで、LLM modelやsystem prompt、 context, tool(MCP) の管理がスムーズに行えます。
また、対話形式でのセッションはPlain text (org/markdown)のバッファ上で行われるので、例えば「最初にローカルLLMやコストの小さいモデルで質問し、回答が不十分だったらモデルを切り替えて再度リクエストを送る」、のようなことが素早く簡単にできます。
LLMは過去の自身の回答内容に捉われたり、コンテクストが長くなるに連れて精度が落ちたりすること (AI Cliff)が知られていますが、 gptel
ではLLM の過去の回答を削除/編集したうえで再度質問することもできるので、これを抑制することができる点も非常に気に入っています。
最近では多くの人がClaude-Code や Cursor といったAgent系のツールを使っていると思いますが、これらと違って常に主導権が自分にあり、LLMに思考を乗っ取られづらいという点も使い続ける理由の一つです。 (ただ、なるべく頭を使わずに早く成果物をとりあえず得たいときに、Claude-Code を使うこともあります。)
このように、 gptel & mcp.el
はユーザーの主体性を保ちながら、LLMの力を最大限にもたらしてくれる、必要不可欠なツールとなっています。
私の gptel
の設定
(use-package! gptel
:config
(require 'gptel-integrations)
(setq gptel-model 'gpt-4.1
gptel-default-mode 'org-mode
gptel-use-curl t
gptel-use-tools t
gptel-confirm-tool-calls 'always
gptel-include-tool-results 'auto
gptel--system-message (concat gptel--system-message " Make sure to use Japanese language.")
gptel-backend (gptel-make-gh-copilot "Copilot" :stream t))
(gptel-make-xai "Grok" :key "your-api-key" :stream t)
(gptel-make-deepseek "DeepSeek" :key "your-api-key" :stream t))
どの backend を使うか
gptel の README を見れば分かるように、gptel ではかなり多くの LLM の backend を使うことができます。
目的や使用量、予算によって選ぶべき backend は変わるのですが、 自分は Github Copilot Pro ($100.00 per year) に契約して、Github Models をメインの backend として使っており、今のところ満足しています。
Github Models backend を使っている理由
- AI 自動補完 のために pro plan を既に買っていたので追加コストがゼロだった
- 従量課金ではなく、定額制だから、コストを気にせず使える
- openai7種、Gemini 2種、Claude 3種の計12種類のモデルが使える2 (2025年6月現在)
モデルの使い分け
実際、毎回どのモデルを使うのかを考えるのは面倒なので、だいたい以下のように使い分けています。
- 要約や翻訳、commit メッセージの生成などの簡単なタスク: GPT-4.1
- コーディング: Claude Sonnet 4
- 勉強・長いコンテクストが必要なタスク: Gemini 2.5 Pro
モデル | MMLU スコア | インテリ指数 | 速度 (トークン/秒) | レイテンシ (秒) | コンテキスト長 (トークン) | 倍率 | 最適な用途 |
---|---|---|---|---|---|---|---|
無料 | |||||||
GPT-4.1 ⭐ | 80.6% | 53 | 155.6 | 0.42 | 1M | 0 | 一般的なコーディング 長文コンテキスト分析 新デフォルト |
GPT-4o ⚠️ | 74.8% | 41 | - | - | 128k | 0 | マルチモーダルタスク 高速反復 |
高速・低コスト | |||||||
Gemini 2.0 Flash | 78.2% | 46 | 230.5 | 0.24 | 1M | 0.25x | 高速プロトタイピング コスト重視プロジェクト |
o3-mini ⚠️ | 79.1% | 63 | 166.3 | 12.83 | 200k | 0.33x | 効率的推論 |
o4-mini | 83.2% | 70 | 149.7 | 40.10 | 128k | 1x | 合理的コストでの高度推論 |
高性能・バランス型 | |||||||
Claude 3.5 Sonnet | 77.2% | 44 | - | - | 200k | 1x | コードワークフロー チャート解釈 |
Claude 3.7 Sonnet | 80.3% | 48 | 79.0 | 1.24 | 200k | 1x | 柔軟な推論 バランス型パフォーマンス |
Claude 3.7 Sonnet Thinking | 80.3% | 48 | 79.0 | ~2.5 | 200k | 1.25x | プロセス可視化 段階的推論 |
Claude Sonnet 4 ⭐ | 83.7% | 53 | 49.1 | 1.33 | 200k | 1x | コーディング強化 命令理解向上 |
Gemini 2.5 Pro ⭐ | 86.2% | 70 | 146.4 | 35.45 | 1M | 1x | 高度推論 科学計算 |
o1 ⚠️ | 84.1% | 62 | 206.1 | 12.91 | 200k | 1x | 複雑な問題解決 |
o3 | 85.3% | 70 | 142.0 | 16.39 | 130k | 1x | 高度推論 研究タスク |
最上位・特化型 | |||||||
GPT-4.5 ⚠️ | - | 53 | 77.0 | 0.94 | 130k | 50x | クリエイティブライティング 事実知識 |
Claude Opus 4 ⭐ | - | - | - | - | 200k | 10x | 自律的長時間タスク 複雑ワークフロー |
- ⚠️ 廃止予定のモデル
- ⭐ オススメモデル
私の mcp.el
の設定
(use-package! mcp
:after gptel
:custom
(mcp-hub-servers
`(("github" . (:command "docker"
:args ("run" "-i" "--rm"
"-e" "GITHUB_PERSONAL_ACCESS_TOKEN"
"ghcr.io/github/github-mcp-server")
:env (:GITHUB_PERSONAL_ACCESS_TOKEN ,(get-sops-secret-value "gh_pat_mcp"))))
("duckduckgo" . (:command "uvx" :args ("duckduckgo-mcp-server")))
("nixos" . (:command "uvx" :args ("mcp-nixos")))
("fetch" . (:command "uvx" :args ("mcp-server-fetch")))
("filesystem" . (:command "npx" :args ("-y" "@modelcontextprotocol/server-filesystem" ,(getenv "HOME"))))
("context7" . (:command "npx" :args ("-y" "@upstash/context7-mcp") :env (:DEFAULT_MINIMUM_TOKENS "6000")))))
:config (require 'mcp-hub)
:hook (after-init . mcp-hub-start-all-server))
(nixOS) uvx
で起動するmcp server が Could not start dynamically linked executable
というエラー
私の mcp.el
の設定 にあるようにmcp.el で使ういくつかの mcp server の起動で uvx
を使用しているのですが、nixOS で最初に起動したときにエラーが出てしまいました。
解決方法と原因を Gemini に調べさせたので、メモしておきます。
症状とエラーメッセージ
*Mcp-Hub*
buffer で mcp-hub-view-log
を確認すると、以下のエラーが出ていました:
[stderr] Could not start dynamically linked executable: /home/bk/.cache/uv/archive-v0/unI5q9QapqXHm9fPXna4G/bin/python
[stderr] NixOS cannot run dynamically linked executables intended for generic
[stderr] linux environments out of the box. For more information, see:
[stderr] https://nix.dev/permalink/stub-ld
[jsonrpc] D[14:56:49.461] Connection state change: `exited abnormally with code 127
解決策:nix-ld を有効にする
nix conifguration で、以下を足したら直りました。
programs.nix-ld.enable = true;
根本原因: ファイルシステムの思想的対立
このエラーの根本原因を理解するには、一般的なLinuxディストリビューションとNixOSとの間にある、ファイルシステムに関する哲学的な違いを理解する必要があります。
FHSの世界
ほとんどのLinuxディストリビューションは、Filesystem Hierarchy Standard (FHS) に準拠しています。
これは /bin
に実行ファイル、 /usr/lib
に共有ライブラリといった、ファイルシステムの標準的なディレクトリ構造を定めたものです 。
外部でコンパイルされたバイナリは、共有ライブラリ( .so
ファイル)や、バイナリを実行するために不可欠な動的リンカ(例: ld-linux-x86-64.so.2
)が、これらの標準的な場所に存在することを前提としています。
NixOSの世界
NixOSは意図的にFHSを採用しません。
すべてのパッケージとその依存関係は、 /nix/store
内の、ハッシュ値を含む一意で隔離されたパスにインストールされます。
各バイナリはビルド時にパッチが当てられ、依存するライブラリをこの特定の /nix/store
パスから探すように変更されます。
この仕組みこそが、NixOSの純粋性、再現性、そして信頼性の源泉です 。
衝突の瞬間
今回のエラーは、この二つの世界の思想が衝突する瞬間に発生します。
ユーザーのケースでは、uvxが実行しようとしているMCPサーバー(内部的にはPythonで書かれている)は、FHSの世界でコンパイルされたバイナリです。
Linuxカーネルがこのバイナリを実行しようとすると、まずELFヘッダにハードコードされた動的リンカ(例: /lib64/ld-linux-x86-64.so.2
)を探しに行きます。
しかし、標準的なNixOSシステムにはそのパスに動的リンカは存在しないため、カーネルは実行を開始できず、 Could not start dynamically linked executable
というエラーを吐き出して即座に失敗するのです 。
nix-ld を有効にするとうまくいく理由
nix-ldを有効にすると、FHSが期待する標準的な動的リンカのパス(例: /lib64/ld-linux-x86-64.so.2
)に、nix-ldが提供する特別なプログラムが配置されます 。
カーネルがFHS準拠の非Nixバイナリを実行しようとすると、今度はこのnix-ldのシムプログラムの起動に成功します。
起動したシムプログラムは、 NIX_LD_LIBRARY_PATH
という特別な環境変数を読み取ります。この環境変数には、 programs.nix-ld.libraries
で指定されたライブラリが格納されている /nix/store
内のパスが設定されています。
この情報を使って、シムプログラムはNixOSの真の動的リンカを /nix/store
から探し出し、必要なライブラリのパス情報を引き渡して実行します。
結果として、本来NixOS上では実行できなかったFHSバイナリが、あたかも標準的なLinux環境にいるかのように、自身の依存ライブラリを見つけて正常に起動できるようになるのです 。
より広い文脈:代替策とトレードオフ
nix-ldは強力な解決策ですが、唯一のものではありません。この問題を解決する別のアプローチとしてbuildFHSEnv(や、それをラップしたsteam-runなど)が存在します 。 buildFHSEnvは、システム全体にFHS互換のシムを導入するのではなく、特定のアプリケーションのためだけに、一時的でサンドボックス化されたFHS環境を構築します。 この二つの選択は、NixOSコミュニティで頻繁に議論される、古典的なエンジニアリング上のトレードオフを象徴しています 。
nix-ld
: 実用主義(Pragmatism)。システム全体の互換性を高め、非Nixバイナリを「ただ動かす」ことを容易にします。その代償として、NixOSの純粋性をわずかに損なう可能性があります。buildFHSEnv
: 純粋主義(Purity)。アプリケーションごとに厳格な隔離を保ち、NixOSの思想を忠実に守ります。しかし、アプリケーションごとに明示的な設定が必要であり、手間がかかる場合があります。
教訓
この問題解決のプロセスは、単なるバグ修正以上の教訓を含んでいます。
それは、依存関係の管理と再現可能な環境の確保という、現代のソフトウェア開発におけるより大きな挑戦の縮図です。
クリーンで宣言的なシステム(NixOS)と、外部の混沌とし、命令的で、プリコンパイルされたソフトウェアエコシステムとの境界で発生する摩擦(インピーダンスミスマッチ)を、どのようにして管理するか。
nix-ld
のようなツールは、異なるソフトウェア哲学の間を繋ぐ「Foreign Function Interface (FFI)」のようなものであり、この課題に対する一つの洗練された答えなのです。この経験は、単なるトラブルシューティングに留まらず、システム統合の本質を理解する上での貴重な学びとなります。