Obsidianを操作するMCPサーバーを自作して、Claudeから自分のノートを読み書きする
自宅のUbuntuサーバーにObsidian操作用のMCPサーバーを立てて、スマホやパソコンのClaudeから自分のメモを検索・閲覧・編集できるようにした記録です。Python 3.12+FastMCP(HTTP・ポート8751)の構成、Dockerでの動かし方(user: 1000:1000)、用意した20個のツール、ウィキリンク解析やdry_run・transactionといった安全機構、Tailscale Funnelでのつなぎ方、Claude Codeへの指示書までまとめました。
目次
はじめに
「スマホのClaudeアプリから、自分のObsidianのメモを直接読んだり書いたりできたら便利なのに」と思ったことはありませんか。
私はメモをすべてObsidianにためているのですが、外出先でClaudeと相談しているときに、「その内容、私のメモに追記しておいて」とか「前に書いたあのメモを見て」と頼めたら、と思っていました。
普通のチャットAIは、自分の手元のメモを直接は見られません。
そこで、Obsidianを操作するための「MCPサーバー」を自分で作って、自宅サーバーで動かしました。
これにより、スマホやパソコンのClaudeから、自宅サーバーにある自分のObsidianのメモを、検索したり、読んだり、追記したりできるようになりました。
前提として、Ubuntuを入れてCasaOS(Dockerのアプリを画面から管理できる仕組み)まで用意した自宅サーバーの土台と、Syncthingで各端末と同期しているObsidianのメモがあるものとして進めます。
外からの接続にはTailscaleを使うので、そちらも入っている前提です。
少し聞き慣れない「MCP」という言葉が出てきますが、ひとつずつ噛み砕いて説明します。
MCPとは何か
MCPは「Model Context Protocol(モデル・コンテキスト・プロトコル)」の略で、ざっくり言うとAIと、外部のツールやデータをつなぐための「共通の差し込み口」のことです。
これまでのチャットAIは、基本的に「会話の中で渡された情報」しか扱えませんでした。
AIに自分のメモやアプリを触らせるには、それぞれ専用のつなぎ込みが必要で、簡単ではありませんでした。
MCPは、その「つなぎ方」を共通のルールとして決めたものです。
コンセントの形が統一されていれば、いろいろな家電を同じ差込口で使えるのと同じイメージです。
このMCPに対応した「サーバー」を用意すると、Claudeのような対応AIから、そのサーバーが提供する機能(ツール)を呼び出せるようになります。
つまり、Obsidian用のMCPサーバーを作れば、Claudeから自分のObsidianを操作できるわけです。
仕組みをもう一段だけ噛み砕くと、MCPサーバーは起動すると「自分はこういう道具(ツール)を持っています。それぞれの名前・説明・必要な引数はこうです」という一覧をAIに渡します。
AIはユーザーの依頼を読み、その一覧の中から「今はこの道具を、この引数で呼べばよさそうだ」と判断して呼び出します。
呼ばれたサーバーは実際の処理(ファイルを読む、書く、など)をして、結果をAIに返します。
AI自身がファイルを直接いじるのではなく、「AIが道具を選んで呼び、実際の手を動かすのはサーバー側」という分担になっているのがポイントです。
だから、危ない操作を断ったり、事前確認を挟んだりする安全装置は、AI任せにせずサーバー側に作り込めます(後述)。
何を作ったのか(技術構成)
私が作ったのは、Obsidianのメモ(Markdownファイルの集まり)を操作できるMCPサーバーです。
技術的な中身は次のようになっています。

