はじめに
技術チャレンジ部のとも(Tomo)です。
以前作った、DifyとDiscordやSlackをつなぐチャットボットに、ナレッジを持たせたい!と思い、DifyでRAGを色々試していましたが、チャンク分割と検索というアプローチがうまくマッチしないケースもあると感じていました。
技術チャレンジ部では、ブラウザのみでwikiのように使えるOutlineに、VR活動で訪問したVRChatワールドの情報などを蓄えていました。
OutlineにはAPIもあり、世の中のコンテキストウィンドウの拡大も著しく、これを丸ごと読ませられれば、またRAGとは異なる結果になるのでは…?
そこで今回は、Outlineをナレッジとして活用できるか、チャレンジします。
ご注意
いずれも執筆時点で「試してみた」レベルの記事となっています。
日々進化している領域で、自分の理解が追いついていない部分があります。
特にセキュリティにはご注意ください。
元の環境
- Ubuntu 22.04 Server
- Docker Engine上で動作する、セルフホストのDify
- Docker Engine上で動作する、セルフホストのOutline
- DiscordやSlackとDifyをつなぐ、自作Pythonスクリプトのチャットボット
全体設計
世の中MCPが話題なので、チャットボット <-> Dify <-> MCP <-> Outline という構成を検討します。
OutlineでMCPを使えないかと調べたところ、いくつか実装があるようです(未比較)。
他にも、MCPを集約して利用するようなサービスがいくつかあるかと思います(未調査)。
元環境がセルフホストということもあり、試しにmcp-outlineを使ってみます。
mcp-outline
インストール
uv
READMEのInstallationを読むと、uvを使うようです。
今回は、uvのInstallationの最初にある、curlでインストールしてみます。
curl -LsSf https://astral.sh/uv/install.sh | sh
uvでvenvを作れるということで、作ってみます。
uv venv mcp-outline-venv
source mcp-outline-venv/bin/activate
cd mcp-outline-venv
mcp-outline
READMEのInstallationの通りに行います。
git clone https://github.com/Vortiago/mcp-outline.git
cd mcp-outline
uv pip install -e ".[dev]"
設定
READMEのConfigurationの通り、.env
にOutlineのAPIキーとURLを設定します。
-
OUTLINE_API_KEY
: 設定 -> アカウント -> APIキー -> (画面右上の)新しいAPIキー… で生成 -
OUTLINE_API_URL
:http://localhost:3000
など
起動
早速動かそうとしましたが、MCPに無知すぎて、何を行えばよいか、全くわかりません。
READMEのInstallationには、mcp dev src/mcp_outline/server.py
と、./start_server.sh
があり、どういう関係かわかりません。Dify側も、どうすればいいか、わかりません。
MCPのことを理解するのも目的の1つなので、tcpdump
なども活用しながら、理解を進めました。
MCPの調査
試行錯誤で理解したのは、以下のような点です。
(無知すぎて、間違っているかも&明日には世の中が変わっているかも、すいません)
- MCPには、現状、stdioとSSEがある
- stdioは、標準入出力を使う
- SSEは、Server-Sent Eventsを使う (Dify APIでSSEに少し触れていてよかった…)
- 今後、Streamable HTTPに変わっていく模様
- 認証は、MCPの2025-03-26の仕様で規定されたが、まだサポートされていない実装もありそう
-
MCP Inspectorという、ブラウザからMCPの動作を確認できるツールがある
- mcp-outlineでは、
mcp dev src/mcp_outline/server.py
で起動できる - 起動すると、MCP Inspector client UI(MCPI)とMCP Proxy(MCPP)がそれぞれ起動する
- MCPIは、起動時のコンソールログより、
http://127.0.0.1:6274
でアクセスできる (アドレスの変え方はわからない) - MCPPは、6277でアクセスできるが、MCPプロセスと通信できてしまうので、セキュリティ的に公開しないほうがよい (と書かれているが、
ss
で調べると、6277はデフォルトでは*でlistenしている)
- MCPIは、起動時のコンソールログより、
- mcp-outlineでは、
- mcp-outlineの実装は、ClaudeとWSL2環境でのstdioが想定されている
MCP Inspectorの使用
起動
venvを有効化し、MCP Inspectorを起動します。
source .venv/bin/activate
mcp dev src/mcp_outline/server.py
なお、起動しても、コンソールには何も表示されません。
sshポートフォワーディング
MCP Inspector client UIはhttp://127.0.0.1:6274
でアクセスできますが、リモートでホストするため、ブラウザの動く手元のPCからsshでポートフォワーディングします。
ssh -L 6274:localhost:6274 remotehost -N
ブラウザからの接続
ブラウザでhttp://localhost:6274
に接続します。
stdioでの使用 (あまり試せていません)
(画面左下の)Connectボタンを押せばすぐ使える…と思いましたが、当方ポンコツのため色々ひっかかりました。
結果として、以下の設定が必要でした。
- Arguments -> Configuration -> Inspector Proxy Address に、httpで始まるMCPPのURL(
http://localhost:6277
など)を入力する- MCPIからアクセスするURLのはずなので、
localhost
などでも大丈夫
- MCPIからアクセスするURLのはずなので、
- (必要に応じて)環境変数を追加する
Connecting to MCP Inspector Proxyエラー
Connectボタンを押すと、画面左下にError Connecting to MCP Inspector Proxy - Check Console logs
と出たので、試行錯誤しました。
結果として、Arguments -> Configuration -> Inspector Proxy Address には、http://remotehost:6277
など、MCPPのアドレスをhttp://
から入力する必要があるようです。
uvのVIRTUAL_ENVエラー
これでConnectできるかと思いきや、画面左下に
warning: `VIRTUAL_ENV=/home/tomo/mcp-outline-venv` does not match the project environment path `.venv` and will be ignored; use `--active` to target the active environment instead
といったエラーが出続けました。軽く調べた範囲では、UV_PROJECT_ENVIRONMENT
と、VIRTUAL_ENV
が一致していれば、回避できるようです。
Arguments -> Environment Variables -> Add Environment Variable で環境変数を追加し、UV_PROJECT_ENVIRONMENT
にVIRTUAL_ENV
の内容(フルパス)を書いてみます。(最適解でない可能性あり)
接続後
接続すると、
- Command:
uv
- Arguments:
run --with mcp mcp run src/mcp_outline/server.py
が、それぞれ入力されていました。これで色々実験できそうです。
SSEでの使用
SSEサーバーの起動
mcp-outlineをSSEに対応させるために、src/mcp_outline/server.py
のmcp.run()
を、mcp.run(transport="sse")
に書き換えた後、SSEサーバーを起動します。
source .venv/bin/activate
bash ./start_server.sh
設定
- stdioと同じように、Arguments -> Configuration -> Inspector Proxy Address に、httpで始まるMCPPのURL(
http://localhost:6277
など)を入力 - (画面左上の)URLに、SSEサーバーのアドレス(デフォルトでは
http://localhost:8000/sse
でアクセスできるはず)を入力
これでConnectできるようになりました。

