| name | docker-mcp-gateway-custom-server |
| description | Containerize any MCP server (especially Python-based ones like docling-mcp) and serve it through Docker MCP Gateway on-premises. Covers Dockerfile authoring, air-gapped model pre-baking, Docker Hub re-tagging for docker.io-only environments, and docker-compose patterns for the gateway. Use when deploying an MCP server in an environment that can only reach docker.io (no GitHub, HuggingFace, quay.io, etc. at runtime).
|
| tags | ["docker","mcp","on-premises","docling","air-gapped"] |
| security_review | confirmed |
Context
Docker MCP Gateway (docker/mcp-gateway) orchestrates MCP servers running as isolated Docker
containers. It translates between AI-client transports (SSE, streaming HTTP) and per-server
stdio. In on-premises environments with docker.io-only access, all images must be pre-built
and pushed to Docker Hub before deployment.
Key Pitfalls Discovered
1. No official Dockerfile in docling-mcp
docling-mcp has NO Dockerfile (Issue 73 was closed, pointing to docling-serve on quay.io)
- Official image is
quay.io/docling-project/docling-serve — not on Docker Hub
- Must build from source via
git clone https://github.com/docling-project/docling-mcp
2. HuggingFace model download fails at runtime in air-gapped environments
- docling downloads layout/table detection ML models from HuggingFace Hub on first use
- Air-gapped on-prem (no access to
huggingface.co) causes runtime model DL to fail silently
- Fix: Pre-download models at image build time with
docling-tools models download
- Set
DOCLING_ARTIFACTS_PATH=/app/.cache/docling/models so docling finds them
3. CPU-only PyTorch must be explicit
- Default
pip install docling pulls GPU PyTorch (~3 GB extra)
- Force CPU-only before installing docling:
pip install torch torchvision --index-url https://download.pytorch.org/whl/cpu
- Then:
pip install /build/docling-mcp
4. Gateway requires DOCKER_MCP_IN_CONTAINER=1 without Docker Desktop
- Running gateway container on plain Docker Engine (no Docker Desktop) fails feature checks
- Must set env:
DOCKER_MCP_IN_CONTAINER=1
5. Gateway needs docker.sock to spawn MCP server containers dynamically
- Volume mount required:
/var/run/docker.sock:/var/run/docker.sock
- Alternative: use Static Compose pattern (pre-started containers, no sock needed for spawning)
6. type: image ボリュームは Docker Engine 26+ 専用 — hermes-dev では使えない
- Static compose の
type: image ボリューム(gateway bridge binary注入)は Docker Engine 26以降の機能
- hermes-dev は Docker Engine 25.0.14 のため
type: image を使うと起動時に invalid mount config for type "image": mount type unknown エラー
- 対処: Pattern B(Static)の
mcp-docling サービスごと削除し、Pattern A(Dynamic, docker.sock)を使う
- Gateway が docker.sock 経由で docling コンテナを自動起動するため mcp-docling サービス定義は不要
docker-compose.yml から type: image ボリュームと mcp-docling サービスを除去すればOK
9. --servers フラグを使うと dynamic-tools disabled でツール数0になる【重要】
--servers=docling フラグを使うと起動ログに以下が出てツールが登録されない:
Note: dynamic-tools disabled when using --servers flag
> 0 tools listed in 10µs
--static を追加しても解決しない。GatewayはdoclingコンテナをDockerで起動するが、MCP tools/listの結果が0のまま返る。
根本原因:--servers フラグはdynamic-toolsを無効化する。
確認手順:
docker logs <gateway-container> 2>&1 | grep -E "tools listed|servers are enabled|dynamic-tools"
docker ps -a | grep docling
--servers を外して --catalog のみにした場合の挙動(hermes-dev 確認済み)
docker-compose.yml の command から --servers=docling を削除し --catalog=/mcp-catalog.yaml のみにすると:
- No server is enabled
- Adding internal tools (dynamic-tools feature enabled)
> mcp-find: tool for finding MCP servers in the catalog
> mcp-add: tool for adding MCP servers to the registry
> mcp-remove: tool for removing MCP servers from the registry
> code-mode: write code that calls other MCPs directly
> mcp-exec: execute tools that exist in the current session
> mcp-config-set: tool for setting configuration values for MCP servers
> mcp-discover: prompt for learning about dynamic server management
> Initialized in 5.5ms
> Start streaming server on port 8811
dynamic-tools は有効化され、mcp-find・mcp-add ツールが使えるようになる。
クライアント(Claude Code)から mcp-add docling を呼ぶことで、セッション内にdoclingツールを追加できる可能性がある(ただし、mcp-add がDocker公式カタログのみを参照する場合は自前イメージの追加不可)。
対処策(優先順):
- Pattern C(Claude Code + stdio直結、Gateway不要)への切り替え ← 唯一確実な方法・推奨
--catalog=/path/to/catalog.yaml のみ(--serversなし)で起動 → dynamic-tools有効化 → mcp-add でdocling追加を試みる
--enable-all-servers に変更 → NG: デフォルトのDocker公式カタログのみ参照し、--catalogで指定した自前カタログを認識しない(No server is enabled のまま)
--oci-ref=docker.io/mitlabo/docling-mcp:latest で直接指定 → NG: 通常のDockerイメージには使えない(artifact type is not application/vnd.docker.mcp.server エラー)。GatewayのOCIアーティファクト形式(特殊なOCIラベル付きイメージ)が必要
結論: Docker MCP Gateway(docker/mcp-gateway)はDocker公式カタログに登録されたイメージ専用の仕組み。自前イメージを使う場合はPattern C(stdio直結)一択。
試したカタログ形式(すべて0 tools):
version: v1
servers:
- id: docling
name: Docling Document Processor
description: Docling MCP server
image: docker.io/mitlabo/docling-mcp:latest
command:
- --transport
- stdio
- conversion
- generation
- manipulation
カタログをファイルにしてdocker-composeでマウントしても結果は変わらない。
公式カタログの正しい形式(v3)
curl -s "https://desktop.docker.com/mcp/catalog/v3/catalog.yaml" で確認した実際の形式:
version: 3
name: docker-mcp
displayName: Docker MCP Catalog
registry:
サーバーID:
description: ...
title: ...
type: server
dateAdded: "2025-05-05T20:04:34Z"
image: mcp/イメージ名@sha256:ハッシュ
tools:
- name: ツール名
prompts: 0
resources: {}
metadata:
pulls: 93321
stars: 2
category: devops
tags: [...]
license: Apache License 2.0
owner: 組織名
重要: image フィールドは @sha256:... digest 形式が必要。また通常の docker.io イメージは artifact type is not application/vnd.docker.mcp.server エラーになるため、v3形式でも自前イメージは登録できない。
--additional-catalog vs --catalog フラグの違い
--catalog strings カタログを全置換(デフォルト: https://desktop.docker.com/mcp/catalog/v2/catalog.yaml)
--additional-catalog strings デフォルトカタログに追記
--catalog=/mcp-catalog.yaml にすると公式カタログが無効になる代わりにカスタムカタログのみ読む(ただし自前イメージは動かない)。--additional-catalog=/mcp-catalog.yaml は公式カタログ + カスタムカタログの両方を読む。
10. docker-mcp.yaml の command フォーマット — ENTRYPOINTの有無に注意
イメージの ENTRYPOINT が設定されているかで command の書き方が変わる:
docker inspect docker.io/mitlabo/docling-mcp:latest \
--format '{{json .Config.Entrypoint}} {{json .Config.Cmd}}'
ENTRYPOINTが docling-mcp-server の場合 → command には引数のみ記載(コマンド名不要):
command:
- --transport
- stdio
- conversion
- generation
- manipulation
ENTRYPOINTがない場合 → command にコマンド名から記載:
command:
- docling-mcp-server
- --transport
- stdio
- conversion
- generation
- manipulation
command にコマンド名を重複させると 'docling-mcp-server' is not one of 'conversion', ... エラーになる(位置引数と混同するため)。
docling-mcp-server のツールグループは位置引数([TOOLS]:[conversion|generation|manipulation|...]...)。
ツール数0の原因がここではない場合、--servers フラグによる dynamic-tools 無効化を疑う(Pitfall 9参照)。
8. Gateway tools/list が0を返す場合(セッションID取得方法とMCPプロトコル正しいフロー)
セッションID取得
- Gateway の HTTP streaming transport では、
initialize リクエストをセッションIDなしで送ると、レスポンスヘッダー Mcp-Session-Id に新しいセッションIDが返ってくる
- クライアントが独自のセッションIDを
Mcp-Session-Id ヘッダーに付けて送ると session not found エラーになる
- curlでの取得例:
SESSION_ID=$(curl -si -X POST http://localhost:8811/mcp \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}}}' \
| grep -i "Mcp-Session-Id" | tr -d '\r' | awk '{print $2}')
⚠️ initialize 後に initialized notificationを送らないと次のPOSTが400になる
MCP仕様では initialize → initialized notification → その後のリクエスト、という順序が必須。
initialize の直後に tools/list 等をPOSTすると HTTP 400 Bad Request が返る。
正しいフロー:
SESSION_ID=$(curl -si -X POST http://localhost:8811/mcp \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"hermes","version":"1.0"}}}' \
| grep -i "Mcp-Session-Id" | tr -d '\r' | awk '{print $2}')
curl -s -X POST http://localhost:8811/mcp \
-H "Content-Type: application/json" \
-H "Mcp-Session-Id: $SESSION_ID" \
-d '{"jsonrpc":"2.0","method":"notifications/initialized","params":{}}'
curl -s -X POST http://localhost:8811/mcp \
-H "Content-Type: application/json" \
-H "Mcp-Session-Id: $SESSION_ID" \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}'
レスポンス形式はSSEストリーム(JSONではない)
tools/call 等のレスポンスは Content-Type: text/event-stream で返ってくる。
pythonで処理する場合は "data:" プレフィックスを外して JSON パースする:
for line in body.splitlines():
if line.startswith("data:"):
result = json.loads(line[5:].strip())
tools/list で0件が返る場合は、Gatewayがdoclingコンテナをまだ起動できていない可能性がある。docker logs <gateway-container> でGatewayログを確認し、実際にdoclingコンテナが docker ps -a に表示されているか確認する
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}}}'
| grep -i "Mcp-Session-Id" | tr -d '\r' | awk '{print $2}')
tools/list で0件が返る場合は、Gatewayがdoclingコンテナをまだ起動できていない可能性がある。docker logs <gateway-container> でGatewayログを確認し、実際にdoclingコンテナが docker ps -a に表示されているか確認する
7. docker compose vs docker-compose on hermes-dev
docker compose (space, v2 plugin) is NOT available as a docker sub-command on hermes-dev
- Use
docker-compose (hyphen, binary at /usr/bin/docker-compose and /usr/local/bin/docker-compose) — version v2.28.1 is installed
- All README instructions and scripts should use
docker-compose (not docker compose) for hermes-dev compatibility
- Verify with:
docker-compose version
Dockerfile Design (docling-mcp, CPU-only, air-gapped)
Base: python:3.12-slim-bookworm (pinned Python version from .python-version)
Build stages:
- Install system packages: libgl1, libglib2.0-0, tesseract-ocr (+eng/jpn), libleptonica-dev, libreoffice, git, curl
- Create non-root user (uid 1000)
- Clone source at build time (internet required during build only)
- Install CPU-only torch FIRST, then pip install from cloned source
- Run
docling-tools models download to pre-bake ML models (~500MB-1GB)
- Clean build artifacts, set ownership, switch to non-root user
- ENTRYPOINT:
docling-mcp-server, CMD: --transport stdio
- EXPOSE 8000 (for streamable-http mode)
- Set env vars: HF_HOME, TESSDATA_PREFIX, DOCLING_ARTIFACTS_PATH
docker-mcp.yaml (MCP Gateway catalog definition)
version: v1
servers:
- id: docling
name: Docling Document Processor
description: "PDF/DOCX/PPTX to Markdown/JSON via Docling MCP"
image: yourorg/docling-mcp:latest
command:
- docling-mcp-server
- --transport
- stdio
- conversion
- generation
- manipulation
docker-compose.yml — Pattern A (Dynamic, docker.sock) ✅ hermes-dev 動作確認済み
hermes-dev では必ずこのPatternを使うこと(Pattern Bは Docker Engine 26+必須のため不可)
services:
mcp-gateway:
image: docker/mcp-gateway:latest
ports:
- "8811:8811"
command:
- --transport=streaming
- --port=8811
- --servers=docling
- --catalog=/config/docker-mcp.yaml
- --verbose
- --log-calls
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./docker-mcp.yaml:/config/docker-mcp.yaml:ro
- ./documents:/documents:ro
- docling-models:/root/.cache
environment:
- DOCKER_MCP_IN_CONTAINER=1
restart: unless-stopped
volumes:
docling-models:
driver: local
ヘルスチェック: curl -sv http://localhost:8811/health → HTTP 200 OK
MCPエンドポイント: http://localhost:8811/mcp(/sse/docling ではない)
リダイレクト確認: curl http://localhost:8811/ → <a href="/mcp">Temporary Redirect</a>
docker-compose.yml — Pattern B (Static, production-stable)
services:
mcp-gateway:
image: docker/mcp-gateway:latest
ports:
- "8811:8811"
command:
- --transport=streaming
- --port=8811
- --servers=docling
- --static=true
depends_on:
- mcp-docling
restart: unless-stopped
mcp-docling:
image: yourorg/docling-mcp:latest
init: true
cpus: 2
mem_limit: 6g
security_opt:
- no-new-privileges
labels:
- docker-mcp=true
- docker-mcp-tool-type=mcp
- docker-mcp-name=docling
- docker-mcp-transport=stdio
volumes:
- type: image
source: docker/mcp-gateway
target: /docker-mcp
- docling-models:/app/.cache
environment:
- DOCLING_ARTIFACTS_PATH=/app/.cache/docling/models
- HF_HOME=/app/.cache/huggingface
restart: unless-stopped
volumes:
docling-models:
Docker Hub PAT Authentication
Docker Hub PAT はBasic認証で使う(Bearer認証は401)。
TOKEN=$(cat /tmp/dockerhubpat.txt | tr -d '[:space:]')
echo "$TOKEN" | docker login --username USERNAME --password-stdin
Hub APIを叩く場合は -u "username:token" 形式が必須(Authorization: Bearer は不可)。
接続テストだけしたい場合(docker login がシステムに記憶を残さないようにするには):
docker logout
設定ファイルのイメージ参照更新・git push
.mcp.json / docker-mcp.yaml / docker-compose.yml 内のイメージ名変更は
GitHub API PUT を execute_code から直接叩くと401になりやすい。
git clone → sed → git push が確実。
git clone --depth 1 --branch BRANCH \
"https://USERNAME:${GITHUB_PAT}@github.com/ORG/REPO.git" /tmp/work
cd /tmp/work
sed -i "s|OLD_IMAGE:tag|NEW_IMAGE:latest|g" \
path/.mcp.json path/docker-mcp.yaml path/docker-compose.yml
git config user.email "agent@example.com" && git config user.name "Hermes Agent"
git add -A && git commit -m "fix: update image reference"
git push origin BRANCH
docker-compose.yml で同一イメージが複数サービスに出る場合、sed は全箇所を一括置換できる。
PAT を使った git push(hermes-dev 確認済み)
/root/.hermes/profiles/bedrock-slack/.env の PAT は grep | cut では末尾に改行が入る場合がある。
python3 -c "import re; ..." で確実に取得する:
PAT=$(python3 -c "
import re
with open('/root/.hermes/profiles/bedrock-slack/.env') as f:
m = re.search(r'GITHUB_HERMESSHIFT_PAT=(.+)', f.read())
print(m.group(1).strip()) if m else print('')
")
cd /tmp/aws-rebuild
git remote set-url origin "https://MITLabo:${PAT}@github.com/shiftrepo/aws.git"
git push origin main
git status で Your branch is ahead of 'origin/main' by N commits と出ていれば push するだけ(commit 不要)。
/opt/docling-workspace/ は git リポジトリではないため、変更後は /tmp/aws-rebuild/container/claudecode/docling-mcp/ にコピーしてから commit/push する。
Build & Push to Docker Hub
docker login
docker buildx create --name mcp-builder --use 2>/dev/null || docker buildx use mcp-builder
docker buildx inspect --bootstrap
docker buildx build \
--platform linux/amd64 \
--build-arg DOCLING_MCP_VERSION=main \
--tag yourorg/docling-mcp:latest \
--push \
--file ./Dockerfile .
For air-gapped transfer (no internet on target):
docker save yourorg/docling-mcp:latest | gzip > docling-mcp.tar.gz
docker load < docling-mcp.tar.gz
AI Client Connection Config
Pattern C: Claude Code + stdio直結(Docker MCP Gateway不要)
Claude Codeが .mcp.json の設定に従い docker run --rm -i でコンテナを直接起動し、stdio経由でMCPプロトコルをやり取りする。Gateway不要でシンプル。
{
"mcpServers": {
"docling": {
"type": "stdio",
"command": "docker",
"args": [
"run", "--rm", "-i", "--init",
"--cpus", "2",
"--memory", "6g",
"--volume", "${PWD}/documents:/documents:ro",
"--env", "DOCLING_ARTIFACTS_PATH=/app/.cache/docling/models",
"--env", "DOCLING_MCP_KEEP_IMAGES=true",
"yourorg/docling-mcp:latest",
"--transport", "stdio",
"conversion", "generation", "manipulation"
]
}
}
}
配置場所: プロジェクトルートの .mcp.json(プロジェクトスコープ)または claude mcp add でグローバル登録。
Claude Code のサブエージェント構成(.claude/agents/ 配下に配置):
| ファイル | 名前 | 役割 | 自動起動キーワード |
|---|
doc-converter.md | doc-converter | PDF/DOCX等をMarkdown変換 | 「変換して」「Markdownにして」 |
doc-analyst.md | doc-analyst | 要約・分析・テキスト検索 | 「要約して」「分析して」「比較して」 |
doc-editor.md | doc-editor | ドキュメント編集・新規作成 | 「編集して」「追記して」「作成して」 |
スラッシュコマンド(.claude/commands/ 配下に配置):
/convert <ファイル名またはURL> # doc-converter を呼び出し
/analyze <ファイル名またはURL> # doc-analyst を呼び出し
ワークフロールール(.claude/rules/docling-workflow.md):
- 変換前に
is_document_in_local_cache でキャッシュ確認(二重変換防止)
- ローカルファイルパスは
/documents/ファイル名(コンテナ内パス)
document_key は変数に保持(コンテナ再起動で消える)
- 破壊的操作(
update_text / delete_items)の前にユーザー確認
- 変換後は必ず
save_docling_document でディスク保存
AWS Bedrock + Claude Code の起動:
CLAUDE_CODE_USE_BEDROCK=1 claude
注意: ~/.claude/settings.json が { "theme": "dark" } 等のみの場合、CLAUDE_CODE_USE_BEDROCK=1 環境変数を設定するだけでBedrockが使われる。settings.json に "apiProvider": "bedrock" / "awsRegion": "us-east-1" を追加する方法も有効だが、環境変数が最もシンプル。
auth status = "Not logged in" というログはAWS Bedrock接続時の正常状態。AnthropicのAPIキーは不要で、AWS IAMクレデンシャル(AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY / AWS_DEFAULT_REGION)が環境変数にあればよい。
✅ Pattern C はオンプレミス(docker.io + AWS Bedrock + npm のみ通信可能)環境で動作確認済み。
Pattern A/B: Docker MCP Gateway 経由(ネットワーク越し接続)
重要: Gateway のエンドポイントは /sse/docling ではなく /mcp(HTTP streaming)。
Claude Code での登録:
claude mcp add --transport http docling http://localhost:8811/mcp
.mcp.json への記載:
{
"mcpServers": {
"docling": {
"type": "http",
"url": "http://localhost:8811/mcp"
}
}
}
リモート接続の場合:
{
"mcpServers": {
"docling": {
"type": "http",
"url": "http://ONPREM_IP:8811/mcp"
}
}
}
エンドポイント確認方法:
curl http://localhost:8811/health
curl http://localhost:8811/
curl -X POST http://localhost:8811/mcp \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}}}'
Resource Estimates
| Item | Value |
|---|
| Image size | ~4-6 GB (PyTorch + LibreOffice + baked models) |
| Build time | 30-50 min first build |
| RAM at runtime | 4 GB min, 6 GB recommended |
| CPU | 2 cores min |
| External port | 8811 (gateway only) |
Available docling-mcp Tool Groups (19 tools — hermes-dev 実動作確認済み)
以下は Claude Code による実テスト(2026年4月)で確認した実際のツール名。
claude -p の -p フラグ(非対話プロンプトモード)でグループ別に動作確認した結果。
正確な19ツール名一覧(コンテナ内 Python で inspect.getmembers 確認済み)
⚠️ export_docling_document_to_json は存在しない — Claude が勝手に「存在しない」と判断して代替実行することがある。
⚠️ add_table_to_docling_document は存在しない — 正しくは add_table_in_html_format_to_docling_document
ツール名の正典確認コマンド:
docker run --rm --entrypoint python3 \
-e DOCLING_MCP_KEEP_IMAGES=true \
docker.io/mitlabo/docling-mcp:latest -c "
import inspect
from docling_mcp.tools import conversion, generation, manipulation
for mod in [conversion, generation, manipulation]:
for name, obj in inspect.getmembers(mod):
if callable(obj) and name.islower() and '_' in name and not name.startswith('_'):
print(name)
" | sort -u
| グループ | ツール名 | 動作 | 備考 |
|---|
| conversion | convert_document_into_docling_document | ✅ | URL/ファイルをDocling形式に変換してdocument_keyを返す |
| conversion | export_docling_document_to_markdown | ✅ | document_keyのドキュメントをMarkdown形式で出力 |
| conversion | convert_directory_files_into_docling_document | ⚠️ | ディレクトリ内ファイルを一括変換。.gitkeep等の非対応ファイルがあるとエラー |
| conversion | is_document_in_local_cache | ✅ | ファイルがキャッシュ済みか確認。in_cache: true/falseを返す |
| conversion | page_thumbnail | ⚠️ | PDFのみ対応。HTML/Excel等ページ概念のないファイルは page_no not found エラー。DOCLING_MCP_KEEP_IMAGES=true が必須(未設定ではPDFでも DoclingDocument does not have page images エラー)。docker-compose.yml の mcp-gateway の environment: に追加し down→up で再起動(restart では環境変数変更が反映されない)。reportlab製PDFで動作確認済み |
| generation | create_new_docling_document | ✅ | 新規空ドキュメントを作成してdocument_keyを返す |
| generation | add_title_to_docling_document | ✅ | ドキュメントタイトルを追加 |
| generation | add_section_heading_to_docling_document | ✅ | セクション見出しを追加(levelで階層指定) |
| generation | add_paragraph_to_docling_document | ✅ | 段落テキストを追加 |
| generation | open_list_in_docling_document | ✅ | リストブロックを開始 |
| generation | add_list_items_to_list_in_docling_document | ✅ | リストアイテムを追加(items配列で複数指定可) |
| generation | close_list_in_docling_document | ✅ | リストブロックを閉じる |
| generation | add_table_in_html_format_to_docling_document | ✅ | HTML形式のテーブルを追加(⚠️ add_table_to_docling_document という名称は存在しない) |
| manipulation | get_overview_of_document_anchors | ✅ | ドキュメントの構造(anchor一覧)を取得 |
| manipulation | get_text_of_document_item_at_anchor | ⚠️ | テキスト要素専用(テーブル・画像anchorには使用不可でエラー) |
| manipulation | search_for_text_in_document_anchors | ⚠️ | テキスト要素のみ検索対象(テーブル内テキストは検索不可、検索結果なしは仕様通り) |
| manipulation | update_text_of_document_item_at_anchor | ✅ | 指定anchorのテキストを更新 |
| manipulation | delete_document_items_at_anchors | ✅ | 指定anchorの要素を削除(テーブルも削除可能) |
| manipulation | save_docling_document | ✅ | コンテナ内CACHE_DIRに .json と .md の2ファイルを保存 |
⚠️ Claude Codeがツール名を幻覚する(重要な落とし穴)
Claude Code は -p モードで存在しないツール名(例: export_docling_document_to_json)を呼ぼうとした場合、
「ツールが存在しないため代替で export_docling_document_to_markdown で実行しました」と報告することがある。
これはエラーではなく Claude の自律的な代替行動だが、スキルテスト/ツール動作確認の場面では誤った確認結果になる。
対策: ツール動作確認は --allowedTools "mcp__docling__<正確なツール名>" で厳密に指定するか、
ガバナンスログ(docker logs <gateway> の Calling tool 行)で実際に呼ばれたツール名を確認する:
docker logs docling-workspace-mcp-gateway-1 2>&1 | grep "Calling tool" | \
sed 's/.*Calling tool //;s/ with.*//' | sort -u
⚠️ 動作制約(実テスト結果)
- ExcelファイルはテーブルとしてMarkdown出力される:
export_docling_document_to_markdown でセル内容がテーブル形式で出力
search_for_text_in_document_anchors の制約: テーブルアイテム内のテキストは検索対象外(テキスト要素のみ)
get_text_of_document_item_at_anchor の制約: テキスト要素専用。テーブルや画像には使用不可
page_thumbnail の制約: PDFのみ対応。HTML/Excelはページ概念なし → page_no not found。さらに DOCLING_MCP_KEEP_IMAGES=true が必要(デフォルト未設定のためPDFでも失敗する場合あり)。docker-compose.yml の environment に DOCLING_MCP_KEEP_IMAGES=true を追加して再起動が必要。
save_docling_document の保存先: コンテナ内の CACHE_DIR(/tmp/docling-cache)に .json と .md の2ファイルが保存
convert_directory_files_into_docling_document の注意: .gitkeep 等の非対応ファイルがあるとエラー。変換対象ファイルのみのディレクトリで使用すること(テスト前に rm .gitkeep が必要)
document_key はサービス再起動で失われる(重要)
document_key はインメモリキャッシュのため、コンテナ再起動・セッション切れで消える。
変換→操作→保存は必ず1つの claude -p "..." プロンプト内で一貫して指示すること。
claude -p "ファイルを変換してください"
claude -p "abc123 を Markdown に出力して"
claude -p "
1. convert_document_into_docling_document で documents/xxx.xlsx を変換
2. 同じdocument_keyで export_docling_document_to_markdown で出力
3. 同じdocument_keyで save_docling_document で保存
" --max-turns 20
claude -p での動作確認プロンプト例(README掲載用)
⚠️ -p モードの注意: claude -p は非対話モードだが出力が空になる場合がある。
出力が空でも exit_code 0 なら成功(Claude Code v2.1.x のprint mode制限)。
tmux インタラクティブモードが確実だが、-p は CI/確認用途には使える。
ツール一覧確認:
cd /opt/docling-workspace
CLAUDE_CODE_USE_BEDROCK=1 claude -p \
"mcp__docling__ で始まるツールを全て箇条書きで列挙してください" \
--max-turns 3
XLSX変換・Markdown出力・保存(一連フロー):
CLAUDE_CODE_USE_BEDROCK=1 claude -p \
"以下を1セッションで連続実行してください:
1) convert_document_into_docling_document(source=documents/db_model_complex.xlsx)
2) 得たdocument_keyで export_docling_document_to_markdown(最初の300文字表示)
3) 同じdocument_keyで save_docling_document" \
--max-turns 20
全conversion/inspectionツール確認(キャッシュ・変換・検索・保存):
CLAUDE_CODE_USE_BEDROCK=1 claude -p \
"以下を順番に実行してください:
1) is_document_in_local_cache(path=documents/db_model_complex.xlsx)
2) convert_document_into_docling_document(source=documents/db_model_complex.xlsx)
3) export_docling_document_to_markdown(最初の300文字表示)
4) get_overview_of_document_anchors(最初の5件)
5) search_for_text_in_document_anchors(query=id, 最初の3件)
6) save_docling_document
各ツール結果をツール名: 成功/失敗(理由)形式で報告してください" \
--max-turns 20
新規ドキュメント作成フロー(全generation/manipulationツール確認):
CLAUDE_CODE_USE_BEDROCK=1 claude -p \
"以下の手順で11個のMCPツールを動作確認してください。
1) create_new_docling_document で新規作成
2) add_title_to_docling_document: Docling MCP Test Document
3) add_section_heading_to_docling_document: level=1, Introduction
4) add_paragraph_to_docling_document: This is a test paragraph.
5) open_list_in_docling_document
6) add_list_items_to_list_in_docling_document: [Item 1, Item 2, Item 3]
7) close_list_in_docling_document
8) add_table_in_html_format_to_docling_document: Name/Value列 1データ行のHTMLテーブル
9) get_overview_of_document_anchors
10) update_text_of_document_item_at_anchor: paragraphのテキストをUpdated paragraph text.に変更
11) delete_document_items_at_anchors: tableのanchorを削除
最後にexport_docling_document_to_markdownで全体表示。
各ツール結果をツール名: 成功/失敗(理由)形式で報告してください" \
--max-turns 25
全ツールの確認コマンド(Bedrock + Claude Code):
cd /opt/docling-workspace
CLAUDE_CODE_USE_BEDROCK=1 /root/.hermes/node/bin/claude \
-p "利用可能なMCPツールをすべてリストアップしてください" \
--max-turns 3 --allowedTools "mcp__docling__*"
page_thumbnail テスト(PDF必須 + DOCLING_MCP_KEEP_IMAGES=true 必須):
python3 -c "
from reportlab.pdfgen import canvas
c = canvas.Canvas('documents/sample.pdf')
c.drawString(100, 750, 'Test PDF page 1')
c.showPage()
c.drawString(100, 750, 'Test PDF page 2')
c.showPage()
c.save()
"
CLAUDE_CODE_USE_BEDROCK=1 claude -p \
"documents/sample.pdf を convert_document_into_docling_document で変換し、
page_no=1 で page_thumbnail を取得してください" \
--max-turns 10
convert_directory_files_into_docling_document テスト(.gitkeep除去が必要):
rm -f documents/.gitkeep
CLAUDE_CODE_USE_BEDROCK=1 claude -p \
"convert_directory_files_into_docling_document(path=documents) でディレクトリ内ファイルを一括変換してください" \
--max-turns 10
General Pattern for Any Python MCP Server
- Clone repo in Dockerfile (build-time internet required, runtime not needed)
- Install CPU-only torch before the MCP package if ML deps are involved
- Pre-bake model downloads at build time (critical for air-gapped)
- Add docker-mcp.yaml catalog entry: id, image, command
- Set DOCKER_MCP_IN_CONTAINER=1 in gateway compose env
- Pattern A (dynamic via docker.sock) for dev; Pattern B (static) for production
結論: カタログ形式が鍵 — v3形式なら自前イメージも動く(2026年4月確認済み)
v1/v2形式のカタログでは自前イメージは動かないが、version: 3 + registry: マップ形式なら自前イメージも正常動作する。
| 試したオプション | 結果 | 備考 |
|---|
v1形式 + --servers=docling | ❌ dynamic-tools disabled, 0 tools | v1形式は非対応 |
--enable-all-servers + v1カタログ | ❌ No server is enabled | --enable-all-servers はDocker公式カタログのみ参照 |
--oci-ref=docker.io/mitlabo/docling-mcp:latest | ❌ artifact type is not application/vnd.docker.mcp.server | OCI MCP アーティファクト形式(特殊ラベル付き)が必要 |
v3形式 + --additional-catalog + --servers=docling | ✅ 19 tools 正常動作 | これが正解 |
✅ v3形式カタログ + --additional-catalog + --servers の動作確認済み構成
以下の条件をすべて満たすと、Docker MCP Gateway で自前イメージが19ツール認識される:
- カタログファイルを version: 3 + registry: マップ形式 で書く(v1形式は無効)
--additional-catalog=/path/to/catalog.yaml フラグで追加(--catalog で置換してもOK)
--servers=docling で有効化(必須。ないと「No server is enabled」で0 tools)
- volumes の documents マウントは
:rw(save_docling_document のための書き込み権限が必要)
env: CACHE_DIR: /tmp/docling-cache を必ず設定(設定しないと save_docling_document が /usr/local/lib/python3.12/site-packages/_cache への書き込みで権限エラー)
version: 3
name: docling-custom
displayName: Docling Custom Catalog
registry:
docling:
description: PDF, DOCX, PPTX, XLSX, HTML などのドキュメントを変換する MCP サーバー
title: Docling Document Processor
type: server
image: docker.io/mitlabo/docling-mcp:latest
cmd:
- --transport
- stdio
- conversion
- generation
- manipulation
volumes:
- /opt/docling-workspace/documents:/documents:rw
- docling-models:/root/.cache
env:
- name: CACHE_DIR
value: /tmp/docling-cache
- name: DOCLING_MCP_KEEP_IMAGES
value: "true"
tools:
- name: convert_document_into_docling_document
prompts: 0
resources: {}
metadata:
category: productivity
tags:
- docling
services:
mcp-gateway:
image: docker/mcp-gateway:latest
ports:
- "8811:8811"
command:
- --transport=streaming
- --port=8811
- --additional-catalog=/mcp-catalog.yaml
- --servers=docling
- --long-lived
- --log-calls
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./mcp-catalog.yaml:/mcp-catalog.yaml:ro
- ./documents:/documents:rw
- docling-models:/root/.cache
environment:
- DOCKER_MCP_IN_CONTAINER=1
restart: unless-stopped
起動ログで以下が出れば成功:
- Those servers are enabled: docling
- docling: (19 tools)
> Start streaming server on port 8811
Claude Code からの接続は --transport http(SSEではなく HTTP Streaming)
claude mcp add --transport sse docling http://localhost:8811/mcp
claude mcp add --transport http docling http://localhost:8811/mcp
.mcp.json には "type": "http" で記載:
{
"mcpServers": {
"docling": {
"type": "http",
"url": "http://localhost:8811/mcp"
}
}
}
save_docling_document の CACHE_DIR と --allowedTools の注意
save_docling_document ツールは /tmp/docling-cache/ に .json と .md の2ファイルを生成する。
CACHE_DIR が設定されていれば変換・保存は成功するが、Claude Code が後処理で Bash ツールを使おうとする場合がある。
--allowedTools "mcp__docling__*" のみでは Bash が許可されていないため permission_denial になる。
後処理(ファイルコピー等)も許可する場合は --allowedTools "mcp__docling__*,Bash" を指定すること。
CLAUDE_CODE_USE_BEDROCK=1 /root/.hermes/node/bin/claude \
--mcp-config /opt/docling-workspace/.mcp.json \
--allowedTools "mcp__docling__*,Bash" \
--max-turns 5 \
"db_model_complex.xlsx を変換して /tmp/docling-output に保存してください"
⚠️ Pattern C の重大な制約: document_key はコンテナ間で共有されない
docker run --rm はツール呼び出しのたびに新しいコンテナを起動して終了する。
docling-mcp のインメモリキャッシュもそのコンテナの生存期間に限られるため、
convert_document_into_docling_document で得た document_key は次の呼び出しでは無効になる。
# 1回目の呼び出し(コンテナA起動→終了)
convert_document_into_docling_document(source=...) → document_key: "abc123"
# 2回目の呼び出し(コンテナB起動)
export_docling_document_to_markdown(document_key="abc123")
# → ERROR: "Existing document-keys are:" (空 = キャッシュなし)
対処策:
- 1つの
claude -p "..." プロンプト内で変換→保存まで一気通貫で指示する(Claude Code が同一コンテナ内で連続ツール呼び出しをするように誘導)
--max-turns 20 以上を指定してセッション内で完結させる
- 推奨プロンプト例:
CLAUDE_CODE_USE_BEDROCK=1 /root/.hermes/node/bin/claude \
--allowedTools "mcp__docling__*,Write,Read" \
-p "以下を1つのセッションで連続実行してください:
1) convert_document_into_docling_document(source=/documents/db_model_complex.xlsx)
2) 返ってきたdocument_keyで export_docling_document_to_markdown
3) 同じdocument_keyで save_docling_document" \
--max-turns 20
- ステップを分割した
-p "..." の複数回呼び出しは機能しない(document_keyが消える)
Pattern C での docling-models ボリューム永続化(重要)
毎回コンテナを起動するのでモデルキャッシュを永続化しないと毎回DLが走る。
事前にボリュームを作成しておく:
docker volume create docling-models
.mcp.json の args にボリュームマウントを含める:
"-v", "docling-models:/app/.cache"
Pattern C の .mcp.json 正確な設定(hermes-dev 動作確認済み)
{
"mcpServers": {
"docling": {
"type": "stdio",
"command": "docker",
"args": [
"run", "--rm", "-i",
"-v", "/opt/docling-workspace/documents:/documents:ro",
"-v", "docling-models:/app/.cache",
"-e", "DOCLING_ARTIFACTS_PATH=/app/.cache/docling/models",
"-e", "HF_HOME=/app/.cache/huggingface",
"docker.io/mitlabo/docling-mcp:latest",
"--transport", "stdio",
"conversion", "generation", "manipulation"
]
}
}
}
接続確認コマンド(hermes-dev, Claude Code):
cd /opt/docling-workspace
/root/.hermes/node/bin/claude mcp list
MCP登録スコープの注意
claude mcp add で登録すると「local config」スコープになる。
.mcp.json に書くと「project config」スコープになる。
両方に同じ名前で登録されると claude mcp remove docling が失敗し、
スコープを明示して個別に削除する必要がある:
claude mcp remove docling -s local
claude mcp remove docling -s project
Claude Code 運用 Tips(hermes-dev)
.claude/settings.json で MCPツールを事前許可する
--dangerously-skip-permissions は root 環境で使えないが、プロジェクトの .claude/settings.json に permissions を設定すれば MCP ツールを自動承認できる:
{
"permissions": {
"allow": [
"mcp__docling__*"
],
"deny": []
}
}
配置場所: /opt/docling-workspace/.claude/settings.json
これにより -p の非対話プロンプトでも MCP ツールが許可確認なしで実行される。
--dangerously-skip-permissions は root 環境で使えない
--dangerously-skip-permissions cannot be used with root/sudo privileges for security reasons
代替策: --allowedTools で必要なツールを明示的に許可する:
CLAUDE_CODE_USE_BEDROCK=1 /root/.hermes/node/bin/claude \
--allowedTools "mcp__docling__*,Write,Read" \
-p "..." \
--max-turns 20
.claude.json の正しいパス(hermes-dev)
/root/.claude.json ではなく /root/.hermes/profiles/bedrock-slack/home/.claude.json
hasTrustDialogAccepted を Python で更新する例:
import json
path = '/root/.hermes/profiles/bedrock-slack/home/.claude.json'
with open(path, 'r') as f:
d = json.load(f)
d.setdefault('projects', {}).setdefault('/opt/docling-workspace', {})['hasTrustDialogAccepted'] = True
with open(path, 'w') as f:
json.dump(d, f, indent=2)
AWS Bedrock 接続確認(最小コマンド)
CLAUDE_CODE_USE_BEDROCK=1 /root/.hermes/node/bin/claude \
-p "日本語で「こんにちは」と一行だけ返してください" --max-turns 1
auth status = "Not logged in" は Bedrock 接続時の正常状態。~/.claude/settings.json への設定追加は不要で、CLAUDE_CODE_USE_BEDROCK=1 環境変数のみで動作する。
Docker Hub PAT の接続確認
TOKEN=$(cat /tmp/dockerhubpat.txt | tr -d '[:space:]')
echo "$TOKEN" | docker login --username mitlabo --password-stdin
docker logout
リポジトリへの .claude/settings.json 追加(.gitignore 除外問題)
ルートの .gitignore が .claude/ を丸ごと除外している場合、git add .claude/settings.json は silently 無視される。
対処: サブディレクトリに否定パターン .gitignore を追加する(gitignore-subdirectory-exception スキル参照)
!.claude/
!.claude/settings.json
この .gitignore を追加してから git add すると settings.json が追跡対象になる:
git add container/claudecode/docling-mcp/.gitignore
git add container/claudecode/docling-mcp/.claude/settings.json
git status --short
注意: ルートの .gitignore に !.claude/ を追加する方法は機能しない(ルートの否定パターンはサブディレクトリの否定に影響しない)。必ずサブディレクトリの .gitignore を使うこと。
⚠️ mcp-catalog.yaml の env フォーマットはリスト形式のみ正常動作(2026年4月 hermes-dev 確認済み)
mcp-catalog.yaml の env フォーマットはリスト形式(- name: / value:)が正しい。
マップ形式(KEY: VALUE)を使うと Gateway 起動時に YAML パースエラーが発生する。
# マップ形式でのエラーログ
reading catalog: yaml: unmarshal errors:
line 17: cannot unmarshal !!map into []catalog.Env
env:
- name: CACHE_DIR
value: /tmp/docling-cache
- name: DOCLING_MCP_KEEP_IMAGES
value: "true"
公式カタログの確認方法(正しいフォーマットの参照元):
curl -s https://desktop.docker.com/mcp/catalog/v3/catalog.yaml | grep -A5 "env:"
完全クリーンアップ手順(再構築前の初期化)
環境を完全に初期化して README 手順から再構築する際の手順:
cd /opt/docling-workspace
docker-compose down -v
docker volume rm docling-models 2>/dev/null || true
rm -f /opt/docling-workspace/.claude/agents/*.md
rm -f /opt/docling-workspace/.claude/commands/*.md
rm -f /opt/docling-workspace/.claude/rules/*.md
rm -f /opt/docling-workspace/.mcp.json
rm -f /opt/docling-workspace/mcp-catalog.yaml
rm -f /opt/docling-workspace/.env
docker ps | grep docling
docker volume ls | grep docling
ls /opt/docling-workspace/.claude/agents/
注意: docker-compose down -v の -v は compose が管理する名前付きボリューム(docling-workspace_docling-models 等)のみ削除する。docker volume rm docling-models で手動作成したボリュームは削除されないため個別に docker volume rm docling-models が必要。
注意2: .claude/settings.json と .claude/settings.local.json は削除しないこと。これらはClaude Code のMCP有効化設定(enabledMcpjsonServers: ["docling"])と自動許可設定(permissions.allow: ["mcp__docling__*"])を含むため、削除するとClaude CodeからMCPが使えなくなる。
setup.sh の非対話実行(CI/自動化向け)
デフォルト値(DOCLING_WORKSPACE=スクリプトのあるディレクトリ、DOCLING_GATEWAY_PORT=8811)で自動実行する場合:
printf "\n\n" | bash setup.sh
カスタム値を指定する場合:
printf "/opt/my-workspace\n9000\n" | bash setup.sh
環境依存箇所の変数化と再現可能セットアップ
別環境でも git clone してすぐ動かせるようにするため、絶対パスやポート番号を環境変数に切り出す。
変数化すべき箇所
- DOCLING_WORKSPACE は必須(絶対パスは環境依存のためデフォルト値なし)
- DOCLING_GATEWAY_PORT はデフォルト値 8811(省略時は従来どおり動作)
.env.example テンプレート(リポジトリにコミット)
# 作業ディレクトリの絶対パス(必須)
DOCLING_WORKSPACE=/opt/docling-workspace
# Gateway がリッスンするポート(デフォルト: 8811)
DOCLING_GATEWAY_PORT=8811
.env 本体は .gitignore で除外する(ローカル差分を汚さないため)。
setup.sh(対話式セットアップスクリプト)の要点
setup.sh は以下を実施する:
- .env が未存在なら対話プロンプトで DOCLING_WORKSPACE / DOCLING_GATEWAY_PORT を入力させ .env を生成
- ${DOCLING_WORKSPACE}/documents/ ディレクトリを作成
- docker volume create docling-models(冪等)
- .mcp.json の URL を実際のポートに sed で更新
chmod +x setup.sh
git add setup.sh
git diff --staged setup.sh | head -3
setup.sh を git add する前に chmod +x を忘れると 100644 でコミットされ、clone 先で ./setup.sh が Permission denied になる。
⚠️ mcp-catalog.yaml の volumes は Docker MCP Gateway が直接 docker run に渡す — シェル変数展開が効かない
mcp-catalog.yaml の volumes に ${DOCLING_WORKSPACE}/documents:/documents:rw と書いても、
Docker MCP Gateway はその文字列をシェルを介さずに直接 docker run -v に渡すため、
$DOCLING_WORKSPACE がリテラル文字列のまま Docker Daemon に届いてエラーになる。
# エラー例(ログに出る)
docling: docker: Error response from daemon: create $DOCLING_WORKSPACE/documents:
"$DOCLING_WORKSPACE/documents" includes invalid characters for a local volume name
対処策: setup.sh でシェル変数を展開した状態の mcp-catalog.yaml を動的生成する
source .env
cat > mcp-catalog.yaml << CATALOG_EOF
version: 3
...
volumes:
- ${DOCLING_WORKSPACE}/documents:/documents:rw # ← シェルが展開してから書き込む
CATALOG_EOF
これにより mcp-catalog.yaml には実パス(例: /opt/docling-workspace/documents:/documents:rw)が書き込まれ、
Gateway は正しく docker run -v /opt/docling-workspace/documents:/documents:rw を実行できる。
重要: テンプレートとして ${DOCLING_WORKSPACE} を記載した mcp-catalog.yaml をリポジトリにコミットしても構わないが、
実際に動かす前に必ず setup.sh を実行して実パス展開済みのファイルを生成すること。
リポジトリの mcp-catalog.yaml はテンプレート扱い(直接使用不可)。
docker-compose.yml の volumes は compose が .env を自動読み込みして展開するため問題なし(Gateway には展開済みパスが渡る)。
問題があるのは mcp-catalog.yaml の volumes のみ。
⚠️ 手動コピー時の注意:.env は自動生成されない
cp でファイルをコピーした場合、.env は含まれていないため別途手動作成が必要。
.env がないと docker-compose が ${DOCLING_WORKSPACE} を空文字に展開し、
volumes マウントが壊れてファイル変換が失敗する。
確認コマンド:
docker-compose config | grep documents
→ /opt/docling-workspace/documents:/documents:rw ← 展開成功
→ :/documents:rw ← 空文字 = .env 未読み込み
⚠️ cp 後は setup.sh のバージョンを必ず確認する
setup.sh を cp でコピーした場合、古いバージョン(mcp-catalog.yaml 生成ステップなし)が
残っていないか行数で確認すること:
wc -l /opt/docling-workspace/setup.sh
最新版の setup.sh には以下のステップが含まれる:
- .env 生成
- mcp-catalog.yaml を実パスに展開して生成(最重要)
- documents/ 作成
- docker volume create docling-models
- .mcp.json の URL 更新
⚠️ README のサンプル mcp-catalog.yaml の env フォーマットは常にリスト形式を維持
setup.sh 内のヒアドキュメントと README サンプルの両方で、env は必ずリスト形式にすること。
(hermes-dev + docker/mcp-gateway:latest 2026年4月時点で確認済み)
env:
- name: CACHE_DIR
value: /tmp/docling-cache
- name: DOCLING_MCP_KEEP_IMAGES
value: "true"
また README の設定ファイル詳細セクション(mcp-catalog.yaml のサンプル)は
「setup.sh が自動生成する」という注記と「生成後のファイル例(実パス展開済み)」の形式で記載すること。
変数プレースホルダ(${DOCLING_WORKSPACE})を直接サンプルとして載せると
読者が Gateway に渡して動かないと混乱する。
⚠️ setup.sh のヒアドキュメント(tools リスト・env フォーマット)は実際と同期する
setup.sh が動的生成する mcp-catalog.yaml 内の tools: リストと env: フォーマットも、
実際にコンテナが公開するツール名・正しいフォーマットに合わせる必要がある。
setup.sh の tools リストが旧ツール名のまま残るパターン(よくある落とし穴):
mcp-catalog.yaml テンプレートファイルを更新しても setup.sh のヒアドキュメント部分を更新し忘れる
setup.sh が mcp-catalog.yaml を上書き生成するため、テンプレートに正しい名前を書いても起動時に旧名で上書きされる
setup.sh の env フォーマットが間違っているパターン(よくある落とし穴):
setup.sh のヒアドキュメント内でマップ形式(CACHE_DIR: /tmp/docling-cache)を使っている
setup.sh が mcp-catalog.yaml を上書き生成するため、起動時に YAML パースエラーが再発する
確認コマンド:
grep -n "name:" /path/to/setup.sh | head -30
grep -A3 "env:" /path/to/setup.sh
⚠️ DOCLING_MCP_KEEP_IMAGES の環境変数反映には docker-compose down && up が必要
docker-compose restart だけでは環境変数の変更が docling-mcp コンテナには反映されない。
Gateway が管理するサブコンテナ(dazzling_carver 等のランダム名)は Gateway が docker run で起動するため、
docker-compose.yml の変更を反映するには Gateway コンテナ自体を down && up し直す必要がある。
確認コマンド:
docker ps --format "{{.Names}}\t{{.Image}}" | grep mitlabo
docker inspect dazzling_carver | python3 -c "
import sys, json
data = json.load(sys.stdin)
[print(e) for e in data[0]['Config']['Env'] if any(k in e for k in ['KEEP', 'CACHE', 'DOCLING'])]
"