少しだけ補足します。
FastMCPは、MCPサーバーに必要な決まりごと(ツールの一覧をAIに知らせる、呼び出しを受け取って結果を返す、など)を肩代わりしてくれる枠組みです。
実装では、関数の前に @mcp.tool(...) と一行付けるだけで、その関数がそのままClaudeから呼べるツールになります。
引数の説明や型も関数の定義から自動で拾ってAIに渡してくれるので、私たちは「どんな機能を提供するか」という中身だけに集中できます。
通信方式はHTTPで、サーバー本体は mcp.run(transport="http", host="0.0.0.0", port=8751) の一行で起動します。
HTTPにしているのは、後述するTailscale経由で、スマホやWebのClaudeアプリから接続するのに向いているからです。
Obsidianのメモの実体は、/DATA の下に置き、Syncthingで各端末と同期しているものです。
そのフォルダを、Dockerの「マウント」という仕組みでコンテナの中の /vault に見せ、サーバーは環境変数 VAULT_PATH=/vault でその場所を受け取ります。
マウントしているのはこのメモのフォルダ1つだけなので、コンテナの中からはサーバーの他のディレクトリには一切触れません。
ここで一つ、地味だけれど効いた設計判断があります。
コンテナは user: "1000:1000"、つまりサーバーの自分のユーザーと同じ権限で動かしています。
最初これを指定せず管理者(root)のまま動かしていたら、Claudeがメモを書き込むたびに、そのファイルの持ち主がrootに変わってしまい、あとからパソコンのObsidianやSyncthingで開けない・直せないという困りごとが起きました。
コンテナの実行ユーザーを自分のユーザー番号に合わせることで、誰が書いても同じ持ち主のファイルになり、この問題が消えました。
Dockerでホストのフォルダを共有するときに、よくハマる落とし穴です。
実際にこのサーバーを立てているDockerの構成は、おおむね次の内容です(Vaultの場所などは自分の環境に置き換えてください)。
services:
obsidian-mcp:
build: . # 手元のソースからローカルでイメージをビルド
container_name: obsidian-mcp
restart: unless-stopped
user: "1000:1000" # 自分のユーザー番号に合わせる(root所有を防ぐ)
ports:
- "8751:8751" # 家の中では http://(サーバーのIP):8751
volumes:
- /DATA/AppData/obsidian:/vault # ← 自分のVaultの場所に置き換え
environment:
- VAULT_PATH=/vault # サーバーが読み書きするVaultの場所
- TZ=Asia/Tokyo
要点だけ補足します。
build: . は、できあいのイメージを取ってくるのではなく、手元のソースから自分でイメージをビルドする指定です(中身が自作なので公開イメージはありません)。
ports: "8751:8751" は、サーバーが待ち受ける8751番を、家のネットワークからも同じ番号で開けるようにしています。
volumes の左がホスト側のVaultフォルダ、右がコンテナ内の /vault で、ここがメモの受け渡し口です。
user: "1000:1000" は先ほど触れた「書き込んだファイルの持ち主をrootにしない」ための指定で、ご自身のユーザー番号(id -u で確認できます)に合わせます。
そして、ここがこの構成の肝なのですが、プログラムのほとんどはサーバーに入れたClaude Codeに書いてもらいました。
私は「Obsidianをこう操作できるMCPサーバーがほしい」と日本語で伝え、出てきたものを動かして確認する、という進め方をしただけです。
サーバー本体のコードは、ツールを定義する server.py と、実際のファイル操作をまとめた vault.py の2ファイルだけというシンプルな構成です。
どんなことができるのか(提供しているツール)
このMCPサーバーには、Claudeから呼び出せる機能(ツール)を20個持たせています。
役割ごとに整理すると、こんな構成です。

