mcpField
C
Cite Caddy

Cite Caddy

リモート
github.com/herbertkokholm生産性02 週間前に更新
リポジトリ

Cite Caddy について

Cite Caddyは、Zoteroライブラリの読み書きを行うAIアシスタント向けのリモートMCPブリッジです。39個のツールでアイテム、コレクション、添付ファイル、ノート、タグ、保存済み検索を扱い、バージョン競合や再試行に備えた仕組みも備えています。

主な機能

  1. 01アイテムの検索・作成・更新・削除・移動
  2. 02コレクション、タグ、保存済み検索の管理
  3. 03添付ファイルのアップロード・ダウンロードと抽出テキストの読み取り
  4. 04アイテムノートの読み書き
  5. 05一部の書き込み操作でのバージョン確認と冪等性キー

活用シーン

  1. 01調査中の参考文献の追加とタグ付け
  2. 02ライブラリ添付ファイルから抽出されたテキストの閲覧
  3. 03コレクションと保存済み検索の整理
  4. 04応答が不明確な場合のZotero操作の再試行

MCP 対応

リモートHTTPサーバーまたはローカルstdioモードで、Zoteroライブラリ用の39個のツールをMCPクライアントに公開します。HTTPアクセスではOAuth 2.1を使用し、各呼び出しを利用者自身の登録済みライブラリに振り分けます。

設定

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

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

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

ツール

詳細

Cite Caddy

cite-caddy MCP server smithery badge MCP Registry MCPBundles Lint Latest release License: MIT

AI アシスタント向けの文献ライブラリブリッジ — スタンドアロンのリモート MCP サーバーです。

独立した非公式プロジェクトです。Corporation for Digital Scholarship(Zotero)とは提携、推奨、スポンサー関係にありません — Zotero の商標ポリシーをご覧ください。 Zotero Web API 上に構築されています。現在のバックエンドは Zotero ですが、名称と ツールの範囲は、将来的にほかのサービスにも対応できるよう設計されています。

Cite Caddy は Zotero ライブラリへの完全な読み書きアクセスを提供します。検索、アイテムの追加、 タグ付け、更新、削除、移動、コレクションの作成、名前の変更、削除、 添付ファイルのアップロード/ダウンロードと抽出済み全文の読み取り、 アイテムのノートの読み書き、ライブラリ全体のタグ、ごみ箱、保存済み検索の管理、 Zotero 独自のアイテムタイプ/フィールドスキーマの参照ができます。ツールは全部で 39 個です。 全リストは下記のツールをご覧ください。

このプロジェクトの目的

調査結果を Zotero ライブラリと照合する読み取り専用ツール(たとえば DOI/arXiv ID による照合)は、結果を報告するだけで安全に完結できます。ライブラリに何かを書き戻す必要はありません。このプロジェクトは意図的にさらに踏み込み、削除や移動も含む Zotero ライブラリへの完全な CRUD を実現します。これにより、アイテムへのタグ付け、追加、整理も自動化できます。

これは意図的な範囲の選択であり、現実的なリスクも伴います。既存のアイテムのキーを変更する書き込み操作(削除、別のライブラリへの移動、「ライブラリのクリーンアップ」リセット)は、Zotero Word プラグインのライブフィールドコードで引用している Word 文書を壊します。削除や移動を行う前に、下記の「キーの安全性」をご確認ください。

キーの安全性(削除/移動を実装する前にお読みください)

Zotero Word プラグインを使って Word 文書で引用されている Zotero アイテムは、ライブフィールドコードに埋め込まれた、そのアイテムのキーで参照されます。アイテムのキーを維持する操作(作成、フィールドの更新、タグの追加/削除、ノートの追加)は安全です。キーを維持しない操作(削除、および Zotero が削除+再作成として実行するライブラリ間の移動)は、引用を気付かれないまま壊します。Word 文書にエラーが表示されるわけではありませんが、誰かが次にフィールドを更新するか、Zotero を開いたときに、古い、または壊れたフィールドテキストが表示されます。

このプロジェクトでは、そのリスクを承知のうえで意図的に完全な CRUD を選択しました。削除/移動ツールを実装する場合は、次の点に留意してください。

  • ツール名と docstring で破壊的な意図を明確にしてください(MCP クライアントのモデルは呼び出し前に両方を読みます)。この README だけで明示するのでは不十分です。
  • 呼び出し元にアイテムの現在の Zotero version を渡させることを検討してください(楽観的同時実行制御)。これにより、Zotero デスクトップアプリや別のクライアントから同時に加えられた変更を、削除/更新が気付かれないまま上書きする事態を防げます。
  • 削除前のドライラン/確認ステップも検討する価値があります。ただし、これはそのツールを構築する人が決める実装上の判断であり、ここでは決定していません。