MCP InspectorによるOutlineの挙動確認
LLMの気持ちになって(?)MCP InspectorでOutlineの挙動の確認することで、LLMからOutlineがどう見えるのか、よくわかりました。
この理解が、後々のLLMのプロンプト改善に役立ちました。
MCP Inspectorの操作
詳細は本家に譲りますが、下記を試してみました。
- (画面上の)Tools -> List Tools で、Toolの一覧が取得できる
- 取得後、一覧にある各Toolをクリックすることで、Toolを呼び出せる
- 呼び出した結果は(画面右の)Tool Resultに表示され、JSONは(画面下の)Historyにtool/callとして格納されるので、展開して見られる
- HistoryのResponse内の長い文字列は折りたたまれているので、クリックで展開して見られる
- Historyは、新しい操作が上になる模様
MCPで呼び出せるOutlineの挙動
気になった挙動をいくつかピックアップします。
- 呼び出せるToolの説明が(英語で)取得できる
- コレクション(Outlineの大分類)の一覧が取得できる
- コレクションのタイトルだけでなく、コレクションに直接書かれた内容も取得できる
- コレクション配下のドキュメント一覧は、コレクション一覧には入ってこない
- コレクションのIDを指定すれば、ドキュメントの一覧を取得できる
- 検索機能がある
基本はOutlineのAPIそのものかと思います。LLMは、自然言語の説明を読んでToolを活用するというわけですね。
MCP Inspectorではこんな感じで見られます。
Difyからの呼び出し
ここまででmcp-outlineが動かせるようになりました。MCP Inspectorを停止し、DifyからMCPで接続します。
Difyでは、マーケットプレースにてMCP SSEとMCP Agent Strategyがプラグインとしてインストールできるようです。
いずれもlanggeniusによるものではなさそうですが、dify.aiのblogで紹介されていました。また、SSEに対応しているようです。
今回はチャットフローで使いたいので、MCP Agent Strategyを使ってみます。
mcp-outlineは、SSEで使用するので、start_server.sh
でSSEサーバーを起動しておきます。
MCP Agent Strategyのインストール
(画面右上の)プラグイン -> (画面左上の)マーケットプレイスを探索する -> (画面中の)エージェント戦略 で、MCP Agent Strategyをインストールします。
DockerのDifyからホスト上のサービスへの接続
元々、DifyのDockerコンテナからホスト上のサービスに接続できるように、Difyのdocker-compose.override.yaml
のextra_hosts:
で- "host.docker.internal:host-gateway"
を設定していました。これをこのまま利用します。
docker-compose.override.yaml
では、api
とplugin_daemon
のservice
に対して、host.docker.internal
を設定していました。(どちらが必須かなど、詳細未調査)
チャットフロー
エージェントノードを追加し、以下のように設定してみました。
- エージェンティック戦略: MCP Agent -> MCP FunctionCall を設定
- MCP Server URL:
{ "outline": { "url": "{{#env.mcp_url#}}", "headers": {}, "timeout": 60, "sse_read_timeout": 300 }}
- (画面右上の)ENVで、環境変数
mcp_url
に、エンドポイントも含むhttp://host.docker.internal:8000/sse
を設定
- (画面右上の)ENVで、環境変数
- Maximum Iterations: デフォルトの
3
では少なそうなので、とりあえず5
を設定
実行と解析
これでついに質問ができる! と思い、DifyのプレビューでOutlineに含まれていそうな情報の質問をしてみます。…が、微妙な回答…。
Difyでの解析
Difyの会話ログ -> 実行追跡 -> エージェント(ノードの名前) -> function calling -> 詳細情報 で、エージェントの各Roundの挙動をJSONレベルで確認します。
すると、LLMがまずOutlineの検索機能でキーワード検索し、検索にかからなかったのであきらめたりしている様子が見えました。
Outline
LLMの気持ちになって、Outlineの画面で単語を入れて色々検索してみます。
うーん、確かにこれは厳しいかも…。
改善
Outlineのドキュメント構造
MCP Inspectorでの確認で、Outlineのコレクション一覧を取得する際に、コレクションに直接書かれた内容も取得できることがわかっています。
そこで、コレクションのトップ階層に、目次として、各ドキュメントにどんなことが書いてあるか、ドキュメントへのリンクとセットで記述しておきます。リンクには、ドキュメントのIDが入っているので、該当ドキュメントを取得してくれると期待します。

