NotePM内容搜索MCP服务器

NotePM MCP服务器是一款通过Model Context Protocol提供NotePM内容全文搜索、标题/标签/代码检索、条件筛选及详情获取的搜索服务,适用于在MCP兼容客户端中快速查找和获取NotePM文档内容。

  • by k-ibaraki
  • Python
  • 本地部署
  • 1 星标
  • 更新于
在 GitHub 查看 k-ibaraki/notepm-mcp-server

使用说明

NotePM MCP Server

NotePMのコンテンツを検索するためのModel Context Protocol (MCP) サーバーです。このサーバーを使用することで、NotePMの検索機能をMCP対応のクライアントから利用することができます。

機能

  • NotePMのコンテンツ全文検索
  • タイトルのみの検索
  • タグによる検索
  • ノートコードによる検索
  • 作成者(ユーザーコード)による絞り込み
  • 作成日時・更新日時の範囲による絞り込み
  • アーカイブされたページの検索オプション
  • ページネーション対応
  • 詳細な記事の内容を取得

必要条件

  • Python 3.10以上(開発時の想定は3.12)
  • NotePMのアカウントとAPI Token
  • uv

インストール

uv sync

依存関係の方針(重要)

デプロイ時は必ず uv.lock を尊重してインストールしてください。

uv sync --frozen --no-dev   # uv.lock の固定バージョンのみを使う(開発用依存は除く)

--no-dev を省くと [dependency-groups] dev(ty や pytest)まで実行環境に入り、 不要な依存をコンテナ起動ごとに取得することになります。

uv を使わないイメージ(pip install 系)では、ロックから requirements を書き出してください:

uv export --frozen --no-dev -o requirements.txt
pip install -r requirements.txt --no-deps

逆に uv.lock を無視する経路(uvx、pip install git+... など)を使うと、 起動ごとに PyPI から最新版を取得するため、上流の破壊的変更でそのまま起動不能になります。

2026-07-28 に公開された mcp 2.0.0 で mcp.server.lowlevel.server.request_ctx が削除され、 これを参照する同居ライブラリごと ImportError で起動失敗する障害が発生しました。 uv.lock は当時 mcp==1.6.0 を固定していたため、ロックを尊重していれば影響はありませんでした。

二重の防御として pyproject.toml の依存には上限(mcp>=2.2.0,<3 など)を付けています。 依存を更新する際は、上限を外さずにレンジごと引き上げてください。

現在の主要依存

パッケージ レンジ 備考
mcp >=2.2.0,<3 低レベルサーバは 2.x のハンドラ注入方式(on_list_tools / on_call_tool)を使用
httpx2 >=2.12.0,<3 httpx の後継。httpx 0.28.1 は実質保守停止のため移行済み
pydantic >=2.12.0,<3 ツールの入力スキーマ生成に使用
click >=8.1.0,<9 CLI エントリポイント
python-dotenv >=1.1.0,<2 .env の読み込み

mcp の CLI extra(mcp[cli])は使用していないため外しています。 本体は click を直接使っており、mcp.cli を import していません。

TLS 証明書の取り扱い(デプロイ時の注意)

httpx から httpx2 への移行に伴い、CA 証明書の解決方法が変わりました。 httpx2 は certifi を同梱せず、既定では truststore 経由で OS のトラストストアを参照します。

そのため CA 証明書を持たない最小構成のイメージ(distroless、ca-certificates 未導入の Alpine など)では、 NotePM への HTTPS 接続が証明書検証エラーになります。イメージに ca-certificates を導入するか、 SSL_CERT_FILE もしくは SSL_CERT_DIR で証明書の場所を明示してください。

開発

型チェック

型チェックには ty を使用します。dev 依存に含まれているため、 uv sync 済みであれば追加の準備は要りません。

uv run ty check

ty はまだ 0.0.x のプレビュー段階です。本リポジトリでは pyproject.toml の [tool.ty.rules] で all = "error" を指定し、既定では警告にとどまる規則も含めてすべてエラーとして扱っています。 ty を更新すると新しい規則の追加によって指摘が増えることがあるため、バージョンの上限 (ty>=0.0.79,<0.1)は外さず、レンジごと引き上げてください。

テスト

テストは pytest で実行します。

uv run pytest

実際の NotePM API には接続しません。HTTP 応答は httpx2 の MockTransport で差し替えており、 差し替えを忘れたテストは tests/conftest.py のフィクスチャが検知して失敗させます。 同じフィクスチャが NOTEPM_ 系の環境変数も毎回消すため、手元に .env があっても結果は変わりません。

環境設定

サーバーが読む環境変数は次のとおりです。NOTEPM_TEAM と NOTEPM_API_TOKEN は必須で、 どちらかが欠けていると起動時に ValueError を送出して停止します。設定の誤りは 動き出す前に気付けるほうがよいためです。

