pydantic-zotero-mcp
pydantic-zotero-mcp
AIエージェントにZoteroライブラリへの読み取りアクセスを提供するMCPサーバーです。検索、アイテムのメタデータ、コレクション、タグ、研究者自身のノート、添付PDFのインデックス化された全文テキストにアクセスできます。
要件は PRD.md を参照してください。
ステータス: M1(読み取りコア)+ M2(全文)を実装済み。 引用フォーマットとエクスポート(M3)、プロンプト(M4)、書き込みツール(M5)はまだ未実装です — 未実装 を参照してください。
インストール
ツールとして(pipx)
zotero-mcp コマンドを専用の分離環境にインストールします:
pipx install pydantic-zotero-mcp # or: pipx install /path/to/checkout
zotero-mcp --help別のプロジェクトの環境へ
uv add pydantic-zotero-mcp # or: uv pip install pydantic-zotero-mcpこのサーバーの開発用
git clone https://github.com/jmlon/pydantic-zotero-mcp
cd pydantic-zotero-mcp
uv sync # creates ./.venv from this project's own lock file
uv run pytest
uv run ruff checkRelated MCP server: zotero-cli-cc
設定
https://www.zotero.org/settings/keys から読み取り専用のAPIキーと数値のユーザーIDを取得してください。ライブラリIDはユーザー名ではなく数値です。
export ZOTERO_API_KEY=...
export ZOTERO_LIBRARY_ID=123456 # numeric
export ZOTERO_LIBRARY_TYPE=user # or group変数 | デフォルト | 目的 |
| — | Web APIキー( |
| — | 数値のユーザーIDまたはグループID |
|
|
|
|
| 代わりにZotero 7デスクトップAPIを読み取る: キー不要、レート制限なし、読み取り専用 |
|
| M5用に予約済み。書き込みツールはまだ存在しない |
|
| 全文テキストのデフォルト上限。呼び出しごとの |
|
| M3用に予約済み |
|
| 上流へのリクエスト上限(Zoteroは≤4を推奨) |
|
|
|
|
| HTTPバインドアドレス |
|
| HTTPポート |
|
| HTTPマウントパス |
| — | Bearerトークン。HTTPでは必須 |
CLIフラグは環境変数を上書きします。
実行
インストール後は、zotero-mcp がエントリポイントになります。インタープリタのパスも python -m も、正しい作業ディレクトリの指定も不要です。これはMCPクライアントの command: が求めるものです:
# stdio (default) — an agent launches this as a subprocess
zotero-mcp
# streamable HTTP — requires ZOTERO_MCP_AUTH_TOKEN
ZOTERO_MCP_AUTH_TOKEN=secret zotero-mcp --transport http --port 8000
# read the Zotero desktop app instead of the web API
zotero-mcp --localチェックアウトから、インストールせずに python -m zotero_mcp でも動作します:
uv run python -m zotero_mcp--transport http でトークンなしで起動すると、未認証のまま提供するのではなく終了コード2で終了します。これは個人ライブラリへの読み取りチャネルだからです。
インメモリ(エージェントプロセスに埋め込む)
サブプロセスもソケットもありません。設定は注入されるため、ホストは環境変数を必要としません:
from fastmcp import Client
from zotero_mcp import ZoteroSettings, create_server
server = create_server(
ZoteroSettings(
api_key=key,
library_id="123456",
library_type="user",
)
)
async with Client(server) as client: # lifespan opens here
result = await client.call_tool("search_items", {"query": "attention"})
print(result.structured_content["items"]) # dict; result.data is a modelzotero_mcp のインポートには副作用がありません。設定の読み取りも、クライアントの構築も、ネットワークアクセスもありません。これが埋め込みを可能にしています。これを検証するテストもあります。
エントリポイントによる検出
Pythonのエントリポイントを通じてバンドルされたMCPサーバーを検出するホストアプリケーション向けに、このパッケージは deep_research.mcp_servers グループにエントリポイントを宣言しています:
[project.entry-points."deep_research.mcp_servers"]
zotero = "zotero_mcp:build_server"build_server() は引数を取らず、環境から設定を取得します。このパッケージをホストの環境にインストールすると、ホストは設定ファイルからパスで何かをインポートすることなく、zotero という名前でサーバーを解決し、プロセス内で実行できます。
自動化ホスト向けのチューニングメモ: このサーバーのデフォルトの全文上限は100,000文字(単一の get_item_fulltext 呼び出しで約25〜30kトークン)です。インタラクティブ利用には十分すぎるほどですが、トークン予算内で多数の呼び出しを行うエージェントには大きすぎます。呼び出しごとに小さめの max_chars を渡すか、ZOTERO_FULLTEXT_MAX_CHARS を下げてください。
ツール
ツール | 目的 |
| サイズ、モード、権限。低コストな方向確認用呼び出し — 最初に使うこと |
| 主要なエントリポイント。 |
| 最近追加されたアイテムを新しい順に一覧表示 |
| DOI、ISBN、arXiv ID、またはキーで「これはもう持っているか?」を確認 |
| 完全なメタデータ。 |
| 添付ファイルとノート。添付ファイルごとに |
| 研究者自身のノート。HTMLは除去済み |
| インデックス化された添付ファイルのテキスト。親 → 添付ファイルを解決 |
| 入れ子になったコレクションツリー |
| 1つのコレクション内のアイテム |
| タグの語彙。オプションでプレフィックスフィルタリング |
リソース: zotero://library/info、zotero://collections、zotero://items/{key}、zotero://items/{key}/fulltext、zotero://collections/{key}/items、zotero://schema/item-types、zotero://schema/item-types/{type}/fields。
設計メモ
要点はプロジェクションです。 生のZotero JSONは、links、library、meta、空の型フィールドを含めてアイテムあたり約1KBあります。zotero_mcp/projection.py は、25アイテムのページを推定トークン約6,100から約2,400(生の39%)に削減し、PRDの4,000予算を下回ります。Nullフィールドは CompactModel によってシリアライズ時に削除されます。
pyzoteroは同期型でステートフルです。 Zotero.request と Zotero.links は呼び出しのたびに上書きされ、Total-Results はその後インスタンスから読み戻されます。そのため、共有クライアントを同時に使うと、別の呼び出しの合計値が報告される可能性があります。gateway.py は最大 ZOTERO_MAX_CONCURRENCY 個のクライアントのプールを保持し、操作ごとに1つをチェックアウトし、そのクライアントを保持する同じワーカースレッド内でレスポンスメタデータを読み取ります。すべての呼び出しは anyio.to_thread.run_sync を経由するため、イベントループがブロックされることはありません。
バックオフはpyzoteroの役割です。 pyzotero ≥ 1.13 はすでに Backoff / Retry-After を尊重し、429を内部的にリトライするため、ゲートウェイはそれを再実装しません。ゲートウェイは、一時的なトランスポート障害と5xxエラーのみに対して、上限付きの3回リトライを追加します。
何も黙って切り詰められません。 検索は total_matched、truncated、next_start を報告し、全文は total_chars と truncated を報告します。
結果は判定ではなく候補です(PRD D3)。find_item_by_identifier は matched_on(key / doi / title / identifier / none)に加えて、信頼度とすべての妥当な候補を返します。プレプリントとその公開版の両方が残ります。フィルタリングは呼び出し側が行います。
PRDからの逸脱
実装中に行った判断であるため、知っておく価値があります:
モジュールレベルの
mcpオブジェクトはありません。 PRD 7.2は、モジュールレベルのmcp = create_server()とインポート時の副作用がないことの両方を要求していました。これらは矛盾します。サーバーの構築は設定を検証するため、モジュールレベルのインスタンスはZoteroの環境変数がないマシンではImportErrorを発生させ、サポートするはずだったインメモリパスを壊します。存在するのはcreate_server()/build_default_server()のみです。書き込みツールは
enabled=Falseではなく、条件付きで登録されます。 PRD 5.5は@mcp.tool(enabled=False)を指定していましたが、FastMCP 3.xにはenabledキーワード引数がなく、無効でも一覧に載るツールはコンテキストを消費します。M5が実装されたら、書き込みツールはZOTERO_ALLOW_WRITES=trueの場合を除いて単に登録されません。このサーバーは FastMCP 3.x を対象としています。ここでのコードを形作る3.x固有の点が2つあります: デコレータから
enabledがなくなったことと、result.dataが生成されたpydanticモデルである一方、result.structured_contentはプレーンなdictであることです。テストは後者を検証しており、これによりワイヤー上でのnull省略も確認されます。has_fulltextは3値です。 PRD 6ではboolと型指定されていましたが、親アイテムについてそれを判定するにはアイテムごとに別途childrenリクエストが必要になり、25アイテムの検索が26リクエストになってしまいます。アイテムに子がまったくない場合はFalse、添付ファイルおよびget_item(include_children=True)の後はTrue/False、未確定の場合はnull(省略)です。ItemSummary.num_childrenが低コストなシグナルを提供します。find_item_by_identifierはItemSummary | NoneではなくCitationMatchを返します。 D3に従ったものです。以前のシグネチャは、その決定がクライアントに移したまさに同一性の呼び出しを行っていました。matched_onにはkeyとidentifierが追加されました。 PRDの4つの値に加えて、正確なキーの一致と弱い検索の一致を区別するためです。list_recent_items(since_days=...)はローカルでフィルタリングします。 Zoteroにはサーバーサイドの日付フィルタがないため、狭い期間ではlimitより少ないアイテムが返されることがあります。レスポンスのhintがその発生を示します。
テスト
uv run pytest # 80 passedテストスイートは、pyzoteroのインスタンスからメタデータを読み取る動作を再現する FakeZotero に対して、FastMCPのインメモリトランスポートを使用します。ネットワークもサブプロセスも実際の認証情報もありません。カバレッジ: スキーマ表面、プロジェクションとトークン予算、ページネーションと切り詰めの報告、全文上限と親の解決、マッチ再現率(プレプリント/公開版のペアが両方返されること)、エラーメッセージの品質、トラバーサル試行を含むリソーステンプレート検証、設定検証、CLIの優先順位、ゲートウェイのリトライ/キャッシュ、そしてパッケージのインポートがネットワークに触れると失敗するインポート純度チェック。
Not yet implemented
M3 —
format_citation、format_bibliography、export_itemsM4 — 4つのプロンプト(
literature_review、find_related_work、check_citations、summarize_reading)、LogfireインストルメンテーションM5 — 書き込みツール(
create_item、update_item_fields、add_item_tags、add_items_to_collection、create_note)。バージョンチェック付きPATCHセマンティクス。削除は恒久的にスコープ外。
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- AlicenseAqualityCmaintenanceA lightweight MCP server that connects AI agents to a local Zotero library for paper management and metadata retrieval. It enables users to search titles and abstracts, browse collections, and automatically ingest papers via arXiv ID or DOI with PDF attachments.815MIT
- AlicenseNot gradedqualityAmaintenanceMCP server that exposes 45 tools for Zotero reference management, enabling AI agents to read/write items, search, extract PDF text, and manage workspaces via the Zotero CLI.198AGPL 3.0
- AlicenseNot gradedqualityAmaintenanceMCP server that lets AI assistants search, create, organize, and cite from a Zotero library.3MIT
- AlicenseNot gradedqualityDmaintenanceMCP server that connects AI assistants to your Zotero library, enabling full-text PDF extraction and metadata search.MIT
Related MCP Connectors
Remote MCP server for full read/write access to a Zotero library
Agentic search over your Dewey document collections from any MCP-compatible client.
An MCP server that gives your AI access to the source code and docs of all public github repos
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/jmlon/pydantic-zotero-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server