冪等性

delete_item_permanently、delete_collection、delete_tag、 delete_saved_search、move_item_to_different_library、update_publication_status は、すべて省略可能な idempotency_key を受け付けます。応答が失われた場合や不明瞭な場合(ネットワークタイムアウトなど)に呼び出しを再試行するときは、同じ不透明な文字列を渡してください。そうすると、Zotero に対して操作を再実行する代わりに、元の結果(成功またはエラー)が再現されます。異なる引数で同じキーを再利用すると、以前の結果を黙って返すのではなくエラーが発生します。そのため、論理リクエストごとにキーを1つ生成し、同じリクエストの再試行で自由に再利用しても安全です。

これは特に move_item_to_different_library で重要です。この操作は、対象ライブラリにアイテムを再作成してから、元のライブラリから削除します。作成は成功したものの、削除に失敗した場合、単純に再試行すると処理全体が再実行されます。元のアイテムのバージョンは変わっていないため、対象ライブラリに重複アイテムがもう1つ作成されてしまいます。idempotency_key を使えば、Zotero に再度アクセスする代わりに、キャッシュされた失敗結果(および「手動でクリーンアップする」ための案内)が再現されます。

キャッシュはサーバープロセスごと(HTTP モードではオンボーディング済みテナントごと)のメモリ内に保存され、TTL は24時間です。この期間内の再試行には保持されますが、再デプロイや再起動をまたいで保持されることはありません。

設定

stdio モード(ローカル、単一ユーザー — $PORT なし):接続するライブラリは環境変数から取得します。

ZOTERO_LIBRARY_ID      numeric library ID (user or group)
ZOTERO_LIBRARY_TYPE    "user" or "group" (default: user)
ZOTERO_API_KEY         from Zotero -> Settings -> Security -> Applications
                        (needs write permission, not just read)

HTTP モード(リモート、マルチテナント — $PORT が設定済み):単一のライブラリは設定されません。各呼び出し元が /login フォームで自分の Zotero Library ID/Type/API Key を指定します(下記の「デプロイ」を参照)。代わりに、次のように設定します。

MCP_TOKEN_STORE_KEY    Fernet key encrypting onboarded tenants' API keys at
                        rest; generate once at deploy time with:
                        python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"
MCP_PUBLIC_URL          public HTTPS URL this server is reachable at
MCP_DATA_DIR            where OAuth clients/tokens/tenants persist (default: ./.data)

どちらのモードでも省略可能です。

MCP_WEBSITE_URL         public site reported as serverInfo.website_url; also used to
                        build serverInfo.icons[0].src as MCP_WEBSITE_URL + "icons/icon.svg"
                        and the /login page's privacy policy link as
                        MCP_WEBSITE_URL + "privacy.html" (both files must actually be
                        served there). Left unset, all three are simply omitted.
MCP_SECURITY_CONTACT    security contact (email or URI) served as /.well-known/security.txt
                        (RFC 9116), e.g. for MCPBundles' ownership verification. Left
                        unset, that path 404s.

デプロイ

独立した Docker コンテナ(docker-compose.yml)として提供されます。TLS を終端し、ローカルホスト上のコンテナのポートに転送するリバースプロキシの背後に配置することを想定しています。デフォルトではリモート/ホスト型であり、ローカルの stdio サーバーではありません。MCP クライアントは URL を指定するだけでよく、ローカルでインストールや実行をする必要はありません。

アクセス制御には、アプリ自体に組み込まれた実際の OAuth 2.1 認可サーバー(app/oauth_provider.py)を使います。前段に HTTP Basic Auth を置く方式ではありません。これは意図的な設計上の選択です。Claude Desktop/claude.ai の「カスタムコネクターを追加」UI は OAuth を前提としており、新しいサーバーに対して常に OAuth のディスカバリーと認可コードフローを試みます。そのため、サーバーの前段に単純な 401(Basic Auth が返すもの)を置くと、「このサーバーは OAuth を必要としている」と解釈され、存在しない /authorize エンドポイントに到達した時点で失敗します。実際の(最小限の)OAuth サーバーを実装することで、「カスタムコネクターを追加」が機能します。

