mcpField
C
Contextator

Contextator

リモート
contextator.comメモリ・ナレッジ13 日前に更新
リポジトリ公式サイト

Contextator について

Contextatorは、プロジェクトごとに独立した検索可能なMCPエンドポイントを設けるセルフホスト型ドキュメントサーバーです。ローカルファイル、リポジトリ、ドキュメントサイト、Notion、ConfluenceをAIクライアントから利用するチーム向けです。

主な機能

  1. 01プロジェクトごとの検索エンドポイント
  2. 02複数のドキュメントソース
  3. 03ローカルでの埋め込み生成
  4. 04ダッシュボードでの索引・アクセス管理
  5. 05変更ファイルの差分索引
  6. 06ドキュメント用ツール、リソース、プロンプト

活用シーン

  1. 01AIクライアントからプロジェクト文書を検索
  2. 02リポジトリ、Notion、サイト文書を統合検索
  3. 03変更ファイルだけを再索引

MCP 対応

各プロジェクトのMCPエンドポイントは、文書の検索、一覧表示、読み取り、変更追跡のツールと、文書リソースおよび2つのプロンプトを提供します。Streamable HTTPと、設定可能なアクセス制御に対応します。

設定

リモートサーバーはインストール不要です。クライアントを選んで設定をコピーするか、ワンクリックで導入できます。

エンドポイントhttps://{host}/mcp/{project}
インストールコマンドをコピー
Claude CodeTerminal
Cursor.cursor/mcp.json
VS Code.vscode/mcp.json
Claude Desktopclaude_desktop_config.json
Windsurf~/.codeium/windsurf/mcp_config.json

コマンドはターミナルで実行し、JSON は各クライアントの MCP 設定に貼り付けて再起動します。認可が必要なサーバーは初回接続時にログインを求めます。

すべてのエンドポイント
Streamable HTTPhttps://{host}/mcp/{project}認証が必要

ツール

詳細

Contextator

セルフホスト可能な MCP サーバーです。ドキュメントを、プロジェクトごとにセマンティック検索可能なエンドポイントへ変換します。

Tan による設計・保守

プロジェクトにドキュメントソース(マウントしたフォルダー、git リポジトリ、アップロードしたアーカイブ、Notion ワークスペース、Confluence Cloud サイトまたは Data Center インスタンス、公開ドキュメントサイト)を指定すると、そのプロジェクト専用の Model Context Protocol エンドポイントになり、AI エージェント(Cursor、Claude Code、Claude Desktop、…)から検索できるようになります。

http://localhost:3444/mcp/<project-name>

データベースとアプリの両方を含む contextator/contextator として公開された単一の Docker コンテナで提供され、AGPL-3.0-or-later のもとで利用できるフリーソフトウェアです。商用ライセンスも用意されています(ライセンス)。

