Linux MCP daemon (mcpd) について
Linux MCP Daemon(mcpd)は、型付きツールを介してAIエージェントがLinuxホストを調査・管理できるGo製MCPサーバーです。呼び出しごとのプロセス分離と権限設定により、アクセス範囲を限定し操作を監査できます。
主な機能
- 01プロセス、メモリ、ディスク、サービスの確認
- 02ファイル管理とシステムログの読み取り
- 03ユーザーとツール単位の権限付与
- 04呼び出しごとに新しいワーカープロセスを使用
- 05呼び出しの引数と結果を記録
活用シーン
- 01Linuxホストの性能問題を診断
- 02SSHシェルを使わずサービスやログを確認
- 03エージェントの特権操作を制限
- 04サーバー上のエージェント操作を監査
MCP 対応
HTTPS上のSSEとJSON-RPCを通じて、Linuxシステムのツールとリソースを公開します。MCPクライアントはBearer Tokenで認証し、権限はユーザーとツールごとに設定します。
設定
リモートサーバーはインストール不要です。クライアントを選んで設定をコピーするか、ワンクリックで導入できます。
https://{host}:9091/sseclaude mcp add --transport sse nucleusv-linux-mcp-daemon https://{host}:9091/sse{"mcpServers":{"nucleusv-linux-mcp-daemon":{"type":"sse","url":"https://{host}:9091/sse"}}}Cursor で開く{"servers":{"nucleusv-linux-mcp-daemon":{"type":"sse","url":"https://{host}:9091/sse"}}}VS Code で開く{"mcpServers":{"nucleusv-linux-mcp-daemon":{"type":"sse","url":"https://{host}:9091/sse"}}}{"mcpServers":{"nucleusv-linux-mcp-daemon":{"serverUrl":"https://{host}:9091/sse"}}}コマンドはターミナルで実行し、JSON は各クライアントの MCP 設定に貼り付けて再起動します。認可が必要なサーバーは初回接続時にログインを求めます。
https://{host}:9091/sse認証が必要ツール
詳細