マルチテナントかつセルフサービスです。/authorize はサードパーティーの ID プロバイダーに処理を委任せず、Zotero Library ID、Library Type、API Key の入力を求める独自のログインフォームを表示します。フォームの送信時にキーを Zotero API に直接照会して検証します。検証に成功すると、アクセスを許可すると同時に、そのライブラリをこのサーバーのテナントとして登録(「オンボーディング」)します。個別のサインアップや管理者の承認は必要ありません。どの MCP クライアントも動的に自身を登録できます(RFC 7591)が、実際のアクセス制御は、有効な Zotero キーを使ってログインフォームを完了することで行われます。その後、呼び出し元ごとのツール呼び出しは共有ライブラリではなく、それぞれの Zotero ライブラリにルーティングされます。フロー全体については、app/oauth_provider.py のモジュール docstring を参照してください。登録済みクライアント、発行済みトークン、オンボーディング済みテナントの認証情報(API キーは MCP_TOKEN_STORE_KEY で保存時に暗号化)は、MCP_DATA_DIR(Docker ボリューム)に永続化されるため、再デプロイしても接続済みクライアントがログアウトされたり、オンボーディング済みテナントの情報が失われたりしません。

ホスト上の .env(このリポジトリには含まれません)には MCP_TOKEN_STORE_KEY/MCP_PUBLIC_URL を設定します。これらは docker-compose.yml の env_file: を介して読み込まれます。HTTP デプロイでは ZOTERO_LIBRARY_ID/ZOTERO_LIBRARY_TYPE/ZOTERO_API_KEY は不要です。これらは stdio モードでのみ使用します。

.github/workflows/deploy.yml は、セットアップ済みのホストへの再デプロイを自動化します。手動トリガーのみ(workflow_dispatch)で実行され、プッシュ時には実行されません。最初にテストスイートを実行し、その後 SSH 経由でリポジトリを同期してコンテナを再ビルドします。デプロイ用の SSH キーと接続先のホスト/ポート/ユーザーを、専用の GitHub Actions secrets に設定する必要があります。必要な項目の一覧はワークフローファイルを参照してください。直接/手動アクセスに使うキーではなく、専用のデプロイキーを使用してください。キーが漏えいした場合でも、個別に失効できます。

モニタリング

認証不要の GET エンドポイントが 4 つあります。HTTP モード専用で、すべて /login と同じく $PORT を必要とします。

  • /healthz — ロードバランサー/稼働状況チェック用の、プレーンな 200 OK です。

  • /status — 集約されたプロセスレベルのアクティビティを示す JSON スナップショットです。

    {
      "version": "2.1.0",
      "uptime_seconds": 41213,
      "tenants": 7,
      "tool_calls": {"search_items": 512, "add_tags": 41},
      "tool_errors": {"add_tags": 2}
    }
    

    tenants は TokenStore.tenant_count() で、オンボーディング済みの Zotero ライブラリ数のみを示します。tool_calls/tool_errors は、tools/call ミドルウェア(app/mcp_server.py の _track_tool_call)が記録する、すべてのテナントを合算したツール名ごとの件数です。このレスポンスには、意図的にテナントごと、またはライブラリごとの内訳を一切含めていません。これにより、39 個のツールとは異なり、認証なしで安全に公開できます。カウンターは app/metrics.py にあり、メモリ内にのみ保持されます。再起動/再デプロイのたびにゼロにリセットされます。これはメトリクス/分析システムではなく、「稼働しているか、だいたいどれくらい使われているか」を確認するための軽量な指標です。

  • /status.html — /status と同じデータを小さなページとして表示します(名前とアイコン、バージョン/稼働時間/テナント数の表、ツールごとの呼び出し数/エラー数の表)。スクリプトではなくブラウザーで確認する人向けです。アイコンは /login と同様、MCP_WEBSITE_URL が設定されている場合にのみ表示されます。

  • /.well-known/security.txt — RFC 9116 のセキュリティ連絡先です。MCP_SECURITY_CONTACT が設定されている場合にのみ提供され、それ以外は 404 を返します。Expires はリクエストごとに 1 年後の日付で計算されるため、古くなりません。MCPBundles は、サーバーをディレクトリに公開する際の所有権確認にこれを読み取ります。

  • /.well-known/mcp/server-card.json — 接続前に参照するための情報(サーバー ID、認証要件、スキーマ付きの全ツール一覧)です。実際のツールレジストリからリクエストごとに動的に生成されるため、実際の状態との不整合が生じません。承認済みの標準ではありません。これは SEP-2127(「MCP Server Cards — HTTP Server Discovery」。撤回された SEP-1649 に代わるもの)の実用的な近似です。執筆時点では、まだオープンな未マージの提案です。独自の well-known パスでさえドラフト間で変更されており(一部の改訂版では .well-known/ai-catalog.json が使われています)、仕様への準拠を一般的に主張するものではありません。また、SEP-2127(または後継)が別の仕様で実際に承認された場合、変更が必要になる可能性があります。

