Notes Vault について
Notes Vault は、ローカルディレクトリまたは S3 に保存した Markdown ノート用の MCP サーバーです。ローカルインデックス、スキーマ検証付きの書き込み、Claude Code のセッションフックにより、エージェントのノート検索・管理とコードとの差異の検出を支援します。
主な機能
- 01ローカル SQLite インデックスと BM25 検索
- 02スキーマ検証付きノート書き込み
- 03ノートのアーカイブとリポジトリログ追記
- 04ノートの状態とリンクの lint 検査
- 05Claude Code のセッションコンテキストフック
活用シーン
- 01既存ノートや過去の決定事項の検索
- 02ルールに沿った Markdown ノートの作成・更新
- 03セッション開始時の関連システムノート確認
- 04古いノートや未記録コミットの検出
MCP 対応
MCP サーバーとして、ノートの検索、読み取り、書き込み、整理、検査用のツールをクライアントに提供します。保管先にはローカルディレクトリまたは S3 を使用できます。
設定
ローカルサーバーの設定はどのクライアントでも共通です。コピーしてクライアントの MCP 設定ファイルに貼り付けてください。
uvx notes-vault-mcpVAULT_PATHDirectory holding the vault. Set this or the four S3_* variables.S3_ENDPOINTS3 or MinIO endpoint URL holding the vaultS3_ACCESS_KEYシークレットAccess key for the vault bucketS3_SECRET_KEYシークレットSecret key for the vault bucketS3_BUCKETBucket holding the vaultS3_PREFIXKey prefix inside the bucketS3_REGIONRegion for the vault bucketVAULT_CACHE_DIRWhere the SQLite index livesVAULT_SCHEMALocal schema file overriding the one in the vaultVAULT_TOKENシークレットBearer token required by the streamable HTTP transportVAULT_STOP_HOOKSet to off to disable the stop hookローカルサーバーにはマシン上の実行環境(Node.js 18 以上など)が必要です。設定ファイルの場所はクライアントごとに異なります。
ツール
ローカルサーバーのツールはマシン上で起動して初めて取得できます。メンテナーはまだツール一覧を提供していません。
詳細
notes-vault-mcp
Markdown ノートの保管庫向け MCP サーバーです。Obsidian が扱うような、YAML フロントマターを含む .md ファイルのフォルダーを想定しています。保管庫はローカルディレクトリまたは S3 バケット(MinIO を含む)に配置でき、サーバーはエージェントに低コストでインデックス化された読み書き手段を提供します。
目的は、エージェントが「これについて、すでに何がわかっているか」に一度の呼び出しで答えられるようにし、保管庫の内容がコードと乖離したときに知らせることです。そのため、このサーバーはファイルの読み書きだけでなく、次の機能も提供します。
- ローカルの SQLite インデックス。 すべてのツール呼び出しでインデックスを更新し、バージョンが変わったノートだけを取得します。検索時に保管庫をダウンロードすることはありません。
- BM25 ランキングによる全文検索。 フォルダーの重み、時間経過による減衰、ステータス係数を適用するため、同じ語句で検索した場合、現在有効なシステムノートが 2 年前のアーカイブ済みプランより上位に表示されます。
- スキーマ。 フロントマターのルールは、保管庫内の
.vault/schema.ymlに記述されています。存在するフォルダーとその用途、必須フィールド、使用可能なステータスと種類、areaへのリンクが必要なフォルダーを定義します。書き込みはスキーマに照らして検証され、条件を満たさない場合は拒否されます。 - ライフサイクル。
closeは完了したノートをアーカイブし、ステータスを設定します。log_appendはリポジトリごと、セッションごとに日付入りの行を 1 行追加します。lintは検出可能なすべての乖離を報告します。 - Claude Code 用のセッションフック。
session-startは、これから操作するリポジトリのシステムノートと、各ノートの最終更新以降に行われたコミットをエージェントに渡します。stopは、コミットが未記録のまま、またはノートが古いままセッションを終了しようとすると、それを拒否します。
スウェーデン語と英語のどちらのノートも扱えます。インデックスは発音区別符号を正規化し、保管庫内の各言語のすべての単語を語幹化するため、äppelpressar で äppelpressen が見つかり、pressing で presses が見つかります。また、スキーマには同義語リストが含まれているため、bokning で booking が見つかります。
インストール
Claude Code プラグインとして
claude plugin marketplace add https://github.com/gronare/claude-plugins
claude plugin install vault@gronare
プラグインは保管庫の設定を尋ね、それらを CLAUDE_PLUGIN_OPTION_* 環境変数として渡します。このサーバーは、それらを通常の環境変数名と同じように読み取ります。
MCP サーバーとして PyPI から直接インストール
claude mcp add vault -s user \
-e VAULT_PATH=$HOME/vault \
-- uvx notes-vault-mcp
または S3/MinIO を使用する場合:
claude mcp add vault -s user \
-e S3_ENDPOINT=https://minio.example.com \
-e S3_ACCESS_KEY=... \
-e S3_SECRET_KEY=... \
-e S3_BUCKET=vault \
-- uvx notes-vault-mcp
コンテナとして
claude mcp add vault -s user -- \
docker run --rm -i \
-e S3_ENDPOINT -e S3_ACCESS_KEY -e S3_SECRET_KEY -e S3_BUCKET \
ghcr.io/gronare/notes-vault-mcp:latest
設定
すべての変数は CLAUDE_PLUGIN_OPTION_<NAME> からも読み取られます。Claude Code プラグインはこの方法でユーザー設定を渡します。両方が設定されている場合は、通常の環境変数名の値が優先されます。
| 変数 | 必須 | 説明 |
|---|---|---|
VAULT_PATH | ローカル保管庫の場合 | 保管庫が格納されているディレクトリです。ローカルバックエンドを選択します。 |
S3_ENDPOINT | S3 保管庫の場合 | エンドポイント URL です。例:https://minio.example.com。 |
S3_ACCESS_KEY | S3 保管庫の場合 | アクセスキーです。 |
S3_SECRET_KEY | S3 保管庫の場合 | シークレットキーです。 |
S3_BUCKET | S3 保管庫の場合 | 保管庫を格納するバケットです。 |
S3_PREFIX | いいえ | バケット内のキーのプレフィックスです。 |
S3_REGION | いいえ | リージョンです。デフォルトは us-east-1 です。 |
VAULT_CACHE_DIR | いいえ | インデックスの保存場所です。デフォルトは ~/.cache/notes-vault-mcp です。 |
VAULT_SCHEMA | いいえ | スキーマファイルのローカルパスです。保管庫内のファイルより優先されます。 |
VAULT_TOKEN | --auth bearer の場合 | --transport http の LAN モードで使用する静的なベアラートークンです。 |
VAULT_ALLOWED_HOSTS | いいえ | サーバーが応答する追加の Host ヘッダー値です。スペースまたはカンマで区切ります。 |
VAULT_STOP_HOOK | いいえ | off にすると stop フックが無効になります。 |
VAULT_PATH または 4 つの S3_* 変数を設定してください。どちらも設定されていない場合、サーバーはその旨を 1 行表示して終了します。
初回実行
uvx notes-vault-mcp init
init は保管庫に書き込みを行い、--force を指定しない限り、既存の内容を上書きしません。
.vault/schema.yml— フロントマターのルールです。編集できるよう、組み込みのデフォルトからコピーされます。Areas.base、Open tasks.base、Resources.base、Backlog.base、Log.base— 同じ構造を表示する Obsidian Bases ビューです。
その後、エージェントが会話内で必要とするワークフロールールを記述した CLAUDE.md のスニペットを stdout に出力します。
スキーマ
.vault/schema.yml は組み込みのデフォルトにディープマージされるため、異なる部分だけを指定すれば十分です。
デフォルトでは、次の5つのフォルダーが定義されています。
| フォルダー | 種別 | 重み | 役割 |
|---|---|---|---|
Areas/ | system | 3.0 | 各システムにつき1つの、最新状態を記したノートです。グラフのハブです。 |
Resources/ | reference | 2.0 | 落とし穴、手順、理由を伴う決定事項です。 |
Projects/ | task | 1.0 | 複数のセッションにまたがる進行中の作業です。close で終了します。 |
Log/ | log | 1.0 | リポジトリごとに1つの、追記専用ログです。 |
Archive/ | archive | 0.3 | 履歴です。依頼があった場合にのみ検索されます。 |
また、ノートの契約は次のとおりです。
frontmatter:
required: [title, date, updated, tags, status]
optional: [kind, area, summary, path, superseded_by, priority, source, kelpie]
area_required_in: [Projects, Resources, Log]
status_values: [draft, active, complete, superseded]
kind_values: [system, task, trap, howto, decision, reference, log]
各種別には、その種別に該当するノートの条件を示す1行のルールがあります。サーバーはそのルールを指示に含めるため、どのクライアントも同じ基準で種別を選択できます。
kinds:
trap: "a gotcha that bit once and will bite again; nothing to do, nothing was chosen"
decision: "a choice and its reasons; something was chosen, alternatives were dropped"
howto: "steps that are known to work; nothing chosen, nothing open"
Vault で種別を追加するにはエントリー(research: "reading notes on a topic; no task, no decision")を追加し、ルールを書き換えるには別の表現で記述し、種別を削除するには null を設定します。ルールがなくても、kind_values だけで引き続き動作します。
path はノートとコードを結び付ける項目です。ディレクトリをカンマ区切りで指定します(~ は記述どおりに保持され、展開後のパスでもインデックス化されます)。context とセッションフックはこれを照合します。
リポジトリのログは、リポジトリごとに1つのノートです。ファイル名と行形式のどちらもスキーマ設定で指定します。
log:
folder: Log
file_format: "{repo}-log.md"
entry_format: "- [{date}] {line} | commits: {commits} | {area}"
file_format には {repo} プレースホルダーを1つ指定します。デフォルトのサフィックスは、ログがハブノートと混同されないようにするためのものです。Vault に Areas/myapp.md と Log/myapp.md の両方があると、Obsidian は [[myapp]] を解決できません。ログパスを構築するすべての箇所(log_append、context のログ末尾、stop フックの未記録コミットチェック、changelog、lint)でこの設定が参照されるため、変更するとすべてが一度に切り替わります。変更時は、既存のファイル名も一致するように変更してください。
language は Vault の主言語、other_languages はその他の言語を ISO コードで指定します(language: sv、other_languages: [en])。検索では Snowball が対応するすべての言語で語幹検索が行われ、これらを変更するとインデックスが再構築されます。一致する結果がない場合、Vault の記述言語をクライアントに伝える返信が返されるため、クライアントは語句を翻訳して再検索できます。部分的な結果の場合も、同じ情報が1行で示されます。語幹一致のみの結果には stemmed_weight(デフォルトは0.5で、本文の1.0より低い)が適用されるため、完全一致が上位に表示されます。
ほかに設定できる項目として、タグの語彙とその強制の有無、検索で展開する同義語グループ、stale_after_days、検索の重みがあります。
ツール
呼び出すたびにインデックスが更新されます。ただし、更新は最大20秒に1回までに制限されます。
| ツール | コスト | 機能 |
|---|---|---|
search | 低 | インデックスを全文検索します。ファイル名、タイトル、概要、タグ、本文を対象に、同義語、接頭辞、語幹、引用句、アクセント記号の折りたたみに対応します。クエリがノートのファイル名と完全に一致する場合(または [[stem]] の場合)、そのノートがアーカイブ内にあっても最初に表示されます。すべての用語が必須です。すべての用語に一致するノートがない場合、最も多くの用語に一致するノートが返され、ヘッダーに結果が部分的であることが示されます。コミット SHA のみを指定すると、それに言及しているノートが見つかります。アーカイブ済みおよび置き換え済みのノートは非表示にし、その件数を示します。 |
context | 低 | セッション開始時に呼び出すツールです。パスを対象とするシステムノート、未完了のタスク、stale_after_days より古い未完了ノートとそれぞれを解決する3つの方法をまとめたトリアージリスト、バックログ、参考ノート、リポジトリログの末尾を、1つの回答にまとめます。 |
list_files | 低 | パスのみを返します。 |
read_file | 中 | 1つのノートを、etag: <version> を付けて返します。置き換え済みのノートには警告が表示されます。 |
obsidian_access | 書き込み | --auth forwarded のみです。Obsidian の Remotely Save プラグインが WebDAV 経由で使用する個人用トークンを発行し、一度だけ表示します。新しいトークンを発行すると、以前のトークンは無効になります。 |
lint | 中 | すべてのノートを読み込み、逸脱を報告します。 |
init | 書き込み | Vault をセットアップします。.vault/schema.yml と Obsidian Bases のビューをルートに書き込み、既存の内容は保持します。force を指定すると上書きします。プラグインの /vault:init がこれを呼び出します。 |
write_file | 書き込み | スキーマに照らして検証し、条件を満たさない場合は書き込みを拒否します。updated に現在時刻を記録し、date を補完します。条件付きで書き込むには expected_etag を指定します。まだ存在してはならないノートを新規作成する場合は "*" を指定します。S3 では条件を PUT 自体に付けます(If-Match、If-None-Match: *)。条件付き PUT を拒否するサーバーでは、代わりに PUT の前に HEAD を実行し、そのことをサーバーログに一度だけ記録します。書き込まれるのはドットで始まるフォルダー外の .md ノートのみです。スキーマと .base ビューは init によって作成され、引き続き read_file で読み取れます。 |
append_file | 書き込み | 追記して updated を更新します。ノートが存在し、引き続きスキーマを満たしている必要があります。 |
set_status | 書き込み | ノートを書き換えずにステータスを変更します。優先度を指定して backlog にすると保留になり、active にすると再開します。 |
close | 書き込み | ステータスを完了に設定してノートをアーカイブに移動します(merged_into を指定した場合は、superseded_by を設定して置き換え済みにします)。 |
log_append | 書き込み | リポジトリログに日付付きの1行を追加し、生成されたコミットも記録します。ログがない場合は作成します。 |
move_file | 書き込み | ノートを移動または名前変更します。移動先でスキーマを満たす必要があります。 |
delete_file | 書き込み | 完全に削除します。close を推奨します。 |
search のフィルター:folder、status、tag、kind、area、path_prefix、since、
include_archive、include_superseded、limit。
lint の報告内容
broken_frontmatter、missing_required(フィールドごと)、missing_area、unknown_tags(語彙が厳密な場合のみ)、unresolved_links、orphans(被リンクの wikilink がないもの。ログとアーカイブは無視)、stale_active、archive_status_mismatch、duplicate_stems、superseded_target_missing。--park-stale を指定すると、stale_after_days を超えて更新されていない未完了タスクのノートは、レポートの前に backlog(優先度 low、source veckolint <date>)に設定され、parked の下に一覧表示されます。そのため、active は常に期間内に更新されたものを意味します。再開するには set_status(path, "active") を実行します。過去14日以内に作成または更新された claim(kelpie キー)を持つノートは、since とノートの updated のうち後の日付を基準に判定し、現在の状態のままにします。
uvx notes-vault-mcp lint
uvx notes-vault-mcp lint --write "Log/lint-$(date +%F).md"
uvx notes-vault-mcp lint --write Log/lint.md --park-stale
フック
Claude Code のフックが2つあり、どちらも標準入力からフックの JSON を読み取り、何が起きても終了コード 0 で終了します。
session-start は作業ディレクトリのコンテキスト一式を出力し、次に、返された各システムノートについて、そのノートが最後に更新されてから path に変更を加えたコミットを出力します。これは、エージェントがノートを信じる前に「このノートはまだ正しいか?」を確認するためのものです。
stop は、記録されていない作業を残したセッションの終了をブロックします。対象は、直近24時間のコミットのうち sha がリポジトリのログに存在しないものと、セッションのトランスクリプト内の vault ツール呼び出しから特定した、セッションが読み書きしたスキーマの stale_after_days より古い未完了のタスクノートです。{"decision": "block", "reason": ...} を返すか、vault が最新であれば何も返しません。セッションが触れていない古いノートへの対応は次のセッションに委ねられます。context では、それぞれを解決する3つの方法(クローズする、日付入りの行を追加する、バックログに保留する)とともに triage の下に一覧表示し、毎回の停止時には表示しません。エージェントのセッションを実行し、リポジトリのログも自ら書き込むツールの場合、起動するセッションの設定の env で VAULT_STOP_HOOK=off を設定すると、このフックを無効にできます。
{
"hooks": {
"SessionStart": [
{ "hooks": [{ "type": "command", "command": "uvx notes-vault-mcp hook session-start" }] }
],
"Stop": [
{ "hooks": [{ "type": "command", "command": "uvx notes-vault-mcp hook stop" }] }
]
}
}
その他のコマンド
notes-vault-mcp serve --transport stdio # the default
notes-vault-mcp sync --rebuild # drop the index and read every note again
notes-vault-mcp search "bokning" --limit 5 # the same ranking, from a shell
notes-vault-mcp changelog myapp 2026-08 --repo-path ~/projects/myapp
notes-vault-mcp changelog myapp 2026-08 --repo-path ~/projects/myapp --write
notes-vault-mcp changelog --all # this month (and last month during its first week)
changelog は、ログの行、日ごとにまとめた git コミット、期間内の日付が付いたリポジトリのノートを出力します。--write を指定すると、それらを期間ページ Log/<repo>-<period>.md として保存し、と のマーカーで囲みます。マーカーより上の文章(エージェントが月末に作成した要約)はそのまま残され、ページのステータスはカレンダーに従います。--all を指定すると、このマシンで session-start フックが確認したすべてのリポジトリに対して実行します。また、stop フックが1日1回実行するため、cron を使わなくてもページは最新の状態に保たれます。
バックログ
notes-vault-mcp backlog --area myapp --priority high
バックログ項目は、status: backlog、area、1行の summary、任意の priority(urgent、high、medium、low)、任意の source(誰がいつ言ったか、または sha)を持つタスクノートです。backlog_add は、何かを先送りにした時点で会話から項目を記録します。backlog は、優先度、次に古さの順で一覧表示し、area または family で絞り込めます。context は、未完了のタスクとは別に、現在のリポジトリに関連する項目を表示します。項目に着手するにはステータスを active に設定し、完了するには close を実行します。lint は古さにかかわらずバックログノートをそのままにし、クローズされていない complete ノートにはフラグを立てます。init は、他の Obsidian ベースと同じ場所に Backlog.base を書き込みます。
タスクとログのコマンド
コーディングエージェントを操作し、MCP ツールを呼び出すようエージェントに頼る代わりに、固定のタイミング(開始、プッシュ、完了、リセット)で事実を自動的に記録するツール向けです。各コマンドは標準出力に 1 つの JSON オブジェクトを出力して終了コード 0 で終了し、失敗した場合は標準エラー出力に 1 行を出力して、0 以外の終了コードで終了します。すべての書き込みは MCP ツールと同じ経路を通り、スキーマ検証、updated スタンプ、インデックスの更新が行われます。
notes-vault-mcp task open --name "Fix login" --cwd ~/wt/wt1.myapp [--branch fix-login]
# {"path": "Projects/myapp-fix-login.md", "created": true, "repo": "myapp", "area": "myapp"}
notes-vault-mcp task open --note Projects/myapp-idea.md --cwd ~/wt/wt1.myapp
# {"path": "Projects/myapp-idea.md", "created": false, "repo": "myapp", "area": "myapp"}
notes-vault-mcp task check Projects/myapp-fix-login.md --need problem,solution,decisions
# {"missing": ["solution", "decisions"]}
notes-vault-mcp task close Projects/myapp-fix-login.md # {"path": "Archive/myapp-fix-login.md"}
notes-vault-mcp task backlog Projects/myapp-fix-login.md --priority medium # {"path": "Projects/…"}
notes-vault-mcp task claim Projects/myapp-idea.md --machine mac --task fix-login --repo myapp [--force]
# {"path": "Projects/myapp-idea.md", "claimed": true, "claim": {"machine": "mac", "task": "fix-login", "repo": "myapp", "since": "2026-10-23T09:12:00Z"}, "replaced": null}
notes-vault-mcp task release Projects/myapp-idea.md [--machine mac]
# {"path": "Projects/myapp-idea.md", "released": true, "claim": null}
notes-vault-mcp task release --machine mac --task fix-login
# {"paths": ["Projects/myapp-renamed.md"], "released": true}
notes-vault-mcp log append --cwd ~/wt/wt1.myapp --line "Login fixed" --commit abc1234 --commit def5678
# {"path": "Log/myapp-log.md"}
notes-vault-mcp log path --repo myapp # {"path": "Log/myapp-log.md"}
notes-vault-mcp status
# {"configured": true, "version": "0.5.1", "backend": "s3", "writable": null}
notes-vault-mcp status --probe
# {"configured": true, "version": "0.5.1", "backend": "s3", "writable": true}
repo はメインチェックアウトの名前であり、ワークツリーからも取得されます。area は、そのディレクトリでセッション開始フックが読み込むシステムノートです。一致するノートがない場合は repo 名にフォールバックし、出力では "area": null になります。task open はタスク名を仮のタイトルにしたタスクノートを作成します。ステータスは active、path はメインチェックアウトに設定され、source: branch <name> と、本文が空のセクション見出しが含まれます。ファイル名のベースは repo とタスク名を組み合わせたもので、ボルト全体で一意になります。--note を指定すると、既存のタスクノートを代わりに確保します。この場合、ステータスは active になり、それ以外は変更されません。--branch の既定値は --cwd でチェックアウトされているブランチです。
task claim は、タスクノートの kelpie フロントマターキー(UTC の machine、task、repo、since)に、どのマシンがどのタスクに取り組んでいるかを記録します。これにより、同じボルトを使う別のマシンから、ノートが確保済みであることがわかります。変更されるのはそのキーだけで、読み込んだバージョンに対する条件付き書き込みが行われます。ノートがその間に変更されていた場合は再度読み込みます。別のマシンが確保しているノートはそのままにし、"claimed": false とそのマシンの確保情報を返します。--force を指定すると確保を引き継ぎ、古い確保情報を replaced として返します。同じマシンが再度確保し、タスクが同じ場合は、since が維持されます。task release はキーを削除します。--machine を指定すると、別のマシンの確保情報はそのままにし、"released": false を返します。パスを指定せずに --machine と --task を指定すると、ノートの移動や名前変更後であっても、そのタスクの確保情報を現在のノートの場所で削除します。"paths": [] はノートがなくなっており、確保情報も削除されていることを示します。close とステータスを backlog に変更する操作でも確保情報は削除されます。write_file は内容にかかわらず保存済みの確保情報を維持するため、確保情報を変更するのはこれらのコマンドだけです。スキーマの optional に kelpie を含め、サーバーの指示では、このキーを編集するのはクライアントではないことを伝えます。
task check は、正規キー problem、solution、decisions、tests、scope を受け取ります。セクションは、その見出しの下から、同じレベル以上の次の見出しまでにテキストがあれば記入済みとみなされます。見出しは言語ごとのスキーマ設定であり、ボルトの language に応じて選択されます。
task:
sections:
en: { problem: Problem, solution: Solution, decisions: Decisions, tests: Test decisions, scope: Out of scope }
sv: { problem: Problem, solution: Lösning, decisions: Beslut, tests: Testbeslut, scope: Utanför ramen }
status が失敗することはありません。ボルトが設定されていない場合は "configured": false, "backend": null を出力し、終了コード 0 で終了します。読み取り専用で、writable は null のままです。--probe を指定すると、.vault/write-probe を書き込んで削除し、書き込み可能かどうかを測定します。
これらのコマンドは、まず環境変数からストレージ設定を読み取ります。環境変数でボルトが指定されていない場合は、代わりに Claude Code の保存済みユーザー設定を使用します。場所は $CLAUDE_CONFIG_DIR/settings.json(既定値は ~/.claude/settings.json)の pluginConfigs["vault@<marketplace>"].options で、vault_path、s3_endpoint、s3_bucket、s3_access_key、s3_secret_key、s3_prefix、s3_region を対応する環境変数に割り当てます。Claude Code がそのファイル以外に保存している値は、環境変数で指定する必要があります。
HTTP トランスポート
VAULT_TOKEN=$(openssl rand -hex 32) notes-vault-mcp serve --transport http --host 0.0.0.0 --port 8765
/mcp での Streamable HTTP には、4 つの認証方式があります。--auth bearer(デフォルト)、--auth oidc、--auth builtin、--auth forwarded です。
bearer は LAN モードです。すべてのリクエストに Authorization: Bearer $VAULT_TOKEN を含める必要があります。それ以外の場合は、サーバーに到達する前に 401 が返されます。このモードでは VAULT_TOKEN が必須で、設定されていないとコマンドは起動を拒否します。これはフルアクセス権を持つ単一のトークンであり、claude.ai では使用できません。
Host ヘッダーへの応答は、サーバーのリッスン先に応じて変わります。localhost にバインドされている場合、localhost、127.0.0.1、[::1] にのみ応答し、それ以外には 421 を返します。別のアドレスにバインドされている場合は、すべての Host に応答します。独自の名前、Kubernetes Service、LAN ホスト名で公開しながらチェックを有効にするには、その名前を VAULT_ALLOWED_HOSTS に列挙するか、VAULT_PUBLIC_URL を設定してください。すると、その名前と localhost に応答し、それ以外には 421 を返します。
oidc と builtin は OAuth を使用します。これは Claude コネクターに必要な方式です。リモート:claude.ai、Claude Desktop、モバイルを参照してください。
forwarded は、すでに OAuth 認証を行うプロキシの背後にあるサーバー向けです。署名付きの ID ヘッダーを信頼し、サブジェクトごとに 1 つのボルトを提供します。プロキシの背後での運用:ユーザーごとに 1 つのボルトを参照してください。
リモート:claude.ai、Claude Desktop、モバイル
--auth builtin または --auth oidc を指定して HTTPS 経由でサーバーを実行すると、claude.ai にカスタムコネクターとして追加できます。Web で一度接続すると、同じコネクターが Claude Desktop とモバイルアプリにも表示されます。
1 つのサーバーインスタンスで、1 つのボルトを提供します。
前提条件
- ボルト:ボルト専用のキーを使用する S3 互換バケット、またはホスト上のフォルダー。
- Docker、またはホスト上の
uv。 - マシンを指すドメインと、サーバーの前段にある TLS。
deploy/内の例では Caddy を配置し、Caddy が証明書を自動で取得します。 --auth oidcの場合:すでに運用している OpenID Connect プロバイダー。
2 つのモード
--auth builtin | --auth oidc | |
|---|---|---|
| ログインするユーザー | このサーバーが保持するパスワードを使う 1 人のオーナー | プロバイダーが許可するすべてのユーザー |
| クライアント登録 | 動的に行われるため、claude.ai で設定する必要はありません | プロバイダーが対応していれば動的に行われます。対応していない場合は、クライアント ID とシークレットを claude.ai に貼り付けます |
| 読み取り専用クライアント | ログインページにある 2 つのチェックボックス | グループへの所属 |
| ディスク上の状態 | VAULT_AUTH_DIR 配下の auth.sqlite | なし |
環境
| 変数 | モード | 意味 |
|---|---|---|
VAULT_PUBLIC_URL | oidc, builtin | 必須です。クライアントがアクセスする https アドレスです。サーバーがルートにない場合はパスも含めます。デフォルト値はありません。設定しないとサーバーは起動を拒否し、https://(ローカルで試す場合は http://localhost)以外の値も拒否します。 |
VAULT_AUTH_DIR | builtin | auth.sqlite の保存場所です。所有者のパスワードハッシュ、登録済みクライアント、ハッシュ化されたトークンが保存されます。デフォルトは ~/.cache/notes-vault-mcp/auth です。 |
VAULT_OIDC_ISSUER | oidc | 必須です。プロバイダーの発行者 URL です。 |
VAULT_OIDC_AUDIENCE | oidc | プロバイダーが aud に設定するクライアント ID です。必須です。これがないトークンや、別のオーディエンス向けのトークンは拒否されます。 |
VAULT_OIDC_READ_GROUP | oidc | vault:read を付与するグループです。デフォルトは vault です。 |
VAULT_OIDC_WRITE_GROUP | oidc | vault:read と vault:write を付与するグループです。デフォルトは vault-writers です。 |
VAULT_OIDC_SCOPES | oidc | リソースメタデータで通知するスコープです。クライアントが、プロバイダーで認識されているスコープを要求できるようにします。デフォルトは openid profile email groups です。アクセストークンに groups が含まれていない場合、サーバーはプロバイダーの userinfo エンドポイントに問い合わせます。 |
VAULT_TOKEN | bearer | 固定トークンです。 |
VAULT_ALLOWED_HOSTS | all | サーバーが応答する追加の Host ヘッダー値です。サービス名や、公開ホスト名と同じ LAN 上のホスト名を、スペースまたはカンマ区切りで指定します。 |
VAULT_CACHE_DIR | all | インデックスの保存場所です。デフォルトは ~/.cache/notes-vault-mcp です。 |
S3_ENDPOINT、S3_BUCKET、S3_ACCESS_KEY、S3_SECRET_KEY | all | VAULT_PATH でローカルフォルダーを指定していない場合の Vault です。 |
MCP エンドポイントは VAULT_PUBLIC_URL + /mcp です。これが claude.ai に貼り付ける URL です。
スコープ
vault:read—search、read_file、list_files、context、lint、backlog。vault:write—write_file、append_file、move_file、delete_file、close、log_append、backlog_add。
読み取り専用トークンで書き込みツールを呼び出すと、書き込みが途中まで行われたり、何もせずに終了したりするのではなく、トークンは Vault の読み取りのみ可能であることを示すエラーが返されます。
組み込みログインをゼロから設定する
cd deploy/caddy-builtin
cp .env.example .env
$EDITOR .env
docker compose up -d
docker compose exec vault notes-vault-mcp owner set-password
次に、claude.ai で「Settings → Connectors → Add custom connector」を選び、/mcp の URL(例:https://vault.example.com/mcp)を入力します。それ以外に必要な作業はありません。サーバーがクライアントを自動で登録します。ブラウザーにはサーバーのログインページが表示され、所有者のパスワードを求められた後、「読み取り」と「書き込み」の 2 つのチェックボックスが表示されます。書き込みのチェックを外すと、読み取り専用のコネクターを配布できます。
接続済みの内容と、接続を解除する方法:
docker compose exec vault notes-vault-mcp tokens list
docker compose exec vault notes-vault-mcp tokens revoke <client_id>
パスワードハッシュとトークンは、vault-data ボリューム内の auth.sqlite に保存されます。このボリュームをバックアップしてください。失うと、すべてのクライアントが再接続する必要があります。
OIDC をゼロから設定する
cd deploy/caddy-oidc
cp .env.example .env
$EDITOR .env
docker compose up -d
ここでサーバーはリソースサーバーとして動作します。プロバイダーのアクセストークンを検証し、パスワードを受け取ることはありません。書き込みグループのユーザーには vault:read と vault:write が付与され、読み取りグループのユーザーには vault:read のみが付与されます。どちらのグループにも属していないユーザーは拒否されます。
同じ手順で claude.ai にコネクターを追加します。動的クライアント登録に対応するプロバイダーであれば、これで完了です。対応していないプロバイダー(Pocket ID、Authentik など)の場合は、先にプロバイダーでクライアントを登録し、そのクライアント ID とシークレットをコネクターの「Advanced settings」に貼り付けます。プロバイダーで許可するリダイレクト URI は、claude.ai がそのダイアログに表示するものです。
Pocket ID
claude.ai は MCP リソース(/mcp URL)を OAuth の resource パラメーターとして送信しますが、Pocket ID が API として認識しているリソースしか受け付けません。そのため、以下の手順を行います。
- Settings → APIs → Add API:名前とリソース識別子を設定します。リソース識別子は
VAULT_PUBLIC_URL+/mcpと完全に一致させてください。権限を 2 つ追加します。キーをvault:readとvault:writeに設定し、同意画面に表示される名前を指定します。 - Settings → User Groups:
vault(読み取り)とvault-writers(書き込み)を作成し、ユーザーをそれぞれのグループに追加します。 - Settings → OIDC Clients → Add:名前を
claude.ai、コールバック URL をhttps://claude.ai/api/mcp/auth_callbackに設定し、PKCE をオン、public client をオフにします。許可するユーザーグループにvaultを指定します。API access で、両方の権限を持つ API をユーザー委任で許可します。クライアント ID とシークレットをコピーします。 - サーバーでは、
VAULT_OIDC_ISSUERに Pocket ID のベース URL、VAULT_OIDC_AUDIENCEに手順 1 のリソース識別子(Pocket ID はこれをaudに設定します)、VAULT_OIDC_SCOPESにopenid profile email groups vault:read vault:writeを設定します。 - claude.ai でコネクターを追加し、Advanced settings にクライアント ID とシークレットを入力します。
トークン内、または userinfo を通じてプロバイダーからグループ情報がまったく報告されない場合を除き、トークンで許可される操作はユーザーのグループによって決まります。クライアントの権限が関係するのは、その場合だけです。
Kubernetes
deploy/k8s/ には、/data に PVC をマウントする Deployment、Service、Ingress、サンプルの Secret など、プレーンなマニフェストが含まれています。
cp deploy/k8s/secret.example.yaml secret.yaml
$EDITOR secret.yaml
$EDITOR deploy/k8s/ingress.yaml
kubectl apply -f secret.yaml -f deploy/k8s/deployment.yaml -f deploy/k8s/service.yaml -f deploy/k8s/ingress.yaml
Deployment は --auth builtin で実行されます。OIDC を使うには、--auth 引数を変更し、Secret の VAULT_OIDC_* キーを設定してください。readiness probe は /.well-known/oauth-protected-resource/mcp にアクセスします。このルートはどちらのモードでもトークンなしで応答します。VAULT_PUBLIC_URL にパスが含まれている場合は、probe の URL にもそのパスが入ります。Pod の起動後、次のコマンドで所有者パスワードを設定してください。
kubectl exec -it deploy/notes-vault-mcp -- notes-vault-mcp owner set-password
ingressClassName を設定し、secretName に TLS 証明書を指定してください。既存の Secret を使用するか、cert-manager で証明書を発行します。
プロキシの背後で:一人につき一つの Vault
--auth forwarded は、MCP サーバーの前段ですでに OAuth 認可サーバーを運用し、ヘッダー内の署名付きトークンで呼び出し元の ID を転送する組織向けです(Cloudflare Access が Cf-Access-Jwt-Assertion で使用している方式です)。サーバーはプロキシの公開鍵でそのトークンを検証し、そこからサブジェクトを取得して、そのサブジェクト専用の Vault を提供します。一つのバケット内にユーザーごとのプレフィックスを設けるか、VAULT_PATH の下にユーザーごとのフォルダーを作成し、それぞれに専用のインデックス、専用の .vault/schema.yml、ウェルカムノートを用意します。これらはすべて最初のリクエスト時に作成されます。手動でプロビジョニングする必要はなく、プロキシでアクセスを許可するだけでオンボーディングが完了します。
notes-vault-mcp serve --transport http --host 0.0.0.0 --port 8765 --auth forwarded
プロキシとの取り決めは次のとおりです。有効な ID を含まないリクエストには WWW-Authenticate チャレンジを付けずに 403 を返します(ここで認可サーバーを提示すると、クライアントがプロキシを迂回してしまうためです)。トランスポートはステートレスで、JSON レスポンスをそのまま返すため、バッファリングするプロキシでも Mcp-Session-Id は不要です。Host ヘッダーは検証されず、準備状態の確認用プローブには /up がトークンなしで ok を返します。このモードでは OAuth メタデータのルートは提供されず、プロキシが管理します。
| 変数 | 意味 |
|---|---|
VAULT_PUBLIC_URL | 必須です。クライアントがサーバーにアクセスするアドレスです。ホスト名はトークンに含める必要がある aud で、パスがある場合はすべてのルートのプレフィックスになります。 |
VAULT_IDENTITY_PUBLIC_KEY | 必須です。プロキシの PEM 公開鍵です(RS256 または ES256)。1 行の生データ、または Base64 形式で指定します。 |
VAULT_IDENTITY_ISSUER | 必須です。プロキシのトークンに含まれる iss です。 |
VAULT_IDENTITY_HEADER | トークンを含むヘッダーです。デフォルトは X-Forwarded-Identity です。 |
VAULT_IDENTITY_TYP | 設定した場合、トークンの typ が一致する必要があります。 |
VAULT_IDENTITY_WRITE_CLAIMS | vault:read と vault:write を許可する権限名です(トークンの entitlements リストまたは scope 内)。デフォルトは vault:write です。 |
VAULT_IDENTITY_READ_CLAIMS | vault:read のみを許可する権限名です。デフォルトは vault:read です。どちらの権限も持たないトークンには 403 insufficient_scope が返されます。 |
VAULT_SUBJECT_PREFIX | バケットまたはフォルダー内で Vault を配置する場所です:<prefix>/<sub>/。デフォルトは users です。 |
VAULT_AUTH_DIR | personal.sqlite がハッシュ化された個人用トークンを保存する場所です。 |
VAULT_CACHE_DIR | サブジェクトごとの SQLite インデックスが保存される場所です。ボリュームを割り当ててください。 |
トークンには iss、aud、sub、exp が必要です。email と name は存在する場合に使用されます。通常の識別子ではないサブジェクトは、フォルダー名に使用する前にハッシュ化されます。
同じサーバー経由で Obsidian を利用する
各 Vault は、OAuth に対応していない Obsidian の Remotely Save プラグイン向けに、VAULT_PUBLIC_URL/dav/ で WebDAV 経由でも提供されます。ユーザーが Claude に obsidian_access の実行を依頼すると、サーバーは個人用トークンを発行し、一度だけ返して、そのハッシュのみを保存します。Remotely Save には /dav/ のアドレス、任意のユーザー名(慣例としてメールアドレスを使用します)、パスワードとしてそのトークンを設定します。obsidian_access を再度実行すると、トークンがローテーションされます。プロキシは /dav/* をそのまま転送し、クライアントの Authorization ヘッダーと WebDAV のメソッドおよびヘッダー(PROPFIND、MKCOL、MOVE、COPY、Depth、Destination、Overwrite)を変更せずに通す必要があります。最初のリクエスト時に作成されるウェルカムノートには、ユーザー向けの手順が記載されています。
開発
uv sync
uv run pytest
uv run ruff check .
テストスイートは、tests/fixtures/vault/ にあるフィクスチャ Vault と、moto でモックした S3 バケットを使用して実行されます。実際のバケットには一切アクセスしません。
ライセンス
MIT。詳しくは LICENSE をご覧ください。
コメント
AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。