環境変数 必須 既定値 説明
NOTEPM_TEAM ○ なし NotePM のチーム名。API のホスト名(https://<team>.notepm.jp)に使います
NOTEPM_API_TOKEN ○ なし NotePM の API トークン
NOTEPM_MAX_BODY_LENGTH 200 検索結果の本文を切り詰める文字数
NOTEPM_SEARCH_DESCRIPTION 実装の既定文 notepm_search の説明文の差し替え
NOTEPM_PAGE_DETAIL_DESCRIPTION 実装の既定文 notepm_page_detail の説明文の差し替え
NOTEPM_RAISE_EXCEPTIONS 無効 デバッグ用。ハンドラを抜けた例外を再送出します

.env ファイルに書いても読み込まれます。.env.sample が同じ一覧を持っているので、 複製して値を埋めるのが手早いはずです。

cp .env.sample .env

MCP クライアントから起動する場合は、.env の代わりにクライアント側の設定へ書けます (「MCP クライアントの設定」を参照)。

NOTEPM_MAX_BODY_LENGTH

検索(notepm_search)の結果に含まれる本文を、この文字数で切り詰めます。超えた分は 末尾を ... に置き換えます。詳細取得(notepm_page_detail)は全文を返すため、この設定の 影響を受けません。全文が要るときは、検索結果の page_code を詳細取得へ渡してください。

指定できるのは 0 以上の整数です。0 は本文を丸ごと省略する指定として扱います。整数として 読めない値や負の値を渡した場合は、変数名と受け取った値を添えた ValueError で起動に 失敗します。値を空にしたときは、未設定と同じく既定値になります。

NOTEPM_RAISE_EXCEPTIONS

1 / true / yes / on のいずれかで有効になります(大文字小文字は問いません)。 有効にすると、ハンドラを抜けた例外がそのまま送出され、サーバープロセスが停止します。 原因の切り分けには便利ですが、常駐させる通常の運用では設定しないでください。既定の 無効のままなら、想定外の例外はクライアントへのエラー応答に変換され、サーバーは動き続けます。

なお、ツール実行中の例外は call_notepm_tool() が捕捉して isError の結果に変換するため、 このフラグの影響を受けません。フラグが効くのは、ツール一覧の取得など、ハンドラの外へ 例外が抜ける経路だけです。

HTTP 接続の扱い

NotePM への HTTP クライアントは、サーバーの起動から停止まで使い回します (個々の接続は 60 秒の keepalive を過ぎると入れ替わります)。停止時には接続を解放します。 ツール呼び出しのたびにクライアントを作り直すと、TLS ハンドシェイクとコネクションプールが 毎回捨てられ、検索してから詳細を取るという典型的な流れで接続確立のコストが積み上がります。

タイムアウトと再試行は環境変数ではなく、notepm_mcp_server/notepm.py の定数として明示しています。

項目 値 備考
1 回の呼び出し全体の上限 60 秒 再試行と待ち時間を含みます。超えたらエラーとして返します
タイムアウト(接続 / 読み取り / 書き込み / プール待ち) 5 / 30 / 10 / 5 秒 全文検索は時間がかかり得るため、読み取りだけ長く取っています
試行回数 最大 3 回(初回を含む) 深追いするとツール呼び出しが戻らなくなります
再試行の対象 429 / 500 / 502 / 503 / 504、接続の確立失敗、切られていた接続 認証エラーなど他の 4xx は何度送っても同じ答えなので対象外です
再試行までの待ち時間 0.5 秒から倍々、上限 10 秒 Retry-After が秒数として読めればそちらに従い、同じ上限で丸めます

読み取りのタイムアウト(ReadTimeout)は再試行しません。応答を返さない相手に対して、 待ち時間が試行回数の分だけ積み上がるためです。再試行しない代わりに、他の通信の失敗と 同じ分類のエラーとしてすぐに返します(「エラーの分類」を参照)。

全体の上限を別に持っているのは、試行ごとの上限だけでは 1 回の呼び出しが読み取り 30 秒 × 3 試行で 90 秒を超え得るためです。MCP のホスト側にもツール実行の制限があり (Claude Code の MCP_TOOL_TIMEOUT など)、そちらに先に打ち切られると、 何が起きたのかが呼び出し側に伝わりません。

障害時の切り分け

エラーの分類

NotePM API が失敗を返しても、サーバーは停止しません。原因ごとのメッセージを載せた isError の結果をツールの呼び出し元へ返します。

状況 HTTP 伝えること
API トークンが不正、または権限が足りない 401 / 403 NOTEPM_API_TOKEN の値と、そのトークンの権限を確認する
パラメータを受け付けてもらえなかった、または対象のページに辿り着けない 400 指定した値を見直す。詳細取得では、失敗した page_code を添えて返す
対象の URL が見つからない 404 詳細取得では、失敗した page_code を添えて返す
リクエスト制限を超えた 429 時間をおいて再試行する
NotePM 側で処理に失敗した 5xx 一時的な障害の可能性があるため、時間をおいて再試行する
上限の時間までに応答が返らない - 60 秒で打ち切る。時間をおいて再試行する
NotePM との通信が最後まで完了しない - ネットワークの状態を確認し、時間をおいて再試行する
応答が JSON として読めない 200 メンテナンス中などで JSON 以外が返っている可能性がある

存在しない page_code を詳細取得へ渡したとき、NotePM は 404 ではなく 400 を返します (応答本文は {"messages":["権限がありません"]})。存在しないページと権限の無いページを NotePM が区別しないため、詳細取得の 400 では両方の可能性を挙げたうえで page_code を 添えて返します。表の 404 が指すのはチーム名の誤りのような URL 水準の失敗で、この分岐は 返らないと確かめたわけではないので残してあります。

リクエスト制限の具体的な上限値は、エラーメッセージには載せていません。NotePM の API ドキュメントは「ユーザ毎に1分間に60リクエスト」としていますが、実応答の X-RateLimit-Limit ヘッダは 120 を返します。どちらが実際の上限かをこちらでは断定 できないため、確かめられない数字を呼び出し側へ渡さない方針です。

429 と 5xx、接続の確立失敗、切られていた接続は「HTTP 接続の扱い」の再試行を尽くした うえで、それでも直らなかったときにこの形で返ります。読み取りの時間切れ(30 秒)は 再試行しないため、1 回目でこの形になります。

メッセージには API トークンも応答本文も含めません。応答本文の中身は NotePM 側の都合で 決まるため、そのまま返すとクライアントの記録や LLM の文脈へ制御できない内容が流れ出ます。 本文を確かめたいときは -vv で起動してください(先頭 200 文字までを stderr に出します)。

ログ

ログは stderr に出力します(stdout は JSON-RPC が使うため)。既定では警告以上のみで、 -v で INFO、-vv で DEBUG まで下がります。

uv run notepm-mcp-server -v
水準 出力する内容
WARNING 入力検証や未知のツール名で弾いた呼び出し、再試行することにした一時的な失敗、NotePM とのやり取りで生じた失敗
INFO サーバーの起動と終了、ツール呼び出しの開始と完了
DEBUG 発行する URL とクエリ、応答のステータス、失敗した応答の本文(先頭 200 文字)

トレースバックを残すのは想定外の失敗だけです。弾いた呼び出しと NotePM API が返した失敗は サーバーの不具合ではないため、警告に留めます。トレースバックが並ぶと、本当の異常が 埋もれてしまうためです。

水準はルートロガーへ設定するため、依存ライブラリのログも同じ水準で出ます。-v では httpx2 がリクエストごとに 1 行(メソッド・URL・ステータス)を加え、-vv では接続の 詳細まで加わります。いずれも Authorization ヘッダは出力しません。

この httpx2 の 1 行には、URL の一部として検索語(クエリ文字列)が含まれます。上の表で クエリを DEBUG に挙げているのはこのサーバー自身の出力の話で、検索語が記録に現れるのは -v からです。検索語を残したくない場合は既定の水準のまま運用してください。

MCP クライアント経由で起動している場合、この出力はクライアント側のログに記録されます。

使用方法

サーバーの起動

uv run notepm-mcp-server

MCP クライアントの設定

サーバーを登録するキーの名前はクライアントによって異なります。以下はどちらも設定ファイル 全体なので、そのまま貼り付けたうえで --directory のパスと env の値を書き換えてください。 --directory には、このリポジトリを clone した先の絶対パスを指定します。

--frozen --no-dev を付けているのは、uv.lock を尊重して起動するためです (「依存関係の方針」を参照)。

VS Code(ワークスペースの .vscode/mcp.json、またはユーザーの mcp.json):

{
  "servers": {
    "notepm-mcp-server": {
      "command": "uv",
      "args": [
        "--directory",
        "/path/to/notepm-mcp-server",
        "run",
        "--frozen",
        "--no-dev",
        "notepm-mcp-server"
      ],
      "env": {
        "NOTEPM_TEAM": "your-team-name",
        "NOTEPM_API_TOKEN": "your-api-token"
      }
    }
  }
}

Claude Desktop(claude_desktop_config.json)や Claude Code(.mcp.json):

{
  "mcpServers": {
    "notepm-mcp-server": {
      "command": "uv",
      "args": [
        "--directory",
        "/path/to/notepm-mcp-server",
        "run",
        "--frozen",
        "--no-dev",
        "notepm-mcp-server"
      ],
      "env": {
        "NOTEPM_TEAM": "your-team-name",
        "NOTEPM_API_TOKEN": "your-api-token"
      }
    }
  }
}