Skip to main content
Glama

NiChart DLMUSE MCP server

cbica/nichart_dlmuse(T1 MRI スカルストリップ + MUSE ROI セグメンテーション)を GPU EC2 インスタンス上の MCP サーバーの背後にホストし、すべてのユーザーがローカル GPU を必要とする代わりに Claude Code がリモートでセグメンテーションを実行できるようにします。

なぜこのような構成なのか

MCP ツールの引数は JSON です。.nii.gz は数十 MB のバイナリであり、セグメンテーションには GPU で約 1〜2 分かかります。単一のブロッキングツールコールには遅すぎ、大きすぎます。そこで:

  • ファイル転送は MCP プロトコルの外部で行われ、プレーンな認証付き POST /upload エンドポイントを介します。小さな upload_id だけが MCP ツールの引数として渡されます。

  • ジョブは非同期です: run_dlmuse_segmentation はキューに投入してすぐに戻ります。get_job_status はポーリングし、get_job_result は CSV をインラインで取得し、マスクファイルのダウンロードリンクも返します。

  • ワーカーは 1 つで直列化: 共有 GPU が 1 つのため、docker run は一度に 1 つだけ実行され、asyncio.Queue の背後でキューイングされます。

  • Bearer トークン認証 は /healthz を除くすべてのエンドポイントでチームメンバーごとに要求されます。

  • プライベートインスタンス、パブリック Web 層なし: アプリは 127.0.0.1 のみにバインドされ、セキュリティグループは SSH (22) 以外は開放しません。ユーザーは SSH ポートフォワーディングにより、各自のノートパソコンをそのループバックポートに秘密鍵で接続します。攻撃対象となるドメインもインターネット公開もありません。

  • TLS は自己署名証明書 で、インスタンス上で一度だけ生成されます。公開発行の証明書 (Let's Encrypt など) はドメイン検証のためにインスタンスがインターネットから到達可能である必要がありますが、このインスタンスは該当しないためです。各ユーザーはその証明書をラップトップで信頼します (下記参照)。

  • スキャンは永久に保存されません: アップロードとジョブ出力は RETENTION_HOURS (デフォルト 24 時間) 後に削除されます。

Related MCP server: agent-bridge-mcp

アーキテクチャ

Claude Code (laptop)                    SSH tunnel                  EC2 (private, SG: 22 only)
  │  ssh -i key.pem -L 8420:127.0.0.1:8420 user@instance ─────────────────►  │
  │                                                                          │
  │  1. curl https://127.0.0.1:8420/upload ─────(via tunnel)──────────►  MCP server :8420 (127.0.0.1, self-signed TLS)
  │  2. run_dlmuse_segmentation ────────────────(via tunnel)──────────►         │
  │  3. get_job_status (poll) ──────────────────(via tunnel)──────────►  asyncio job queue (1 worker)
  │  4. get_job_result ─────────────────────────(via tunnel)──────────►         │
                                                                   docker run --gpus all cbica/nichart_dlmuse

リポジトリ構成

server/app.py     MCP tools (run_dlmuse_segmentation, get_job_status, get_job_result)
                  + HTTP routes (/upload, /download/{job_id}/{filename}, /healthz)
server/jobs.py    job queue/worker, docker invocation, root-owned-output cleanup
server/auth.py    bearer-token ASGI middleware
server/config.py  env-driven settings
deploy/           EC2 provisioning script (installs Docker, GPU toolkit, self-signed cert, systemd unit)

EC2 の初回セットアップ

既存の GPU EC2 インスタンスが必要です (AWS Deep Learning AMI 推奨 — NVIDIA ドライバーと Docker が通常すでにインストールされています)。

  1. このリポジトリをインスタンスにコピーします (git clone / scp -r)。

  2. cp .env.example .env を実行し、少なくとも TOKENS を入力します — チームメンバーごとに name:token のペアをカンマ区切りで指定します。openssl rand -hex 32 でトークンを生成します。

  3. インスタンスのセキュリティグループで、インバウンド 22 (SSH) のみを許可し、チームの IP または踏み台に制限します。それ以外は開放しないでください — 443 も 8420 も不要です。アプリは 127.0.0.1 にバインドされ、SSH トンネル経由以外では到達できません。

  4. プロビジョニングスクリプトを実行します:

    sudo ./deploy/setup_ec2.sh

    これは冪等です — Docker/nvidia-container-toolkit がなければインストールし、DLMUSE イメージをプルし、専用の nichart-mcp サービスユーザーを作成し、コードを /opt/nichart-mcp にデプロイし、自己署名 TLS 証明書 (SAN = 127.0.0.1/localhost) を生成し、nichart-mcp systemd サービスをインストールします。

  5. インスタンス自体から確認します:

    curl --cacert /opt/nichart-mcp/tls/server.crt https://127.0.0.1:8420/healthz
  6. /opt/nichart-mcp/tls/server.crt をインスタンスからコピーして、各チームメンバーに渡せるようにします (例: scp -i key.pem ec2-user@<instance-ip>:/opt/nichart-mcp/tls/server.crt .)。

後からユーザーを追加または失効させるには: インスタンス上の /opt/nichart-mcp/.env の TOKENS を編集し、sudo systemctl restart nichart-mcp を実行します。

コードの更新

インスタンス上の更新済みチェックアウトから sudo ./deploy/setup_ec2.sh を再実行します — /opt/nichart-mcp を再同期し (既存の TLS 証明書はそのまま)、依存関係を再インストールし、サービスを再起動します。

ラップトップから接続する

各チームメンバーには、SSH 秘密鍵 (Windows の PuTTY .ppk キーの場合は、標準の ssh クライアントが使用できるように puttygen key.ppk -O private-openssh -o key.pem で一度変換します)、Bearer トークン、およびセットアップ手順 6 の server.crt ファイルが必要です。

1. 自己署名証明書を一度信頼します。これで curl/Claude Code が証明書を拒否しなくなります:

  • macOS: security add-trusted-cert -d -r trustRoot -k ~/Library/Keychains/login.keychain-db server.crt

  • Linux: sudo cp server.crt /usr/local/share/ca-certificates/nichart-mcp.crt && sudo update-ca-certificates

  • Windows: certutil -addstore -f "ROOT" server.crt

2. SSH トンネルを開きます (Claude Code を使用している間はターミナルで実行したままにします):

ssh -i key.pem -N -L 8420:127.0.0.1:8420 <ssh_user>@<instance-ip>

インスタンスにパブリック IP がなく、踏み台経由でしか到達できない場合は、-J <bastion_user>@<bastion_host> を追加します。

3. MCP サーバーを Claude Code に登録します (一度だけ):

claude mcp add --transport http nichart-dlmuse https://127.0.0.1:8420/mcp \
  --header "Authorization: Bearer <their-token>"

使用方法

トンネルを開いた状態で、Claude Code にスキャンのセグメンテーションを依頼すると、次の手順が実行されます:

  1. ファイルをアップロードします (トンネル経由なので、ファイルはリモートインスタンス宛てですが 127.0.0.1:8420 が正しいです):

    curl -X POST -H "Authorization: Bearer <token>" \
      -F "file=@/path/to/scan.nii.gz" \
      https://127.0.0.1:8420/upload
    # -> {"upload_id": "..."}
  2. その upload_id を指定して run_dlmuse_segmentation ツールを呼び出すと、job_id が返ります。

  3. get_job_status(job_id) を status == "done" になるまでポーリングします (通常 GPU で約 1〜2 分)。

  4. get_job_result(job_id) を呼び出すと、ROI ボリュームの CSV がインラインで返り、ICV および MUSE マスク NIfTI ファイルの /download/{job_id}/{filename} リンクも返ります (同じ Bearer トークンを使い、同じトンネル経由で https://127.0.0.1:8420/download/... から取得します)。

運用上の注意

  • GPU 並行性: 設計上、一度に実行されるジョブは 1 つです (共有 GPU が 1 つ)。チームが忙しいとジョブがキューに溜まります。get_job_status は queue_position を報告します。

  • ジョブ状態はインメモリです: systemctl restart nichart-mcp を実行すると、進行中のジョブレコードが失われます (アップロード済みスキャンとディスク上の部分出力は影響を受けませんが、再送信が必要です)。小規模チームの規模では問題ありません。規模が大きくなったら、server/jobs.py のインメモリ dict を Redis/RQ に置き換えてください。

  • PHI: スキャンは実際の患者データです。RETENTION_HOURS はディスク上に保存される期間を制限しますが、実際の患者に使用する前に、これがデータ取り扱い要件と互換性があることを確認してください。まだ有効にしていない場合は、インスタンスのボリュームで EBS 暗号化を有効にすることを検討してください。

  • DLMUSE コンテナは内部で root として実行されます (/app/pipeline.log への書き込みがハードコードされているため、--user では実行できません)。その出力は root 所有になります。server/jobs.py のクリーンアップは、使い捨ての alpine コンテナにフォールバックしてそれらのディレクトリを強制削除します。

ローカル開発

python3 -m venv .venv && .venv/bin/pip install -r requirements.txt
cp .env.example .env   # fill in TOKENS
TOKENS=dev:devtoken DATA_DIR=/tmp/nichart-dev .venv/bin/python -m server.app

これにより、フルサーバー (認証、アップロード、ジョブキュー、MCP ツール) がローカルで実行されます。実際にセグメンテーションを実行するには、GPU アクセスを持つ Docker がまだ必要です。GPU のないマシンでは、ジョブは docker run ステップで失敗しますが、その他すべて (ルーティング、認証、キューイング、ステータス/エラー報告) は試すことができます。

Related MCP Connectors

Related MCP Servers