機能

  • プロジェクトごとに専用 URL を用意し、完全に分離します。 各プロジェクトは、PostgreSQL + pgvector に独自のドキュメントコレクションとベクトル埋め込みを保持します。/mcp/billing に接続したクライアントから /mcp/mobile が見えることはありません。
  • プロジェクトごとに複数のソースを登録できます。 ローカルディレクトリ、git リポジトリ(またはその中のサブディレクトリ)、ファイル/フォルダー/.zip/.tar.gz/.rar のアップロード、Notion ワークスペース、Confluence Cloud サイトまたは Data Center インスタンス、あるいは sitemap.xml から読み込む公開ドキュメントサイトを、1 つの検索可能なエンドポイントにまとめられます。ドキュメントソースをご覧ください。
  • デフォルトで 100 % ローカルです。 埋め込みは CPU 上で transformers.js(100 言語をカバーする検索モデル Xenova/multilingual-e5-small。トルコ語を含みます)を使って生成されます。環境変数を 2 つ設定すれば OpenAI の埋め込みに切り替えられます。
  • 管理ダッシュボード(http://localhost:3444/)から、プロジェクトとそのソースを管理できます。リポジトリの追加、フォルダーやアーカイブのページへのドロップ、接続テスト、再インデックスの実行、進行状況の確認ができます。
  • アカウントとロール。 ユーザーは各自のアカウントでサインインします。root と admin はすべての項目とユーザーを管理します。member に表示されるのは割り当てられたプロジェクトのみで、読み取り専用の viewer、またはソースの追加、ファイルのアップロード、再インデックスを行える editor としてアクセスします。どのアカウントでも、スクリプトや CI ジョブ用に、名前とスコープを指定した取り消し可能なAPI トークンを発行できます。ADMIN_TOKEN も引き続き使用できます。アカウントと権限をご覧ください。
  • 各 MCP エンドポイントに 3 種類の設定が可能なアクセス制御を設けています。 新しいプロジェクトではベアラートークンが必須です。クライアントは通常のヘッダーとして送信し、プロジェクトの作成時に最初のトークンが発行されます。このトークンはそのとき一度だけ表示されます。URL にアクセスできる人なら誰でもドキュメントを読めるようにするには公開に設定します。またはアカウントを必須にすると、既に管理しているメンバーシップにアクセスが従います。ブラウザベースのコネクターは OAuth 2.1 でサインインします。MCP アクセスをご覧ください。
  • 増分インデックス作成。 ファイルをハッシュ化し、変更されたファイルのみ埋め込みを再生成して、削除されたファイルを取り除きます。
  • Markdown 以外にも対応。 .html、.docx、.csv、.pdf、.pptx、.xlsx、.ipynb、.rst、.adoc/.asciidoc はインデックス作成時に Markdown に変換されるため、エージェントは Word ファイルや PDF をドキュメントのページと同じように読み取れます。ファイル形式をご覧ください。

クイックスタート

すべては contextator という単一のコンテナ内で動作します。PostgreSQL 16 + pgvector と Node.js アプリが、小さなエントリーポイントスクリプトによってまとめて起動・停止されます。データは Docker ボリュームに保存され、コンテナを削除しても保持されます(データと永続化を参照)。公開されているイメージは contextator/contextator です。docker compose up -d でこれを取得するため、新規インストールに必要なのは以下の2つのファイルだけで、リポジトリ全体は必要ありません。

mkdir contextator && cd contextator
curl -fsSLO https://raw.githubusercontent.com/Contextator/Contextator/main/docker-compose.yml
curl -fsSLO https://raw.githubusercontent.com/Contextator/Contextator/main/.env.example
cp .env.example .env
# in .env, pick the code that /setup will ask for once:
#   SETUP_CODE=whatever-you-like
# optional: point DOCS_HOST_PATH at your own docs (it defaults to ./docs, created empty if missing)
docker compose up -d
docker compose logs -f            # wait for "embedding model ready"

ポートは 127.0.0.1 のみで公開されます。 ダッシュボードとすべての MCP エンドポイントはこのマシン上でのみ応答し、それ以外ではアクセスできません。そのため、ノート PC やオフィスのサーバーでコンテナを起動しても、接続先のネットワーク上には公開されません。ネットワーク上に意図的に公開するには、.env で CONTEXTATOR_BIND=0.0.0.0 を設定し、TLS 終端を行うリバースプロキシまたは VPN を前段に配置してください。リバースプロキシの背後で実行するを参照してください。

  1. 最初のアカウントを作成します。 http://localhost:3444/setup を開き、選択した SETUP_CODE を入力して、root アカウントを作成します。このコードは、あなたより先にサーバーへアクセスした人がアカウントを取得できないようにするためのものです。そのアカウントが作成された時点で、コードは使えなくなります。

    SETUP_CODE を空のままにしましたか? アカウントが作成されるまで、サーバーは起動のたびにコードを生成して表示します。すでに末尾を表示しているログに出力されています。

    ┌─ Contextator first-run setup ───────────────────────────────────────────┐
    │ No user accounts exist yet; the dashboard is waiting for its first one. │
    │                                                                         │
    │   Open   http://localhost:3444/setup                                    │
    │   Code   K7QM-92QX-VBHT                                                 │
    │                                                                         │
    │ A new code is printed on every start until that first account exists.   │
    └─────────────────────────────────────────────────────────────────────────┘
    
  2. http://localhost:3444/ でサインインします。

  3. 新しいプロジェクト(または n)を押します。名前を demo にし、ディレクトリには /docs 配下にある自分のサブフォルダーを1つ指定します。ホスト側の DOCS_HOST_PATH フォルダーはここにマウントされるため、ホスト上の docs/handbook はここでは /docs/handbook になります。ディレクトリを空のままにすると空のプロジェクトが作成されます。ソースは後から ソースを追加 で追加できます。

  4. リストでプロジェクトのステータスが indexing → idle になるのを確認します。ドキュメントソースパネルには、各ソースとそのドキュメント数、最終同期時刻、エラーが表示されます。

  5. エージェントを接続タブ(Claude Code、Cursor、Claude Desktop、非推奨のレガシー SSE)から、コピーして貼り付けられるスニペットを使います。ソースのチェックアウトからは、同じ操作を付属のスモークテストでコマンドラインから実行できます。

npm install && npm run smoke -- http://localhost:3444/mcp/demo "how do I re-index"

初回起動時にデータベースが初期化され、埋め込みモデル(デフォルトの fp32 モデルでは約 470 MB、EMBEDDING_DTYPE=fp16 では約 235 MB、EMBEDDING_DTYPE=q8 では約 120 MB、all-MiniLM-L6-v2 では約 90 MB)が contextator-models ボリュームにダウンロードされます。2回目以降の起動には数秒かかります。

Compose を使わない場合:

docker run -d --name contextator -p 127.0.0.1:3444:3444 \
  -e SETUP_CODE=whatever-you-like \
  -v contextator-pgdata:/var/lib/postgresql/data \
  -v contextator-models:/app/.cache/models \
  -v contextator-data:/data \
  -v /path/to/your/docs:/docs:ro \
  contextator/contextator

イメージを取得する代わりに、ソースからビルドする場合: イメージを自分でビルドする。

タグ。 latest は最新の安定版リリースです。1(1.0.0 以降)は 1.x メジャー系列の最新リリースを追跡し、1.0 は 1.0.x 系列内の最新パッチを追跡します。1.0.0 は変更されない特定のリリースです。意図的にアップグレードするものには、.env で CONTEXTATOR_TAG を設定してバージョン付きタグを指定し(例:CONTEXTATOR_TAG=1.0.0)、docker compose up -d を実行してください。この設定は初回だけでなく、起動のたびに読み込まれます。0.2.0 以降のこれらのタグにはすべて -slim 版(latest-slim、1-slim、1.0-slim、1.0.0-slim)があり、PostgreSQL を自分で用意するで説明しています。

使用方法

Claude Code、Cursor、Claude Desktop、またはその他の MCP クライアントから http://localhost:3444/mcp/<project> に接続します — ダッシュボードの Connect パネルには、プロジェクトでトークンが必要な場合はトークンヘッダーを含め、正確な設定スニペットが表示されます。トークンが必要なプロジェクトの場合:

claude mcp add --transport http demo-docs http://localhost:3444/mcp/demo   --header "Authorization: Bearer ctxm_9f3a…"

読み取り専用ツールが4つ公開されます。search_docs(ハイブリッド検索、順位付けされた抜粋)、list_topics(インデックス化されたドキュメントの一覧)、read_document(パスまたは見出し範囲を指定して1ファイルの Markdown を読み取り)、whats_new(ある時点以降に追加、変更、削除された内容)です。5つ目の report_gap を使うと、ドキュメントで回答できなかった内容をエージェントからメンテナーに報告できます。このツールは独自のテーブル(reported_gaps)に書き込み、ドキュメントには書き込みません。また、クエリログが無効な場合は利用できません。ツールに加えて、各プロジェクトはドキュメントを MCP リソースとして公開し、読み取り専用のプロンプトを2つ提供します。answer_from_docs(引数 question)と explore_topic(引数 topic)です。

URL は1つ、Streamable HTTP が優先です。 接続には Streamable HTTP を使用します。従来の HTTP+SSE トランスポートは非推奨です。同じ URL で引き続き応答し、引き続き利用できますが、廃止日は設定されていません。応答には Deprecation: true ヘッダーが含まれます。同じ URL は、2025-11-25 に加えて、ステートレス MCP 2026-07-28 プロトコルにも対応しています。

プロジェクトはソースの集合です。ソースには、ローカルディレクトリ、git リポジトリ、アップロード、Notion ワークスペース、Confluence サイト、ドキュメントサイトがあり、それぞれダッシュボードで独自の名前を付けて追加します。そのため、handbook という名前のソースにある install.md は、handbook/install.md として検索・読み取りされます。エージェントは search_docs(query: "rotate the signing key", version: "v3") を使って、検索対象を特定のリリースに絞り込めます。

その他のすべて — プロトコルの各時代、ソースの種類、アクセスモード、ファイル形式 — については、AI クライアントの接続、ドキュメントソース、MCP アクセス、ファイル形式をご覧ください。

仕組み

スタック:TypeScript · Node.js 24+ · Fastify 5 · PostgreSQL 16 + pgvector · Drizzle ORM · @modelcontextprotocol/server(MCP TypeScript SDK 2.x)· @huggingface/transformers · unpdf / mammoth / turndown。

実行のたびにすべてのソースを同期し、ファイルのハッシュを計算して変更のないファイルを除外します。変更のあったファイルを変換してチャンクに分割します(フロントマターを考慮し、見出しごとに分割して、各チャンクに階層を示すパンくずリストを保持します)。その後、各チャンクをベクトル化し、キーワードインデックスとともに保存します。検索はハイブリッド方式です。意味と正確な語句を順位に基づいて組み合わせるため、環境変数名も質問と同じように簡単に該当ページを見つけられます。強制再インデックスでは、稼働中のインデックスを先に置き換えず、その隣にすべてを再構築するため、実行中もプロジェクトは応答を続けます。CHUNK_MAX_TOKENS の既定値は、モデルのはるかに大きなコンテキストウィンドウではなく、実測に基づく 96 です。ウィンドウを埋めると、測定結果が悪化します。

実行全体の仕組み、チャンク予算の測定、ハイブリッド検索と順位融合、クロスリンガル検索の限界(固定値ではなく実測値と、代替策):インデックス作成および埋め込みモデル。

設定

すべて環境変数で設定します。注釈付きの一覧は .env.example を参照してください。 多くのインストール環境で使用される変数は次のとおりです。

変数デフォルト備考
PORT / HOST3444 / 0.0.0.0
DATABASE_URL–空の場合は組み込みの PostgreSQL を使用します。独自の PostgreSQL を使う場合は設定してください。詳しくは独自の PostgreSQL を使用するを参照してください
ALLOWED_DOC_ROOTS/docsカンマ区切りです。プロジェクトディレクトリは、これらのいずれかの中に必ず置いてください
SECRET_KEY–32 文字以上です。git/Notion/Confluence のトークンと Webhook シークレットを保存時に暗号化します
ADMIN_TOKEN–ルート権限で /api/* にアクセスするためのマシン用アクセス権限です
EMBEDDING_PROVIDER / EMBEDDING_MODELlocal / Xenova/multilingual-e5-small埋め込みモデルの変更を参照してください
PUBLIC_BASE_URL / TRUST_PROXY– / 0リバースプロキシの背後で使用する場合は、両方を設定する必要があります。詳しくはリバースプロキシの背後で実行するを参照してください
OIDC_ISSUER_URL–フェデレーションサインインのオン/オフを切り替えます。詳しくはシングルサインオンを参照してください

ストレージ、アップロード、チャンク化、検索ランキング、同期、認証、可観測性に関するすべての変数と、その設定理由を記載した完全な一覧は、設定を参照してください。

デプロイ

クイックスタートで使用する単一コンテナがデフォルトであり、ほとんどのインストール環境ではこの構成のまま使用できます。ほかに、次の 2 つの構成があります。

  • 独自の PostgreSQL。 .env に 1 行追加すると、データベースをコンテナの外部に移せます。

    DATABASE_URL=postgres://user:[email protected]:5432/contextator
    

    PostgreSQL 16 以降と pgvector が必要で、バックアップはご自身で管理することになります。-slim イメージにはデータベースが含まれていません。詳しくは独自の PostgreSQL を使用するを参照してください。

  • Kubernetes。 charts/contextator/ は、-slim と外部データベースを組み合わせた構成向けの Helm チャートです。詳しくはKubernetes で実行するを参照してください。

リバースプロキシの背後で使用する場合は、3 つの設定をまとめて変更する必要があります。 リバースプロキシの背後で実行するを参照してください。データの保存先と、マシンの外部に送信される情報については、データの行き先を参照してください。

データと永続化
項目コンテナ内のパスデフォルトのボリューム上書き設定(.env)
PostgreSQL クラスター:プロジェクト、ドキュメント、埋め込み/var/lib/postgresql/datacontextator-pgdataCONTEXTATOR_PGDATA_VOLUME(別のボリューム名)または CONTEXTATOR_PGDATA_PATH(ホスト上の絶対ディレクトリ)
ダウンロードした埋め込みモデル/app/.cache/modelscontextator-modelsCONTEXTATOR_MODELS_VOLUME または CONTEXTATOR_MODELS_PATH
マテリアライズされたソース:アップロードファイル、git チェックアウト、Notion の取得データ/datacontextator-dataCONTEXTATOR_DATA_VOLUME または CONTEXTATOR_DATA_PATH
ドキュメント(読み取り専用)/docs–DOCS_HOST_PATH(デフォルトは ./docs)

docker compose down、docker compose pull && docker compose up -d、docker rm contextator を実行しても、ボリュームは保持されます。削除されるのは docker compose down -v または docker volume rm を実行した場合のみです。例:

CONTEXTATOR_PGDATA_PATH=/srv/contextator/pgdata     # Linux server: keep the database on a chosen disk
CONTEXTATOR_MODELS_PATH=D:/contextator/models       # Windows host directory (forward slashes)
CONTEXTATOR_PGDATA_VOLUME=contextator-pgdata-v2     # or simply another named volume

ホストディレクトリは初回起動時に作成され、エントリーポイントが所有権を修正します。どちらの上書き方法も、Linux 形式のパスと、Docker Desktop for Windows(WSL 2 バックエンド)で動作を確認済みです。Docker Desktop for Windows では、C:/… ディレクトリ上で PostgreSQL が正常に初期化され、インデックスも作成されました。Docker Desktop でデータベースを使用する場合は、名前付きボリュームのほうが高速です。ホストディレクトリ上で initdb が権限エラーを報告した場合は、そのマウントをボリュームに戻してください。 DATABASE_URLを設定している環境の場合(独自の PostgreSQL の使用) このイメージが運用しないサーバーがデータベースになります。npm run backup と npm run restore は引き続き動作します。アーカイブ内のアップロードツリーはほかのバックアップには含まれないため、DATABASE_URL 経由でそのサーバーに接続します。また、backup は出力とアーカイブのマニフェストで、重要なのはサーバー独自のバックアップ体制であることを明示します。デフォルトイメージから実行してください。-slim イメージには pg_dump がなく、PATH にないことを示して停止します(拒否理由 no_pg_tools)。デフォルトイメージのクライアントは PostgreSQL 16 のため、17 以降のサーバーに対して実行すると、pg_dump は server version mismatch で停止します(また、pg_restore 16 は 17 のダンプを読み込めません)。コンテナにサーバーのメジャーバージョンに対応する postgresql-client-<major> をインストールし、/usr/lib/postgresql/<major>/bin を PATH の先頭に追加してください。Helm チャートのバックアップの取得で手順を説明しています。この構成では、組み込みクラスターに関する記述は適用されません。後述の手動コマンド 3 つ(ユーザー contextator として実行する psql、pg_dump、pg_restore)では何にも接続できず、pgdata ボリュームは空です。以下の、ダンプに含まれないものについての 2 つの箇条書きはどちらの構成にも適用され、/data に関する段落も同様です。

外部データベースの定期バックアップ。 slim スタックにオプションのサイドカーを追加します:

docker compose -f docker-compose.slim.yml -f docker-compose.backup.yml up -d
docker compose -f docker-compose.slim.yml -f docker-compose.backup.yml logs -f backup

サイドカーはアプリが正常な状態になるまで待機し、npm run backup を実行した後、各試行の完了から BACKUP_INTERVAL_SECONDS(デフォルトは 86400、1 日)待機します。スリープループを使うことで、cron デーモンに依存しない任意のレシピとしており、実行の完了後に間隔を計測するため、実行時刻が常に一定になるわけではありません。間隔と保持数を変更するには、.env で BACKUP_INTERVAL_SECONDS と BACKUP_KEEP を設定します。どちらも正の整数である必要があります。デフォルトの保持数は、完了したアーカイブ 7 件です。バックアップに失敗した場合、既存のアーカイブはすべて保持され、間隔を空けて再試行します。設定が無効な場合、コンテナの再起動は最大 3 回で停止します。.env を修正し、up -d backup を実行して再開してください。

PostgreSQL ツールにはデフォルトの非 slim イメージを使用し、そのイメージのエントリーポイントを上書きします。slim アプリに合わせて UID/GID 1000:1000 で実行し(npm 用に HOME=/tmp を設定)、/data に同じ data ボリュームだけをマウントします。組み込み PostgreSQL を起動したり、pgdata をマウントしたりすることはありません。.env はアプリと同じものを使い、DATABASE_URL と SECRET_KEY を含めます。これにより、アーカイブにはアプリの SECRET_KEY のフィンガープリントが記録され、そのキーを使った復元はキー検証に合格します。PostgreSQL クライアントのメジャーバージョンは、サーバーのメジャーバージョン以上である必要があります。

アーカイブは /data/backups/ に保存され、.partial ファイルとして書き込まれます。バックアップが正常に完了した場合にのみ名前を変更するため、書き込みの中断が保持数に数えられることはありません。停止時には、サイドカーは TERM を送信してバックアップのプロセスグループを終了し、1 秒後に KILL を送信して、部分アーカイブと非公開のステージングファイルを削除します。強制終了の後、次の実行では /data/.scheduled-backup-staging 以下に残った定期バックアップの .partial アーカイブとステージングファイルが削除されます。1 つのデータボリュームにつき、バックアップサイドカーは 1 つだけ実行してください。保持数の対象となるのは、デフォルトのタイムスタンプ付きファイル名 contextator-backup-<timestamp>.tar.gz のみです。独自の名前を付けたアーカイブはそのまま残ります。バックアップはホスト外にもコピーしてください。アプリと同じデータボリュームを共有していても、そのボリュームの喪失に備えることはできません。

オンデマンドのバックアップもデフォルトイメージから実行してください。slim アプリコンテナには pg_dump がないためです。backup サービスの定義を再利用しますが、ループを置き換え、BACKUP_STAGING_DIR をクリアしてください。これにより、バックアップは /data/.backup-* 以下にステージングされ、サイドカーのクリーンアップでは削除されません。保持数に数えられないよう、アーカイブには任意の名前を付けます:

docker compose -f docker-compose.slim.yml -f docker-compose.backup.yml run --rm --no-deps \
  -e BACKUP_STAGING_DIR= --entrypoint npm backup \
  run backup -- /data/backups/contextator-manual-$(date +%F).tar.gz

新規インストール環境にアーカイブを復元するには、以下の npm run restore -- <file> --check と npm run restore -- <file> の手順を使います。ただし、上記のオンデマンドバックアップと同じ方法で 両方のコマンドを実行してください(run --rm --no-deps -e BACKUP_STAGING_DIR= --entrypoint npm backup run restore -- …)。 スリムなアプリコンテナ内で docker exec を使わず、アプリは docker compose -f docker-compose.slim.yml restart contextator で再起動してください。復元中に バックアップサイドカーが復元処理中のアーカイブを作成しないよう、復元中は停止します。

docker compose -f docker-compose.slim.yml -f docker-compose.backup.yml stop backup

復元が完了したら、up -d backup で再度起動します。

組み込みの PostgreSQL はコンテナ内の 127.0.0.1 のみでリッスンしており、ポートは公開されていないため、 ここでの処理はすべてコンテナ経由で行います。

バックアップを取得するコマンドは 1 つです。pg_dump はインストール一式ではありません。

docker exec contextator npm run backup -- /data/backups/contextator-$(date +%F).tar.gz
docker cp contextator:/data/backups/contextator-$(date +%F).tar.gz .

新規インストール環境への復元(これが重要なケースです)は、次のディレクトリから始めます。

# The directory first — on a fresh installation nothing has created it yet, and `docker cp` into a
# directory that is not there answers `no such directory`.
docker exec contextator sh -c 'mkdir -p /data/backups'
docker cp contextator-2026-09-22.tar.gz contextator:/data/backups/

# Reads the manifest and every refusal, writes nothing:
docker exec contextator npm run restore -- /data/backups/contextator-2026-09-22.tar.gz --check
docker exec contextator npm run restore -- /data/backups/contextator-2026-09-22.tar.gz
docker compose restart contextator

パスを指定せずに npm run backup を実行すると、<DATA_DIR>/backups/contextator-backup-<timestamp>.tar.gz に書き込まれます。保存先はデータボリュームであり、コンテナの作業ディレクトリ(イメージレイヤー)ではありません。

S3 互換バケットへの保存。 BACKUP_S3_BUCKET、BACKUP_S3_ACCESS_KEY_ID、BACKUP_S3_SECRET_ACCESS_KEY を設定し、 (MinIO、Ceph、R2 の場合は任意で BACKUP_S3_ENDPOINT、さらに BACKUP_S3_REGION、 BACKUP_S3_PREFIX、BACKUP_S3_SESSION_TOKEN を設定して)--s3 を追加します。アーカイブはステージング ディレクトリ内に作成され、アップロードされ(16 MiB を超える場合はマルチパート)、ローカルから削除されます。また、プレフィックス配下には タイムスタンプ付きアーカイブのうち最新の BACKUP_KEEP 件(既定値は 7 件)のみが保持されます。

docker exec contextator npm run backup -- --s3
docker exec contextator npm run restore -- --s3 latest --check
docker exec contextator npm run restore -- --s3 latest
docker compose restart contextator

latest は最新のタイムスタンプ付きアーカイブを指し、名前を指定するとそのアーカイブが復元されます。リクエストはパス形式で、 スクリプト自体が SigV4 で署名するため、追加パッケージのインストールは不要です。サーバー側でこの処理はスケジュールされません。 Kubernetes ではチャートがオプションの CronJob を生成します (S3 への定期バックアップ。それ以外では cron または オーケストレーターを使います。--s3 を指定した restore --check は、まずアーカイブをデータボリュームにダウンロードし、 その後コピーを削除します。

この手順のコピーは、ここにあるものだけです。 Wiki とドキュメントサイトにはこの手順のコマンドはなく、 ここを参照しています。restore は稼働中のデータベースを上書きするため、コマンドを変更する場合は ここだけで変更します。test/readme-mirrors.test.ts は、ミラーを明示する(MIRRORED-IN / MIRRORED-FROM マーカーを持つ) コピーが追加されると失敗します。他のリポジトリにあるマーカーのないコピーは検出できず、それを見つけるのはレビューの役割です。

アーカイブにはデータベース、すべてのアップロードソースの実体化されたファイル(これらは他の場所には存在しません)、 およびアーカイブの最初のエントリーとなるマニフェストが含まれます。そのため、--check では数ギガバイトに及ぶ可能性のあるファイルも、 小さな読み取りを 1 回行うだけで済みます。

SECRET_KEY は含まれておらず、今後も含まれません。 キーを含むバックアップは、アクセス管理が元の環境よりも 弱いボリューム上に、インスタンス全体を 1 つのファイルとして置くことになるためです。持ち運ばれるのはキーのフィンガープリント (128 ビットの鍵付き HMAC)です。これにより、キーが実質的に必要となる場合は、restore が何も書き込む前に 誤ったキーまたはキーの欠落を理由に処理を拒否できます。ただし、ソースにそのキーで暗号化された同期認証情報を持たないアーカイブは、 明らかに安全な唯一のケースを停止するのではなく、メモを表示して復元を続行します。同期認証情報を持つソースは git、notion、confluence、s3、gdrive、github のみであるため、これらのシークレットだけが対象です。 マニフェストに対象の型が記録される前に取得されたアーカイブは、復元先に触れる前に、アーカイブ内のダンプの行を基準に判定されます。 以前のリリースで local、upload、web ソースに保存されたシークレットは復元を拒否する理由にはなりません。復元後は、 一度も読み取られなかったそれらのシークレットは空に設定され、その件数が表示されます。キーはアーカイブとは別の場所に保管してください。 このキーはローテーション可能です。4 ステップのランブック(npm run rotate-secret はステップ 3)に従います。これにより、上の段落にはもう一つの注意点が加わります。古いキーを廃止して SECRET_KEY_PREVIOUS を削除し、その後キーを破棄すると、ソースの同期認証情報(プライベート Git、Notion、Confluence、GitHub のトークン、S3 のキーペア、Google サービスアカウントキー)をそのキーで暗号化して保持している、ローテーション前に取得したすべてのアーカイブが無効になります。 古いアーカイブのフィンガープリントが示すキーは、環境にはもうありません。そのアーカイブについては、認証情報の暗号文が廃止・破棄されたキーで書き込まれていて再導出できないため、restore は拒否します。そのような認証情報を含まないアーカイブは復元できます。また、拒否が発生するのは 4 ステップ中のステップ 2 であり、ステップ 4 ではありません。廃止したキーを破棄せずに保持しておけば、そのアーカイブは保持期限が切れるまで復元可能です。ローテーションは、次の 3 ステップをこの順序で行い、バックアップで挟みます。ローテーション前に、現在のキーで通常のバックアップを取ります。npm run rotate-secret がすべての行の変換完了を報告した後、SECRET_KEY_PREVIOUS を削除する前に、新しいキーで新しいアーカイブを取得します。これは、ローテーション後のインスタンスが復元できる最も古いアーカイブです。廃止したキーは、ローテーション前の最も古いアーカイブの保持期限が切れるまで、ラベルを付けてアーカイブとは別の場所にあるシークレットストアに保管し、その後でのみ破棄します。各ステップのコマンドを含む完全なランブックは、セキュリティ → ローテーションを参照してください。

復元では、復元先の PostgreSQL サーバーより新しいメジャーバージョンで取得されたダンプも拒否されます。逆方向の 16 から 17 への移行は、文書化されているアップグレードであり、このコマンドが存在する理由でもあります。

データベースだけが必要な場合に手動で実行するコマンド — テストスイートが実行するのは、次の 2 つのコマンドです。

docker exec -it contextator psql -U contextator
docker exec contextator pg_dump -U contextator -Fc contextator > contextator.dump
docker exec -i contextator pg_restore -U contextator -d contextator --clean --if-exists < contextator.dump

test/integration/backup-restore.itest.ts は実際の PostgreSQL にデータを投入してダンプし、データベースを削除してから復元し、スキーマ、検索結果(行、順序、スコアがすべて同一)、およびアプリケーションの次回起動がすべて変更なく戻ることをアサートします。行数だけでは、ベクトルインデックスや字句列が失われた復元を検出できないため、アサーションには含まれていません。同じファイルでは、アップロードソースと暗号化された認証情報を使って npm run backup と npm run restore による一連の往復処理も行い、間違ったキーを使うとインスタンスが変更されないことをアサートします。

ダンプに含まれないものが 2 つあります。これが上記のコマンドが存在する理由そのものです。

  • SECRET_KEY。 これは環境変数です。元のキーがなければ、保存されているすべてのソース認証情報を再入力する必要があります。ダンプとは別の場所に保管してください。ダンプとキーを合わせたものがインスタンス全体です。また、ローテーション時に廃止するキーも保管してください。そうしないと、ローテーション前のダンプは復元できなくなります。
  • /data。 Git チェックアウトは再クローンでき、Notion のデータは再取得できますが、アップロードソースの current/ ディレクトリは、そのコンテンツが存在する唯一のコピーです。再クローン可能なギガバイト分を省き、アップロードソースだけに限定して /data をバックアップできます。npm run backup はまさにその処理を行います。

indexing 状態のプロジェクトがないときにバックアップしてください。再インデックスでは稼働中の世代と並行して第 2 世代を書き込み、pg_dump は行をフィルタリングできないため、実行中に取得したダンプはサイズが 2 倍になります。インデックス済みチャンクあたり約 2 KB のダンプサイズを見込んでください。ベクトルはテキストとしてダンプされ、圧縮後は元のサイズに戻ります。また、復元にかかる時間の大半は HNSW インデックスの再構築に費やされると見込んでください。プロジェクトごとに 1 つあり、それぞれがインスタンス内のすべてのチャンクをスキャンして自分のプロジェクトの行を探すため、復元にかかる時間はインスタンスの規模とプロジェクト数の両方で決まります。 バックアップの完了まで、プロジェクトの作成と削除は待機します。 pg_dump はデータベースを1つのスナップショットで読み取り、プロジェクトを作成するときは CREATE INDEX CONCURRENTLY で HNSW インデックスを構築し、削除するときは DROP INDEX CONCURRENTLY でインデックスを削除します。どちらも、開始時に開いているすべてのトランザクションが終了するまで待機します。バックアップ中にプロジェクトを作成(POST /api/projects またはダッシュボードから)、削除、インポートすると、バックアップが終わってから処理が完了します。エラーは発生せず、既存のプロジェクトの検索やインデックス作成は待機しません。プロジェクトを作成する時間帯を避けてバックアップをスケジュールしてください。

POSTGRES_PASSWORD はクラスターの作成時に適用されます。後から変更するには、psql で ALTER USER contextator PASSWORD '...' を実行し、次回の起動前に .env を更新してください。

アップグレード

通常のイメージアップグレードでは docker compose pull を実行します。これによって更新されるのはアプリケーションであり、クラスターではありません。手順が必要なアップグレードは、PostgreSQL のメジャーバージョン変更だけです。

PostgreSQL のメジャーバージョンをまたぐアップグレード

通常のイメージアップグレードで、この変更が行われることはありません。 docker compose pull で更新されるのはアプリケーションです。クラスターはボリュームであり、そのクラスターを読み取れるのはイメージに含まれる PostgreSQL のメジャーバージョンだからです。リリースが PostgreSQL 16 から 17 に移行する日が、この手順を適用する日です。適用するのは pull の後ではなく、前です。

そのまま pull すると、次のことが起こります。

PostgreSQL Database directory appears to contain a database; Skipping initialization
FATAL:  database files are incompatible with server
DETAIL:  The data directory was initialized by PostgreSQL version 16, which is not compatible with
         this version 17.11 (Debian 17.11-1.pgdg12+2).
[contextator] PostgreSQL exited during startup (exit code 1)

コンテナは終了コード 1 で終了し、restart: unless-stopped によって再起動されますが、再び終了コード 1 で終了します。データが損傷することはありません。古いクラスターには手が加えられず、古いイメージであれば引き続き読み取れます。

今まさにこの状態なら、抜け出す方法は1行です。 CONTEXTATOR_TAG を使用していたメジャーバージョンに戻し、docker compose up -d を実行すれば、元の状態に戻ります。その後、稼働中の古いイメージで、以下のアップグレードを順番どおりに行ってください。後述のロールバック用ブロックから始めてはいけません。あちらはクラスターの 退避済みコピー を復元するもので、誤って pull しただけの場合はまだコピーが作られていないため、復元元がありません。

pg_upgrade は使いません。その理由を知っておくとよいでしょう。pg_upgrade には2つのメジャーバージョンのバイナリを同時に置く必要がありますが、このイメージに含まれるのは1つだけです。そのため、論理バックアップを取得して復元する方法を使います。つまり、上記と同じ npm run backup と npm run restore を、バージョン変更をまたいで実行します。

まず、実際のクラスターのボリューム名を確認してください。 手順3では名前を指定してボリュームを削除します。また、docker run -v <name>:/from は、指定した名前のボリュームが存在しなくてもエラーを出さずに空のボリュームを作成します。docker-compose.yml では ${CONTEXTATOR_PGDATA_VOLUME:-contextator-pgdata} という名前が指定されています。そのため、この変数を設定したインスタンスでは名前が異なります。また、CONTEXTATOR_PGDATA_PATH を設定した場合はボリュームではなくホストディレクトリが使われるため、ホスト上で cp -a を使ってコピーし、移動してください。

手順3は、警告を挟んだリストではなく、1つの && チェーンになっています。これが保護のすべてです。docker volume inspect は存在しない名前を指定すると失敗し、cp -a はディスクの空き容量が足りないと失敗し、PG_VERSION の比較はコピーが不完全だと失敗します。いずれの場合も、docker volume rm を実行する前にチェーンが停止します。削除処理は意図的に最後のリンクに置いてあるため、「停止した」と「何も削除されていない」は同じ意味です。実際にはブロック全体をまとめて貼り付けて実行するため、ガードは周囲の説明文ではなくシェル内に置いてあります。 STOPPED:に実際に何が書かれているかを確認してください。2つのブロックで内容が異なるのは意図的です。 それぞれはチェーンの||であるため、いずれかのリンクが失敗すると出力されます。その時点で何が事実かは、削除のどちら側でチェーンが停止したかによって異なります。ステップ3では削除が最後のリンクなので、停止した場合もボリュームは残っています。ロールバックでは削除を最後にできません。ボリュームは再びデータを格納する前に空にする必要があるため、空にする処理とデータを戻す処理は1つのコンテナーコマンドで行います。そしてメッセージには、そのチェーンが失敗したときに常に当てはまる唯一のこと、つまり退避したコピーは読み取られただけであることを記載しています。どちらの場合も「何も削除されていません」というメッセージにすると、片方では誤りになります。

クラスターをホストディレクトリ(CONTEXTATOR_PGDATA_PATH)に保持するインスタンスでは、これらの処理にボリュームは関与しません。ホスト上でcp -aを使ってそのディレクトリを別の場所にコピーし、コピーを確認してから、ボリュームを削除する代わりに元のディレクトリを空にしてください。

PGVOL=${CONTEXTATOR_PGDATA_VOLUME:-contextator-pgdata}   # from your .env; the default is shown
OLD=16                                                   # the major you are leaving
# 1. On the OLD image, still running. Take it when no project is indexing.
#    No mkdir here: `backup` creates the directory it is given. Step 5 needs one, because
#    `docker cp` does not.
docker exec contextator npm run backup -- /data/backups/pre-pg17.tar.gz
docker cp contextator:/data/backups/pre-pg17.tar.gz .     # off this host, not beside the volume

# 2. Stop the old container. KEEP its cluster — it is the rollback.
docker compose down

# 3. Copy the old cluster aside, CHECK THE COPY, and only then remove the original.
#    Step 4 recreates the volume — Compose creates a named volume it does not find — so there is
#    no `docker volume create "$PGVOL"` here to be the link after the deletion.
docker volume inspect "$PGVOL" >/dev/null &&
  docker volume create "$PGVOL-pg$OLD" &&
  docker run --rm -v "$PGVOL":/from -v "$PGVOL-pg$OLD":/to alpine sh -c 'cp -a /from/. /to/' &&
  [ "$(docker run --rm -v "$PGVOL-pg$OLD":/to alpine cat /to/PG_VERSION 2>/dev/null)" = "$OLD" ] &&
  docker volume rm "$PGVOL" ||
  printf '%s\n' \
    "STOPPED: see the error above." \
    "$PGVOL has NOT been removed: the removal is the last link of that chain, so it was either" \
    "never reached or refused by Docker. The parked copy $PGVOL-pg$OLD may or may not exist and" \
    "may or may not be complete — nothing depends on it yet." \
    "Fix the error and run this block again; it is safe to repeat."

# 4. Pull the new image and start it. It initdb's the empty volume and creates an empty schema.
docker compose pull && docker compose up -d
docker exec contextator psql -U contextator -c 'select version()'

# 5. Put the instance back.
docker exec contextator sh -c 'mkdir -p /data/backups'
docker cp pre-pg17.tar.gz contextator:/data/backups/
docker exec contextator npm run restore -- /data/backups/pre-pg17.tar.gz --check
docker exec contextator npm run restore -- /data/backups/pre-pg17.tar.gz
docker compose restart contextator

ロールバックはステップ3を逆順に行うもので、名前変更ではなくコピーです — Dockerにはvolume renameがありません。CONTEXTATOR_TAGを元のバージョンに戻してから、次を実行してください。

# Re-stated here on purpose: a rollback is run later, and often in a shell that never saw step 1.
PGVOL=${CONTEXTATOR_PGDATA_VOLUME:-contextator-pgdata}
OLD=16

docker compose down
# The emptying and the refilling are one container command, so no link here deletes something the
# next link then fails to replace. `$PGVOL-pg$OLD` is only ever read.
docker volume inspect "$PGVOL-pg$OLD" >/dev/null &&
  docker run --rm -v "$PGVOL-pg$OLD":/from -v "$PGVOL":/to alpine \
    sh -c 'find /to -mindepth 1 -delete && cp -a /from/. /to/' &&
  docker compose up -d ||
  printf '%s\n' \
    "STOPPED: see the error above." \
    "Nothing in this block writes to $PGVOL-pg$OLD — it is only read — so the cluster you are" \
    "rolling back to is whatever it was before you started." \
    "$PGVOL may be untouched, emptied, or half-written depending on where this stopped, so do NOT" \
    "start the old image against it yet. Fix the error and run this block again: it empties and" \
    "refills $PGVOL from scratch, so repeating it is safe."

これにより、新しいメジャーバージョンのデータはすべて破棄されます。ステップ5の後にその中にあるのは復元されたインスタンスなので、これは実行時点ではなく、ステップ1の時点へのロールバックです。その間にインデックス化されたものは再インデックス化されます。

開始前に知っておくべきことが3つあります。

  • 移行先でもSECRET_KEYは同じでなければなりません。 アーカイブには含まれておらず、存在しないか異なっている場合、復元は書き込みを行う前に停止します。.envを手元に用意してください。また、このアーカイブを作成してからSECRET_KEYをローテーションしている場合、現在の.envにあるキーは誤ったキーです。ローテーション前のアーカイブは、その作成時に使われたキーでしか復元できないため、ここでは使用済みのキーを使うか、開始前に新しいバックアップを取ってください。
  • 復元ではすべてのインデックスが再構築され、所要時間の大半を占めます。 インスタンス全体のチャンク数を基準にメンテナンス時間を見積もってください。
  • 復元では逆方向への復元を拒否します。 17から取得したダンプを16のサーバーに復元することはできません。適用の途中で停止するのではなく、書き込み前にその旨を示します。つまり、ロールバックの対象はダンプではなくボリュームです。

この手順を記載しているのは、このセクションだけです。 backupとrestoreを実装するコードの隣に、コードと同じバージョンでここに記載しています。クラスターを削除する手順は、その内容を決める場所を厳密に1つにする必要があるためです。Wikiとドキュメントサイトはここを参照し、コマンドを重複して掲載していません。

ドキュメント

プロジェクトのステータス

1.0.0から安定版です。 公開されている契約、つまりMCP APIと管理API、環境変数、CLI、マイグレーション、バックアップアーカイブは、メジャーバージョンでのみ変更されます。対象範囲、非推奨化の進め方、明示されている例外、現在非推奨となっている項目については、docs/compatibility.mdに記載しています。変更内容はCHANGELOG.mdに記載しています。

貢献

貢献を歓迎します。このプロジェクトの作業方法は、すべてCONTRIBUTING.mdに記載されています。環境の起動方法、変更が通過する必要のある4つのチェック、1つの説明を2つのファイルに記載している5か所、そしてコミット記録について説明しています。また、外部からの貢献に求められるライセンスの許諾(CLA.md、プルリクエスト上で署名)についても説明しています。これは、以下の商用ライセンスを適用するには、著作権がすべて保持されている必要があるためです。CODE_OF_CONDUCT.mdはContributor Covenantであり、このプロジェクトにも適用されます。

セキュリティ

セキュリティ上の問題を見つけましたか?イシューを作成しないでください。 SECURITY.mdに報告先が記載されており、脆弱性のように見えても文書化された動作である事項、つまりopenのままになっているプロジェクトや、MCPトークンで許可されることと許可されないことも列挙されています。デフォルトで保証される内容については、セキュリティに関する注意事項をご覧ください。

ライセンス

GNU Affero General Public License、バージョン3以降(AGPL-3.0-or-later)の下でライセンスされています。Contextatorを改変し、ネットワーク経由で改変版にアクセスできるようにする場合、そのユーザーに完全なソースコードを提供する必要があります。未改変のソフトウェアを実行するだけなら、そうした義務は発生しません。著作権はMuhammet Şafakがすべて保有しているため、AGPLが適さない場合は別途商用ライセンスを付与できます。tunedness.comまでお問い合わせください。詳しい説明はdocs/license.mdをご覧ください。

コメント

MCP は初めてですか?

プロトコルの概要から最初のサーバーの導入まで、よくある疑問にまとめて答えます。

AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。