DifyのLLMプロンプト
Maximum Iterationsが5回だと、検索などを試しているうちに、最大回数に近づいてしまいそうです。
そこで、Maximum Iterationsを8回にしつつ、エージェントノードのInstructionのプロンプトで、
ドキュメントの調査は、Outlineの検索機能は使わず、コレクションリストの内容から関連しそうなドキュメントIDを探し出す方法で行い、そのIDのドキュメント内容を取得・解釈した上で回答して下さい
などと指定してみます。
SSEサーバーのlistenアドレス変更
SSEサーバーのlistenアドレスをss
で確認すると、0.0.0.0:8000
になっていました。
気分レベルの対処ですが、Docker内部からhost.docker.internalで解決されるデフォルトブリッジのアドレスに変えてみます。
ip addr show dev docker0
で見ると、この環境でのアドレスはデフォルトの172.17.0.1
のようです。
変え方がわからなかったのですが、MCP Python SDKのfastmcpのserver.pyのコードを読むと、FASTMCP_HOST
という環境変数で設定できそうだったので、.env
にFASTMCP_HOST="172.17.0.1"
を追記したところ、効果がありました。
SSEサーバーのsystemd --user化
SSEサーバーをsystemd --userで管理するために、.serviceファイルを設置しました。(内容は適当です)
[Unit]
Description=mcp sse server for outline
After=network.target
[Service]
WorkingDirectory=/home/tomo/mcp-outline-venv/mcp-outline
ExecStart=bash start_server.sh
Restart=always
Environment="PYTHONUNBUFFERED=1"
[Install]
WantedBy=default.target
その後、起動・確認しました。
systemd --user daemon-reload
systemd --user enable --now mcp-outline.service
systemd --user status mcp-outline.service
ユーザーがログアウトしても動き続けるように、だいぶ前に、lingeringを有効にしていた…と思われる…。
sudo loginctl enable-linger $(USER)
改めて実行

やったね!
感想
未知の世界で、AI自身のMCP関連知識もまだ少なめの模様、自分の中で総力戦でした。
組織でのナレッジ活用に向けて、情報の単位の切れ目をどう表現するか、コンテキストウィンドウとコストにどう合わせるか、それをどうLLMに伝えるか、その結果をどうキャッシュするかなど、今後進化があるかもと思いました。
LLM側の進化で、LLMが既存の情報構造に歩み寄ってくれるかもですが…。
課題と今後
- mcp-outline
- 再起動や停止に時間がかかる: グレースフルな終了には対処が必要そう
- セキュリティ: MCP Python SDKの認証の実装進展に期待(自分でやりましょう>私)
- Outline
- APIキーへのパーミッション: 詳細を調べて、適切なパーミッションにしたい
- 今は自然言語で「変更しないでね」とLLMにお願い…指示追従性は高まっているそうですが、AIなので…
- APIキーへのパーミッション: 詳細を調べて、適切なパーミッションにしたい
- Dify
- (チャットフローではなく)エージェントで作る場合: プラグインと挙動の調査
- 技術チャレンジ部でのユースケース
- VR活動: VRChatの訪問先を自動的にデータベース化したい!(お気に入りがもういっぱいで…)
- 自動運転AIチャレンジ: GitHubのソースコードも見て欲しい!
謝辞
- VR活動の記録を残し続けていただいている、たけさん、いつも本当にありがとうございます!
- 多数の記事に助けられました! ありがとうございます!