Linux MCP Daemon
AI エージェントと Linux オペレーティングシステムを直接、安全に接続するために設計された、高性能な Go ベースの Model Context Protocol(MCP)デーモン(mcpd)です。
概要
このプロジェクトは、依存関係をゼロにする(カーネル優先の)理念に基づいています。肥大化したサードパーティ製の解析ライブラリを必要とせず、生のシステムコールと Virtual File System(/proc、/sys)を介して、AI エージェントがホストの Linux システムを直接調査し、操作できるようにします。
AI エージェントとデーモンは、ポート 9091 の HTTPS 上で Server-Sent Events(SSE)と JSON-RPC を介して直接通信します(TLS はデフォルトで有効で、初回起動時に自己署名証明書が生成されます)。
理由
サーバーの運用を支援する AI エージェントには、サーバーの状態を確認する必要があります。負荷、メモリ、ディスク、プロセス、サービス、ログ、ネットワークなどを把握し、ときには操作する必要もあります。一般的な方法は SSH シェルですが、シェルでは何でも実行でき、そのアカウントがアクセス可能なファイルなら何でも扱えます。sudo を使えばすべて root 権限になり、後から何を行ったのか把握するのも困難です。mcpd はシェルの代わりに、エージェントへ型付きツールを提供します。
- シェルを使わずに診断できます。「サイトの動作が遅いのはなぜですか?」という質問には、
processes/top、memory/usage、disks/usage、logs/journal-control、logs/dmesg、network/connections、services/listが回答します。構造化された出力(json/yaml)なので、エージェントがtopやdfの出力を解析する必要はありません。 - **セッション単位ではなく、ツール単位で root 権限を付与できます。**ユーザーが各ツールを自身の OS アカウントで実行します。root 権限は
mcp-sudo.yamlでツールごとに付与し、パス、ネットワーク接続先、sysctl キーで制限できます。「root として/var/logを読み取り、サービスを再起動できる」と設定でき、「root 権限を持つ」状態を避けられます。 - **エージェントごとに、アカウントとトークンを用意できます。**各エージェントに専用のユーザーを割り当て、すべての呼び出しについてユーザー、ツール、引数(シークレットはマスキング)、結果を記録します。これにより、エージェントが行った操作の監査証跡を残せます。
- **小型で自己完結しています。**単一の静的 Go バイナリで、
/proc、/sys、systemd を D-Bus 経由で直接読み取ります。ベアホスト、Docker、Kubernetes で動作し、linuxctlを使えば kubectl のような CLI から同じツールを利用できます。
**権限の付与には慎重を期してください。**root 権限を付与すると、読み取った内容に含まれる指示に従うエージェントに root 権限を与えることになります。一見限定的に見える権限でも、実際には完全な root 権限となるものがあります(
/etcへの書き込み、services/manage、sysctl への書き込み)。権限を付与する前に、権限とリスクをお読みください。
動作の仕組み
- ツール/リソースの呼び出しごとに、新しいワーカープロセスが起動して終了します。 呼び出しごとに長期間保持される状態はありません。
internal/worker/spawner.goはmcpdバイナリ自体をworkerモードで再実行します(引数は stdin 経由で渡し、argv には渡しません)。syscall.Credential{Uid, Gid, Groups}には、user.Lookup()で解決された実在の OS アカウントが設定されます。これは設定フラグではなく、実際の権限分離です。非特権ユーザーのワーカープロセスは、特権ユーザーのものとは実際に異なる Linux UID で動作します。 configs/mcp-sudo.yamlは、ユーザーごと、ツールごとにprivileged: trueを許可するかどうかを決定します。 リソースには特別な許可が 2 つあります(ARCHITECTURE.mdの注意事項セクションを参照してください)。1 つはリソース URI 自体に対する許可、もう 1 つはその背後で使われる内部ワーカーツール名に対する許可です。mcpdがコンテナ内で実行される場合(configs/daemon.yamlのworker.containerized: true。このプロジェクトでは実際の Kubernetes デプロイメントで使用されています)、特権ワーカーは実ホストのマウント名前空間にも参加します(外部のnsenterバイナリは使わず、/proc/1/ns/mntに対してsetns(CLONE_NEWNS)を実行します)。そのため、privileged: trueはデーモン自身のコンテナイメージ内だけでなく、実ホスト上で root として動作することを意味します。- ベアラートークンは
users.yamlにソルト付きハッシュとして保存されます(token_saltとtoken_hash、sha256を使用し、定時間比較を行います。ファイルの権限は0600です)。平文では保存されません。ユーザーと許可設定の編集は、ホスト上でのみ、ローカルからlinuxctl <verb> mcpd userとlinuxctl edit mcpd configを使って行います(visudoと同様に検証されます)。これらを編集する MCP ツールはないため、ベアラートークンしか持たないものが自分自身に権限を付与することはできません。実行中のデーモンは、再起動せずにdaemon/reload-configで変更を適用します。この処理はファイルを再読み込みし、無効なファイルを拒否するだけです(デーモンのユーザー管理を参照してください)。 - すべての読み取りは、手作業で列挙するのではなく、スキーマに基づいて行われます。
linuxctlは呼び出しのたびに、稼働中のデーモンからtools/list/resources/list/resources/templates/listを取得し、それに照らして<verb> <group> [keyword]の文法を解決します。サーバー側に追加された新しいツールは、コードを一切変更せずに、すぐクライアント側で使えるようになります。
特長
- AI との直接連携: HTTP/SSE トランスポートにより、エージェントとデーモンが即座に通信できます。
- 厳格なセキュリティ: レート制限、ソルト付きハッシュによるベアラートークン認証、ディレクトリトラバーサル対策を備えています。
- 権限分離: マスターデーモンは root として動作し、
configs/mcp-sudo.yamlで定義されたルールに基づいて、非特権または特権の一時的なワーカーを起動します。 - 高いパフォーマンス:
singleflightによる重複排除と TTL キャッシュを使用し、効率的にシステムを調査します。 - Docker と Kubernetes に対応: マルチステージ Docker ビルドと、安全なホスト調査を可能にする Kubernetes デプロイメントマニフェストにより、完全にコンテナ化されています。
はじめに
Linux(systemd)へのインストール
curl -fsSL https://raw.githubusercontent.com/nucleusv/linux-mcp-daemon/main/scripts/install.sh | sudo bash
amd64/arm64 ホストで sudo と curl が必要です(最小構成の ubuntu/debian コンテナーでは、apt update && apt install -y curl ca-certificates を実行してから、root として bash にパイプします)。このスクリプトは、お使いのアーキテクチャ(amd64/arm64)向けの最新リリースをダウンロードして、sha256 チェックサムを検証し、mcpd と linuxctl を /usr/local/bin にインストールし、初期設定のユーザーやトークンを含まないクリーンな設定を /etc/mcpd/configs に書き込み、最初のユーザー mcp を作成してそのトークンを一度だけ表示し、mcpd systemd サービスを起動します。続いて、次のコマンドを実行します。
export MCP_SERVER=https://127.0.0.1:9091
export MCP_TLS_FINGERPRINT=<printed by the installer>
export MCP_TOKEN=<token printed by the installer>
linuxctl get system os-release
linuxctl get processes top
- アップグレード: 同じコマンドをもう一度実行します。設定は保持され、
mcpdが変更された場合にのみサービスが再起動します。v0.1.0 以降では、最初のユーザーに一度daemon/reload-configを付与してください。詳しくはアップグレードを参照してください。 - バージョンを指定/ユーザー名を指定:
... | sudo bash -s -- --version v0.1.0 --user alice - ユーザーの追加: 各ユーザーに同じ名前の OS アカウントが必要です。詳しくはユーザーの追加を参照してください。
- アンインストール:
... | sudo bash -s -- --uninstall(/etc/mcpdも削除するには--purgeを追加します。mcpOS アカウントは残ります。削除するにはsudo userdel -r mcpを実行します) - パッケージ: すべてのリリースで amd64/arm64 向けの
.debと.rpmを提供しています。sudo apt install ./linux-mcp-daemon_<version>_amd64.debまたはsudo dnf install ./linux-mcp-daemon-<version>-1.x86_64.rpmを実行し、表示される次の手順に従ってください。 - コンテナーイメージ:
ghcr.io/nucleusv/linux-mcp-daemon(amd64/arm64)。セットアップ手順はインストールガイドを参照してください。 - macOS(CLI のみ): 同じスクリプトで
linuxctlのみをインストールします。curl -fsSL .../install.sh | bash -s -- --bin-dir ~/.local/binを実行し、続けてexport PATH="$HOME/.local/bin:$PATH"を実行します(macOS のデフォルトの PATH には含まれていません)。これにより、リモートの mcpd を操作できます。
mcpd は、TLS を使用してすべてのインターフェースでリッスンします(初回起動時に作成する自己署名証明書を使用し、クライアントはそのフィンガープリントを固定します)。デフォルトではプレーン HTTP は無効です。ベアラートークンが平文で送信されるためです。ツールへの root アクセスは、
mcp-sudo.yamlでユーザーごと、ツールごとに付与します。
詳細なガイド:インストール · AI エージェントの接続 · mcp-sudo.yaml
開発:ソースからビルド
1. サーバー(mcpd)のビルドとデプロイ
-
Docker イメージをビルドします:
./scripts/build.sh -
ローカルの Kubernetes クラスターにデプロイします:
./scripts/deploy.sh
2. クライアント(linuxctl)のビルド
CLI クライアントは、ホストマシン(macOS など)で直接ビルドできます:
./scripts/build-cli.sh # writes executables/linuxctl
export PATH="$PWD/executables:$PATH" # so the examples below run as written
リリース(install.sh、.deb/.rpm)からインストールした場合、linuxctl はすでに PATH に含まれています。
3. 使用方法(ローカル開発)
デーモンはポート 9091 で実行されます。SSE を使用して AI クライアントから接続するか、linuxctl CLI ツールを使用できます:
# Set your token as an environment variable
export MCP_TOKEN="your_token_here"
# Ping the daemon
linuxctl ping
linuxctl は、簡潔な動詞/グループ構文(linuxctl <verb> <group> [target-keyword] [args])を使用し、実行中のデーモンからすべてのツールとリソースを動的に検出します。クライアント側でコマンド一覧を別途管理し、同期する必要はありません。詳細なリファレンスはlinuxctl ドキュメントまたは man linuxctl を参照してください。以下の各例には、linuxctl と、その実行内容に相当する MCP JSON-RPC の curl 呼び出しの両方が記載されています。すべてのツール/リソースについて両方の形式を並べて記載した完全なリファレンスは、MCP API ドキュメントにあります。
curl で API を直接呼び出す
mcpd はプレーンなリクエスト/レスポンス形式の HTTP ではなく、HTTP + SSE 上で JSON-RPC 2.0 を使用します。すべての呼び出しは 2 段階のハンドシェイクです。まず SSE ストリームを開いて一度限りの POST エンドポイントを取得し、次にそこへ JSON-RPC リクエストを POST します(実際の結果は POST のレスポンスボディではなく、SSE 接続を通じてストリーミングされます)。
# 1. Open the SSE stream in the background and capture the endpoint it prints
curl -N -s --cacert mcpd.crt -H "Authorization: Bearer $MCP_TOKEN" https://localhost:9091/sse &
# server sends: event: endpoint / data: /message?session_id=...
# 2. POST a request to that endpoint
curl -s --cacert mcpd.crt -X POST "https://localhost:9091/message?session_id=<from step 1>" \
-H "Authorization: Bearer $MCP_TOKEN" \
-H "Content-Type: application/json" \
-d '{"jsonrpc": "2.0", "id": "1", "method": "tools/list"}'
# 3. Watch the SSE stream from step 1 for the matching "id": "1" response
以下の例はすべてこのパターンに従います。変わるのは -d のペイロードだけです。
ファイル
# List a directory, as a table (get is the sole read verb - one result or many, same as kubectl)
$ linuxctl get files list /var/log --output table
MODIFIED NAME SIZE IS_DIR
2026-09-22 18:38:14 alternatives.log 6522 false
2026-09-22 18:38:10 apt 4096 true
...
curl -s -X POST "$ENDPOINT" -H "Authorization: Bearer $MCP_TOKEN" -H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":"1","method":"tools/call","params":{"name":"files/list","arguments":{"path":"/var/log","output_format":"table"}}}'
# Read a single file's contents (bare - no keyword needed, files' only other read candidate)
linuxctl get files /etc/hosts
curl -s -X POST "$ENDPOINT" -H "Authorization: Bearer $MCP_TOKEN" -H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":"1","method":"tools/call","params":{"name":"files/read","arguments":{"path":"/etc/hosts"}}}'
リソース(URI を直接指定)
# Read a static resource directly by URI (resource URIs always use scheme://path)
$ linuxctl resource os://uname
Sysname: Linux
Nodename: desktop-control-plane
Release: 7.0.12-linuxkit
...
curl -s -X POST "$ENDPOINT" -H "Authorization: Bearer $MCP_TOKEN" -H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":"1","method":"resources/read","params":{"uri":"os://uname"}}'
ディスク
# Native partition geometry - parsed from /sys/class/block, no fdisk dependency
linuxctl get disks partitions vda --output json
curl -s -X POST "$ENDPOINT" -H "Authorization: Bearer $MCP_TOKEN" -H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":"1","method":"tools/call","params":{"name":"disks/partitions","arguments":{"device":"vda","output_format":"json"}}}'
# Run a privileged tool (requires a root rule in configs/mcp-sudo.yaml)
linuxctl get disks free / --privileged true
curl -s -X POST "$ENDPOINT" -H "Authorization: Bearer $MCP_TOKEN" -H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":"1","method":"tools/call","params":{"name":"disks/free","arguments":{"path":"/","privileged":true}}}'
プロセス:単一の結果と複数の結果、詳細情報
# Same underlying tool - bare form lists many, a specific PID filters to one
linuxctl get processes --sort_by mem --limit 5
linuxctl get processes 1234
curl -s -X POST "$ENDPOINT" -H "Authorization: Bearer $MCP_TOKEN" -H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":"1","method":"tools/call","params":{"name":"processes/list","arguments":{"pid":1234}}}'
# Rich aggregated detail (combines several reads, excludes secret-shaped data like environ)
linuxctl describe processes 1234
# describe aggregates multiple resources/read calls client-side - e.g. one of them:
curl -s -X POST "$ENDPOINT" -H "Authorization: Bearer $MCP_TOKEN" -H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":"1","method":"resources/read","params":{"uri":"process://1234/status"}}'
ログ(ホスト専用ツール。コンテナ化されたデプロイでは privileged: true が必要です)
# journalctl only exists on the host, never in this daemon's own image
linuxctl get logs journal --unit kubelet.service --boot true --privileged true
curl -s -X POST "$ENDPOINT" -H "Authorization: Bearer $MCP_TOKEN" -H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":"1","method":"tools/call","params":{"name":"logs/journal-control","arguments":{"unit":"kubelet.service","boot":true,"privileged":true}}}'
変更操作(サービス、カーネル)
linuxctl restart system services nginx.service --privileged true
linuxctl update kernel sysctl net.ipv4.ip_forward 1 --privileged true
curl -s -X POST "$ENDPOINT" -H "Authorization: Bearer $MCP_TOKEN" -H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":"1","method":"tools/call","params":{"name":"services/manage","arguments":{"service":"nginx.service","action":"restart","privileged":true}}}'
Docker(読み取り、ライフサイクル、exec)
ソケット経由で Docker Engine API と通信するため、docker CLI は必要ありません。すべての docker/* ツールには、mcp-sudo.yaml で個別の許可が必要です。また、コンテナを指定するツールには containers: リストも必要です。これにより、エージェントが操作できるのはリストに記載したコンテナだけになります。
linuxctl get docker containers --all true # running and stopped
linuxctl get docker images
linuxctl get docker network bridge # one network's full configuration
linuxctl get docker container web-1 status # computed runtime summary (state, health, uptime, limits)
linuxctl resource docker-container://web-1/stats # the same templates by URI: docker-container://, docker-image://, docker-volume://, docker-network://
linuxctl get docker logs web-1 --lines 50
# needs docker/manage and docker/exec grants that list web-1
linuxctl restart docker web-1
linuxctl exec docker web-1 ls /usr/share/nginx/html
linuxctl exec docker web-1 -- ls --color /etc # after `--` everything goes to the container as-is
crontab
crontab コマンド本来のルールに従って crontab を読み取り、一覧表示し、置き換えます(cron.allow/cron.deny も引き続き適用されます)。crontab を書き込むと、そのユーザーとしてコマンドがスケジュールされ、セッションの終了後もトークンの取り消し後もジョブは実行されます。許可する前に、crontab のリスクをお読みください。
linuxctl get crontabs # my own crontab, exactly as `crontab -l` prints it
linuxctl update crontabs --content "$(cat mycron.txt)" # replace my whole crontab
linuxctl edit crontabs # open it in $EDITOR, write back if unchanged meanwhile
# another user's crontab: sudo plus a rule naming that account in mcp-sudo.yaml
linuxctl get crontabs --privileged true # the users you may view that have a crontab
linuxctl get crontabs test_user --privileged true
linuxctl update crontabs test_user --privileged true --content "$(cat test_user.cron)"
MCP プロトコル自体のイントロスペクション
linuxctl get mcp-api info # raw initialize response: protocol version + declared capabilities
linuxctl get mcp-api tools # every tool, by literal name
linuxctl get mcp-api resources # every static resource + template
linuxctl get mcp-api prompts # reports plainly that mcpd doesn't implement this MCP capability
curl -s -X POST "$ENDPOINT" -H "Authorization: Bearer $MCP_TOKEN" -H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":"1","method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"curl","version":"1.0"}}}'
名前を直接指定する抜け道(動詞の文法を回避)
linuxctl tool files/list --path /tmp # symmetric with `resource <uri>` above
curl -s -X POST "$ENDPOINT" -H "Authorization: Bearer $MCP_TOKEN" -H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":"1","method":"tools/call","params":{"name":"files/list","arguments":{"path":"/tmp"}}}'
デーモンのユーザー/トークン管理(ローカルのみ。ネットワーク呼び出しは一切ありません)
linuxctl create mcpd user alice # generates a token, prints it once, writes to configs/*.yaml directly
linuxctl list mcpd users
このグループに対応する curl の方法はありません。ネットワーク経由での操作を意図的に完全に禁止している理由については、デーモンユーザー管理をご覧ください。
プロジェクト構成
cmd/mcpd/:メインサーバーアプリケーションのエントリーポイント。cmd/linuxctl/:CLI クライアントアプリケーション。configs/:設定ファイル(デーモン設定、Sudo ルール)。internal/:カプセル化されたビジネスロジック:auth/:ユーザーごとのレート制限。config/:daemon.yaml、users.yaml、mcp-sudo.yamlの解析と検証(mcpd とlinuxctlで共有)。rpc/:MCP JSON-RPC ハンドラー(ツール/リソースのレジストリ、認可、設定の再読み込み)。worker/:呼び出しごとに、呼び出し元の OS アカウント(または root)で短時間実行されるワーカーを起動します。tools/、resources/:ツールとリソースごとのパッケージ。fsafe/:シンボリックリンクを追わずにパスを開きます(O_NOFOLLOW を指定した openat)。kernel/、procstat/:/procの解析(ソケット、プロセス)。logging/:レベル分けされた構造化ログ。netpolicy/:network/curlとnetwork/pingのネットワーク宛先ポリシー。
k8s/:Kubernetes のデプロイマニフェスト。scripts/:ビルドとデプロイの自動化。
リリース
SemVer タグをプッシュすると(git tag -a v0.1.0 -m v0.1.0 && git push origin v0.1.0)、.github/workflows/release.yml が実行されます。テストの後、GoReleaser がプラットフォームごとのアーカイブ、checksums.txt、変更履歴を GitHub Releases に、マルチアーキテクチャイメージを ghcr.io/nucleusv/linux-mcp-daemon に公開します。
ライセンス
Apache License 2.0 — NOTICE もご覧ください。
コメント
AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。