ツール

ツールは全部で 39 個あり、リスク別に分類されています(破壊的なツールを使う前に、上記の「主な安全上の注意」を確認してください)。Version の下に ✓ が付いているツールは、引数として対象アイテム/コレクションの現在の Zotero version(search_items/get_item/list_collections から取得)を要求し、それが古い場合は、同時に行われた変更を黙って上書きするのではなく、呼び出しを拒否します。

ツールカテゴリバージョン備考
search_items読み取り専用query は Zotero のクイック検索(タイトル/作成者/年)をデフォルトで使用します。full_text=True を指定すると、添付ファイルやノートのインデックス済みコンテンツにも一致します(qmode="everything")。query は空でない値が必要です。各結果の creators は {creatorType, firstName, lastName}(単一フィールドの作成者または機関の作成者の場合は {creatorType, name})のエントリーのリストです。create_item/update_item が受け付けるものと同じ形式で、役割(著者、編集者、翻訳者など)が保持されます。
get_item読み取り専用creators の形式は上記と同じです。
list_collections読み取り専用
list_tags読み取り専用
list_trash読み取り専用creators の形式は上記と同じです。
list_saved_searches読み取り専用
list_groups読み取り専用id は target_library_type="group" を指定した move_item_to_different_library の target_library_id としても使用できます。
list_item_types読み取り専用
list_item_fields読み取り専用
list_item_type_fields読み取り専用推測するのではなく、create_item/update_item の前に確認してください。指定した item_type が受け付けるフィールドを確認できます。
list_item_creator_types読み取り専用creators エントリーの creatorType についても同様です。
list_creator_fields読み取り専用creators エントリー自体で有効な名前形式のフィールド(firstName、lastName、name など)です。役割を示す list_item_creator_types とは異なります。
list_attachments読み取り専用
get_fulltext読み取り専用
download_attachment読み取り専用コンテンツは content_base64 として返されます。サーバーは呼び出し元のローカルファイルシステムにアクセスできません。
list_notes読み取り専用
export_bibliography読み取り専用項目キーのリストに対し、書式設定済みの HTML 参考文献/引用エントリー(指定した CSL style)またはポータブルなエクスポートデータ(csljson、bibtex)を返します。不明なキーは通知なく省略されます。
create_item安全な書き込み
create_collection安全な書き込み
create_saved_search安全な書き込み
update_collection安全な書き込み✓
update_item安全な書き込み✓
update_publication_status安全な書き込み✓プレプリントから出版済みへの変更では、フィールドと、これに限り item_type をその場で更新します。idempotency_key を受け付けます。上記の「冪等性」を参照してください。
add_tags安全な書き込み✓
remove_tags安全な書き込み✓
set_tags安全な書き込み✓
rename_tag安全な書き込みライブラリ全体が対象です。1 件だけでなく、そのタグが付いたすべての項目に作用します。タグごとのバージョン指定はありません。大規模なライブラリでは処理が停止することがあります。[#6](https://github.com/herbertkokholm/cite-caddy/issues/6)。
add_to_collection安全な書き込み✓
remove_from_collection安全な書き込み✓
trash_item安全な書き込み✓元に戻せる論理削除です。restore_from_trash で復元できます。後で完全に削除するか、ゴミ箱を空にしない限り、Word の引用は壊れません。
restore_from_trash安全な書き込み✓
upload_attachment安全な書き込みコンテンツは content_base64 として送信されます。サーバーは呼び出し元のローカルファイルシステムにアクセスできません。
create_note安全な書き込み
update_note安全な書き込み✓
delete_item_permanently破壊的操作✓Word の引用が壊れます。idempotency_key を受け付けます。上記の「冪等性」を参照してください。
move_item_to_different_library破壊的操作✓移動先のライブラリに新しいキーで項目を再作成してから、元の項目を削除します。Word の引用が壊れます。idempotency_key を受け付けます。この操作では強く推奨されます。上記の「冪等性」を参照してください。
delete_collection破壊的操作✓Zotero の「コレクションを削除」と同様に、下位コレクションも削除します。コレクション内に登録された項目は削除しません。idempotency_key を受け付けます。
delete_tag破壊的操作ライブラリ全体が対象です。1 件だけでなく、そのタグが付いたすべての項目に作用します。タグごとのバージョン指定はありません。idempotency_key を受け付けます。
delete_saved_search破壊的操作リスクは低いです。保存済み検索は保存されたフィルターにすぎず、項目や引用には一切影響しません。idempotency_key を受け付けます。

テスト

uv venv && source .venv/bin/activate
uv pip install -e ".[dev]"
pytest

テストで実際の Zotero ライブラリにアクセスすることはありません。.env に実際の認証情報が設定されている場合も同様です。app/zotero_service.py(Zotero のすべての読み書きロジック)は、tests/fakes.py のインメモリ FakeZotero を使ってテストされます。また、app/mcp_server.py のツール関数は、このフェイクを使った ZoteroService に対して直接テストされます(configure_service() を参照)。

ステータス

v2.3 — デプロイ済みで実運用中です。Zotero Web API のアイテム/コレクション/タグ/ゴミ箱/保存済み検索/スキーマの各機能を網羅する、完全な CRUD 対応を備えています(39 個のツール。ツールを参照)。リモート MCP コネクターとして直接追加できます(例:Claude Desktop/claude.ai で、サーバーの公開 URL だけを指定して「カスタムコネクターを追加」)。上記の OAuth フローでは、ブラウザー内で自分の Zotero Library ID/Type/API Key の入力が求められます。手動でヘッダーを設定する必要はなく、管理者へのサインアップも不要です。

公式の MCP Registry に dk.herbertkokholm.citecaddy/cite-caddy として登録されています。メタデータは server.json にあり、mcp-publisher で公開され、citecaddy.herbertkokholm.dk に対する DNS 検証が済んでいます。GitHub の別個の、手動で管理される github.com/mcp ディレクトリには(まだ)登録されていません。こちらはオープンレジストリから自動同期されません。

既知の制限事項

MCP の2026-07-28 仕様(「ステートレスコア、エンタープライズ認可、拡張フレームワーク」)に対する未対応箇所を記録しています。現時点で悪用可能なものやユーザーに見えるものはありません。いずれも、上流の mcp SDK が変更されるまで機能しないか、すでに緩和済みですが、把握済みの事項として明示するため、ここに記載しています。

  • OAuth の iss パラメーター(RFC 9207)は送信されますが、通知されていません。 complete_login()(app/oauth_provider.py)で生成される最終的な認可リダイレクトには iss が含まれており、混同攻撃に対するフローの安全性を高めています(RFC 9207 §2)。対応する authorization_response_iss_parameter_supported フラグは /.well-known/oauth-authorization-server で設定されていません。このドキュメントは mcp SDK が生成しますが、mcp 2.3.0 の時点ではこのフラグを設定する手段がありません。このフラグがなくても、クライアントに iss の検証は義務付けられませんが、検証するクライアントはその保護を受けられます。

  • CIMD ではなく Dynamic Client Registration(RFC 7591)を使用しています。 同じ仕様の更新では、Dynamic Client Registration は正式に非推奨となり、Client ID Metadata Documents(CIMD)が推奨されています。ただし、後方互換性のため DCR は引き続き機能します。このサーバーのクライアント自動プロビジョニング(app/oauth_provider.py の _FlexibleClientInformation/register_client/get_client)は DCR に基づいています。一部の MCP クライアント(確認済み:Claude Desktop/claude.ai)が登録を省略し、未登録の client_id を直接 /authorize に送信するため、これが必要です(このクラスの docstring を参照)。インストール済み SDK で警告なく DCR が機能する間は対応不要です。変更があった場合は、CIMD ベースの代替実装が必要になります。

  • rename_tag は大規模なライブラリでブロックする可能性があります — #6 で追跡中です。仕様の tasks 拡張機能の候補です。mcp 2.3.0 には汎用拡張フレームワークがありますが、Tasks の実装はまだありません。

貢献

CONTRIBUTING.md を参照してください。

セキュリティ

脅威モデルと脆弱性の報告方法については、SECURITY.md を参照してください。

プライバシー

/login でサインインしたときにサーバーが保存する情報(Zotero Library ID/Type/API key)、その保護方法、削除を依頼する方法については、プライバシーポリシーを参照してください。

ライセンス

MIT

コメント

MCP は初めてですか?

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

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