ただ機能を並べただけでなく、Obsidianならではの作り込みをいくつか入れています。
これが、ありものの汎用ファイル操作ツールではなく「Obsidian専用」にした価値です。
- ウィキリンクの解析:Obsidianでメモ同士をつなぐ
[[メモ名]]という書き方を理解します。別名付き([[メモ名|表示名]])、見出し指定([[メモ名#見出し]])、埋め込み(![[メモ名]])も、ひとつの正規表現でまとめて拾い、それぞれの部分(フォルダ・名前・見出し・別名)に分解して扱います。フォルダを書かないリンク([[メモ名]])は、Obsidian本体と同じくボルト全体から同名のメモを探して結びつけます。 - リネーム・移動時のリンク自動追随:あるメモの名前を変えたり別フォルダへ移したりすると、そのメモをウィキリンクで参照している他のメモのリンクも、自動で新しい名前・パスに書き換えます(
update_links、既定でオン)。このとき、別名や見出し指定(表示名)の部分は壊さず、リンク先だけを差し替えます。手作業だと貼り直しが大変なところです。 - 検索が「ただのgrep」ではない:
search_notesは、ヒットしたメモを関連度スコア順に並べて返します。本文に語が多く出るほど高く、さらにファイル名(タイトル)に含まれていれば大きく加点します(実装では1語あたり+20点)。複数語は空白区切りで渡せて、「全部含む(AND)」「どれか含む(OR)」「フレーズとして完全一致」を切り替えられます。結果にはヒット箇所の前後を抜き出した抜粋も付けるので、Claudeが「どのメモを開くべきか」を判断しやすくなっています。 - 本文のキャッシュ:一度読んだメモの中身を一時的に覚えておく仕組み(LRUキャッシュ、既定で上限200MB)を入れています。ただ覚えるだけでなく、ファイルの最終更新時刻(mtime)が変わっていたらキャッシュを捨てて読み直すので、Syncthingで他の端末から書き換わったメモでも、古い内容を返してしまうことがありません。
そして、書き換えや削除のような取り返しのつかない操作には、二段の安全機構を入れてあります。
- ドライラン(
dry_run=true):実際には変更せず、「こう変わります/何件のリンクが書き換わります/削除すると何件のメモからリンク切れになります」という結果だけ先に見せます。削除・置換・移動・リネームで使えます。 - トランザクション(
transaction):複数の操作をまとめて1回で行い、途中で1つでも失敗したら、それまでの変更を全部なかったことにして元に戻します。仕組みとしては、変更する前のファイルの中身を内部にいったん控えておき、失敗したらその控えから書き戻す、という形にしています。たとえば「5件のメモを別フォルダに移して、ついでにリンクを貼り替える」を一度に頼んだとき、3件目でこけても中途半端な状態が残りません。
加えて、AI任せにしない歯止めも入れています。
削除は、そのメモが他のメモからウィキリンクで参照されている場合、既定では拒否します(force=true を明示しないと消せない)。
リンク切れを生む削除を、うっかり一発で通さないためです。
テンプレート置き場(90_Templates)のような壊されたくないフォルダは、そもそも編集・削除できないように保護しています。
パスも、ボルトの外(../ でさかのぼるような指定)を弾くようにしてあり、Claudeがマウントした範囲の外には出られません。
AIにメモの編集を任せる以上、ここは特に気をつけて作り込みました。
これらがあると、たとえば外出先で「さっきの打ち合わせの要点、今日のデイリーノートに追記しておいて」とClaudeに頼むだけで、自宅サーバーのObsidianにメモが書き込まれます(デイリーノートは 05_DailyNotes/年/年-月-日.md のような決まった場所に作る運用にしています)。
帰宅してパソコンでObsidianを開くと、ちゃんとそのメモが入っている、という具合です。
なぜObsidianだとシンプルに作れるのか
少しだけ、なぜこういう仕組みを手軽に作れたのかを説明します。
Obsidianのメモの正体は、特別な形式ではなく、ただのテキストファイル(Markdownという書き方のファイル)の集まりです。
中身がそのまま文字で書かれているので、プログラムから「開いて読む」「文字列を検索する」「末尾に書き足す」といった操作が、特別な変換を挟まずに直接できます。
だからこそ、上で挙げた「検索する」「一部だけ置き換える」「リンクを解析する」といった機能を、複雑な仕掛けなしに実現できます。
もしこれが独自形式のデータベースだったら、まず形式を読み解く部分から作る必要があり、ここまで手軽にはいきませんでした。
普段使っているメモがプレーンテキストの集まりであることが、こうした自作の道具と組み合わせたときの強みになります。
ひとつ、設計でこだわった点があります。
それは「全文を渡して丸ごと上書き」を、できるだけ避ける作りにしたことです。
長いメモの一行だけ直したいときに、メモ全体をAIに読ませて、全文を書き直させて、丸ごと保存し直す——という作り方もできます。
ですがこれは、無駄が多いうえに危険です。
AIが長い本文を再生成する過程で、関係ない箇所をうっかり削ったり書き換えたりするリスクがあるからです。
そこでこのサーバーでは、str_replace_note(直したい部分の文字列だけを「ここをこれに」と指定して置換)、append_to_note(末尾に足す)、prepend_to_note(先頭に足す)といった部分編集のツールを揃え、AIにもこちらを優先するよう指示しています。
str_replace_note は、指定した文字列がメモ内に複数あると、安全のため既定でエラーにして「前後を含めて一意にしてください」と促します(うっかり別の場所を書き換えないため)。
一度に直したい箇所が複数にまたがるときは、先ほどの transaction でまとめて行い、途中でこければ全部巻き戻す。
部分編集と一括ロールバックを組み合わせて、AIに任せても本文が壊れないようにする——ここがこのMCPの設計のいちばんの肝です。
どうやってClaudeとつなぐのか(Tailscale Funnelの出番)
ここで、Tailscaleの回で紹介した「ファンネル」という機能が効いてきます。
スマホやパソコンのClaudeアプリ(claude.ai)から自宅サーバーのMCPサーバーに接続するには、Claude側からこのサーバーに届く必要があります。
普段使いのサービスはTailscaleの専用ネットワークに閉じていますが、claude.aiのような外部のサービスと連携させるには、このMCPサーバーの入り口だけは外から届くようにしておく必要がありました。
そこで、サーバー全体を開けるのではなく、このMCPサーバーの入り口だけを、ファンネルで限定的にインターネットに公開しています。
「普段使いはテールネット限定、外部公開はファンネルで最小限」という二段構えを紹介しましたが、それを実際に使っている例がこれです。
具体的には、ファンネルで公開しているアドレスのうち、ある1つのパス(たとえば /obsidian)への通信だけを、サーバー内部で待ち受けている8751番のこのMCPサーバーに振り向けています。
公開しているのはこの1パスだけで、ポート8751そのものを直接インターネットにさらしているわけではありません。
公開したアドレスを、claude.aiの「コネクター(外部接続)」の設定にMCPサーバーとして登録すると、Claudeがそのアドレスを通じてツールの一覧を受け取り、呼び出せるようになります。
これで、スマホのClaudeアプリからでも、自宅サーバーのメモを読み書きできる状態のできあがりです。
家の中(自宅LANやTailscale端末)からだけ使うなら、ファンネルでの公開はせず http://(サーバーのIP):8751 を直接コネクターに登録する手もあります。
外から使いたいかどうかで、公開の要否が変わります。
Claude Codeへの指示書
自分専用のObsidian操作MCPを作ってもらうための、Claude Codeへの指示書の例です。
ポイントは、いきなり全機能を作らず、まず「読むだけ」の最小構成から始めて、動いたら書き込み系を足していくことです。
読むだけの機能ならメモを壊す心配がなく、つまずいても原因を切り分けやすくなります。
あなたはこのサーバーの開発を手伝うエンジニアです。
私は非エンジニアなので、専門用語は噛み砕いて、作るものの全体像を先に説明してください。
目的:自分のObsidianのメモ(/DATA の下にあるMarkdownのフォルダ)を、
claude.aiのアプリから操作できるMCPサーバーを作りたい。
進め方:
1. まずは「メモを検索する」「メモを読む」だけができる、最小のMCPサーバーを作ってほしい。
PythonのFastMCPを使い、通信はHTTP(transport="http")、Dockerで動かせるようにすること。
Obsidianのフォルダはコンテナの /vault にマウントし、場所は環境変数 VAULT_PATH で渡す。
コンテナの実行ユーザーは私のユーザー番号(1000:1000)に合わせ、書き込んだファイルが
root所有にならないようにすること。
2. 動作を確認できたら、「末尾に追記する」「新しいメモを作る」「一部だけ置換する」を足す。
全文上書きより、文字列を指定して直す部分置換を優先する作りにしてほしい。
3. 削除・移動・リネームなど危ない操作には、dry_run(事前プレビュー)と、
途中失敗で元に戻すトランザクションの仕組みを必ず入れること。
他のメモからリンクされているメモの削除は、既定で拒否してほしい。
4. リネーム・移動のときは、そのメモをウィキリンクで参照している他のメモのリンクも
自動で書き換えてほしい。別名や見出し指定(表示名)の部分は壊さないこと。
各段階で、何を作ったか・どう動かして確認すればいいかを説明してください。
このように最小から始めて段階的に伝えると、自分の理解が追いつくペースで、安全に作っていけます。
注意点(セキュリティ)
便利な反面、気をつける点もあります。
claude.aiとつなぐためにファンネルで公開する入り口は、インターネットに開いている部分なので、ここは慎重に扱う必要があります。
公開するのはこのMCPサーバーの入り口だけに絞り、サーバーの他の部分は引き続きTailscaleの専用ネットワークに閉じておきます。
また、自分のメモという大事なデータを扱うので、接続できる相手を限定する仕組み(合言葉のようなトークン)を入れておくと、より安心です。
このあたりの設計も、「外部公開する入り口を安全にするにはどうすればいい?」とClaude Codeに相談しながら決めていくとよいと思います。
実際の呼び出しの流れ
言葉だけだとイメージしづらいので、Claudeがこのツールをどう使っているかを一度たどってみます。
たとえば「先週書いた自宅サーバーのメモを探して、今日の続きを一緒に考えて」と頼んだとします。
- Claudeはまず
search_notesを呼び、「自宅サーバー」というキーワードで関連するメモを探します。返ってくるのは、関連度スコア順に並んだメモの名前と、一致した箇所の前後の抜粋です。タイトルに語が入っているメモは上位に来やすいので、目当てのメモが先頭付近に出ます。 - 目当てのメモが見つかったら、
read_noteでそのメモの全文を取得します(長いメモなら行範囲だけを指定して、必要な部分だけ読むこともできます)。複数候補があれば、read_notesで一度にまとめて取ります。 - 中身を踏まえて相談に乗り、私が「じゃあ今日のメモに追記して」と言えば、
append_to_noteでその日のデイリーノートに書き足します。「ここの一行だけ直して」ならstr_replace_noteで該当箇所だけを置き換えます。
ポイントは、Claudeが「いきなり全部のメモを読む」のではなく、検索でアタリを付けてから必要なメモだけを読むことです。
メモが何千ファイルあっても、毎回全部をAIに渡すわけではないので、トークン(AIに渡せる情報量)の無駄遣いになりません。
検索の結果に前後の抜粋を付けて返しているのも、Claudeが「どのメモを開くべきか」を本文を読む前に判断できるようにするための工夫です。
よくあるトラブルと対処
私が実際に作って使う中で気づいた点を、Q&Aでまとめておきます。
Q. ファンネルで公開したのに、claude.aiのコネクターからつながらない
まず、公開したアドレス(とパス)が正しいか、MCPサーバーのコンテナがちゃんと起動しているかを確認します。
「ObsidianのMCPサーバーが起動しているかログを見て」とClaude Codeに頼むと、どこで止まっているかを調べてくれます。
トークンでの接続制限を入れている場合は、その合言葉が一致しているかも確認します。
Q. メモは見つかるのに、書き込みがエラーになる
コンテナにフォルダを「読み取り専用」でマウントしていると、検索や読み取りはできても書き込みで失敗します。
書き込み系の機能を使うなら、マウントを読み書き可にする必要があります。
最初は読み取り専用で安全に動かし、書き込みを足すときにここを切り替えるのがおすすめです。
Q. AIがメモを意図しない形に書き換えてしまわないか心配
このために、書き換え系は「全文上書き」ではなく str_replace_note のような部分編集を優先させ、削除・置換・移動には dry_run=true(事前プレビュー)を挟めるようにしています。
複数操作は transaction でまとめ、途中でこけたら全部巻き戻します。さらに、他のメモからリンクされているメモの削除は既定で拒否します。
それでも書き換わってしまったときの最後の砦は、外付けHDDへの自動バックアップです。Vaultは /DATA 配下にあり、世代付きでバックアップしているので、過去の状態に戻せます。AIに編集を任せるなら、このバックアップは必須だと考えてください。
Q. 検索やまとめ読みの反応が遅い
メモの数が非常に多いと、全文をなめる検索は時間がかかります。search_notes は全ファイルをスコア付けしてから並べ替える作りなので、ボルト全体が対象だと相応に待ちます。
一度読んだメモは本文キャッシュで速くなりますが、初回や、大量のメモを横断する検索は重くなりがちです。folder で対象フォルダを絞る、キーワードを具体的にする、scope=title(ファイル名だけを見る)に切り替える、といった指定で軽くなります。
まとめ
いかがでしたか。
今回は、Obsidianを操作するMCPサーバーを自作して、Claudeから自分のメモを読み書きできるようにした話を紹介しました。
振り返ると、
- MCPは、AIと外部のツールやデータをつなぐ共通の差し込み口。AIは道具を選んで呼ぶだけで、実際の手はサーバーが動かす
- Python 3.12+FastMCP(HTTP・ポート8751)で、メモの閲覧・編集・整理ができるツールを20個用意した
- 設計の肝は「全文上書きを避け、部分編集+一括ロールバックでAIに任せても本文を壊さない」こと。ウィキリンク解析・リネーム/移動時のリンク自動追随・関連度順検索・mtime連動キャッシュ・
dry_run・transaction・リンク付きメモの削除拒否などを入れた - コンテナは
user: "1000:1000"で動かし、書き込んだファイルがroot所有になる落とし穴を回避した - つなぐときは、Tailscale Funnelで入り口(1パス)だけを限定公開し、claude.aiのコネクターに登録する。プログラムのほとんどはClaude Codeに書いてもらえる
という内容でした。
既製のアプリを入れるだけでなく、「自分専用の道具を作る」ところまで来られたのは、自分でも少し驚いています。
なお、同じ作り方を応用して、健康ログをClaudeから記録するMCPサーバーも別に作っています。
この記事が誰かの役に立てばうれしいです。