mcpField
L
lit-sync

lit-sync

github.com/aperivue生産性353108 回インストール1 日前に更新
リポジトリ

lit-sync について

lit-syncは、BibTeXの文献をZoteroと同期し、Obsidianに文献ノートを作成するスキルです。研究プロジェクトの引用を管理するオーナー向けで、Better BibTeXによる引用スナップショットの更新確認や、依頼時の全文取得にも対応します。

主な機能

  1. 01BibTeXエントリーの解析
  2. 02重複を避けたZoteroへの同期
  3. 03Better BibTeXスナップショットの確認
  4. 04Obsidianで文献ノートを作成
  5. 05依頼に応じた全文取得とレポート

活用シーン

  1. 01検索文献をプロジェクトのZoteroコレクションへ追加
  2. 02refs.bibスナップショットの更新確認
  3. 03論文をObsidianノートに整理
  4. 04依頼に応じた論文全文の取得と報告

MCP 対応

Zotero MCPサーバーを使って文献を検索・追加し、コレクションを管理できます。未接続の場合は同期フェーズをスキップします。

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/aperivue/medsci-skills --skill lit-sync

インストール後、Claude Code で/lit-syncを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

skill.yml
references/
bbt_lookup.md
concept_note_template.md
fulltext_report.md
references/locale/ko/
note_templates.md
scripts/
check_citekey_provenance.py
tests/
citekey_provenance_challenge_selftest.sh
citekey_provenance_challenge.sh
test_poll_logic.sh

詳細

Literature Sync: Zotero + Obsidian パイプライン

/search-lit の出力である .bib ファイル(またはユーザーが指定した任意の .bib ファイル)を取得し、参考文献を Zotero ライブラリと Obsidian の文献ノートに同期します。また、文献ノートが十分に蓄積されたら、複数の文献にまたがる概念ノートを抽出します。

前提条件

  • プロジェクトオーナーのみが対象です。 コラボレーターは、コミット済みの manuscript/_src/refs.bib スナップショットを読み取り専用で利用します。現在のユーザーがコラボレーター(SSOT.yaml の reference_manager.required_for により Zotero にアクセスできない)の場合は、処理を中止し、原稿内の [@NEW:topic] プレースホルダーにフラグを付けてオーナーに知らせるよう案内してください。
  • Zotero デスクトップ 7.x と Better BibTeX プラグインを用意し、Better BibTeX の「Keep updated」自動エクスポート先を <project>/manuscript/_src/refs.bib に設定してください。
  • Zotero MCP サーバー(接続されていない場合は Phase 2 をスキップしてください。Zotero を再度開くと自動エクスポートで更新されます)。
  • Obsidian CLI、または vault への直接ファイル書き込みを使用します。vault のパスはユーザーの環境から取得します(例:$OBSIDIAN_VAULT)。

成果物の契約

/lit-sync は、manuscript/_src/refs.bib(Better BibTeX の自動エクスポートトリガー経由)、references/zotero_collection.json、および references/fulltext_retrieval.json(Phase 2.7)の唯一の書き込み元です。refs.bib を直接書き込んではいけません。Zotero への接続がない場合を補うためであっても同様です。ライブラリと同期した状態を保てるのは Better BibTeX のエクスポートだけなので、自動エクスポートが機能していない場合は Zotero の設定を修正してください。refs.bib を直接手編集すると不整合が生じます。見つけたらすぐに元に戻してください。


Phase 1: BibTeX の解析

ユーザーが指定した .bib ファイル、または /search-lit が直前に生成した references/library.bib を正規表現で解析します。各エントリから citekey、doi、pmid、title、authors(少なくとも名と姓)、journal、year、存在する場合は volume/number/pages を抽出します。解析に失敗したものは記録し、そのエントリをスキップします。後続の Phase で使用する書誌データはすべて、これらのエントリまたは API の応答から取得します。

解析した citekey は、まだライブラリキーではありません。/search-lit の候補キー(Kim_2024_Validation)は仮のものであり、Phase 2 でアイテムを追加するときに Better BibTeX が実際のキーを生成します。キーを自分で作成してはいけません。ノートのキーの取得元については、Step 3.2 §Citekey provenance を参照してください。


Phase 2: Zotero 同期

Zotero MCP が接続されていない場合は、この Phase をスキップします(それでも Step 2.3 のファイルに status: "skipped" を書き込みます)。その後、Phase 3 に進みます。

Step 2.1: プロジェクトのコレクションを特定

現在の作業ディレクトリ、またはユーザーが明示的に指定した上書き設定からプロジェクトを特定します。記録済みのコレクションキーがあれば再利用します。ない場合は、Zotero の既存コレクションからプロジェクトのものを確認し、存在しなければ zotero_create_collection で作成します。コレクションキーを記録し、新しいコレクションを作成した場合はユーザーに知らせます。

Step 2.2: 重複排除と追加

各エントリに対して:

  1. DOI またはタイトルで zotero_search_items を使って検索し、すでに存在する場合はスキップします。 重複を防ぐには、まず検索することが重要です。zotero_add_by_doi は単独では重複排除を行いません(CrossRef から情報を取得してアイテムを作成します)。そのため、検索を省略してはいけません。
  2. それ以外の場合、DOI があれば zotero_add_by_doi を呼び出します。DOI がなく PMID のみがある場合は、PubMed の URL を指定して zotero_add_by_url を呼び出します。DOI も PMID もないエントリは追加しません。失敗として数え、ユーザーに手動で追加するよう依頼します。
    • zotero_add_by_doi は attach_mode 引数を受け取り、追加時に OA の子 PDF を添付する試みを制御します(インストール済みのサーバーでは linked_url は「PDF の URL をブックマークする」という意味であり、その他の値ではダウンロード/インポートします)。正確な受け入れ値はサーバーのバージョンによって異なるため、接続中のサーバーで確認してください。ここで追加したアイテムに PDF を添付するために zotero_add_from_file を使ってはいけません。親アイテムを指定する引数がなく、親アイテムが重複して作成されるためです。
  3. zotero_manage_collections を使って、アイテムをプロジェクトのコレクションに追加します。
Step 2.3:結果レポート
Zotero Sync:
  Added:     8 papers (new)
  Skipped:   3 papers (already in library)
  Failed:    1 paper (no DOI/PMID)
  Collection: RFA-Meta (TZQEP4NH)

プロジェクトのワークスペースに、必ず references/zotero_collection.json を書き込んでください。

{
  "schema_version": 1,
  "status": "synced",
  "collection": "RFA-Meta",
  "collection_key": "TZQEP4NH",
  "added": 8,
  "skipped": 3,
  "failed": 1
}

Zotero が利用できない場合は、同じファイルに status: "skipped" と、人が読める形式の reason を書き込んでください。


Phase 2.5:refs.bib スナップショットの更新確認

Better BibTeX の「Keep updated」自動エクスポートでは、通常、Zotero の変更から数秒以内に manuscript/_src/refs.bib が更新されます。このフェーズでは、後続のスキルが利用する前に、スナップショットが実際に更新されたことを確認します。

Step 2.5.1:パスの解決

SSOT.yaml → truth.refs_bib を読み取ります。デフォルトは manuscript/_src/refs.bib です。存在しない場合(レガシープロジェクト)は manuscript/_src/refs.bib にフォールバックし、SSOT への移行を推奨する WARN を出力します。

Step 2.5.1b:前提条件の確認(早期終了。ポーリングは行わない)

Step 2.5.2 の10秒間のポーリングループに入る前に、両方の前提条件を確認します。いずれかを満たさない場合、解消されないタイムアウトを待つのではなく、セットアップ手順を示して Phase 2.5 を中止します。

  1. Better BibTeX から応答があること。 ディスク上のファイルではなく、実行中のプラグインをプローブします。

    curl -s -m 5 -o /dev/null -w "%{http_code}" \
      http://127.0.0.1:23119/better-bibtex/json-rpc    # expect 200
    

HTTP 200 以外の応答は、Zotero が終了しているか、BBT の起動が完了していないことを意味します。Zotero のウィンドウが表示された後に一度だけ再試行してください。BBT はアプリの起動から数秒後にエンドポイントを登録します。

⚠️ ~/Zotero/better-bibtex/read-only.json を条件にしないでください。 現行の BBT リリースでは、自動エクスポートの登録情報は独自のストアに保存されるため、正常なインストール環境でもこのファイルが [] になるのはよくあることです。これを「未設定」と見なすと、正常に動作している環境でこのフェーズがスキップされ、古い refs.bib と作り上げた citekey が原稿に入り込む原因になります。

失敗した場合は、次を出力します。

Phase 2.5 をスキップしました:Better BibTeX が 127.0.0.1:23119 で応答しませんでした(HTTP <code>)。Zotero を開き、読み込みが完了するまで待ってから、もう一度 /lit-sync を実行してください。

  1. 対象の refs.bib が存在すること。 Step 2.5.1 で解決した truth.refs_bib のパスがディスク上に存在する必要があります(空でも構いません。BBT が上書きします)。失敗した場合は、次を出力します。

    Phase 2.5 をスキップしました:対象のスナップショット <path> が見つかりません。BBT の自動エクスポートを「On Change」で SSOT のパスに設定してから、もう一度実行してください。

いずれかの理由で早期終了する場合、Step 2.5.3 の JSON で refs_bib_refreshed: false と reason: "precondition:<which>" を設定し、ユーザーに伝えて呼び出し元に制御を戻します。後続処理ではこのフラグはチェックされないため、ユーザーへの通知が古い refs.bib と原稿の間に立つ最後の防波堤になります。

Step 2.5.2:更新の確認

Phase 2 でアイテムを追加した後、次の操作を行います。

  1. Zotero が書き込む前に stat -f "%m" manuscript/_src/refs.bib を実行し、値を記録します。
  2. 最大10秒間待機します(Better BibTeX のデバウンス時間)。mtime をポーリングします。
  3. 10秒後も mtime が変わらない場合:
    • Zotero が起動していることと、BBT のエクスポートが「Keep updated」になっていることを確認するようユーザーに促します。
    • BBT の自動エクスポート先が誤っている場合は、期待されるパス(<project>/manuscript/_src/refs.bib)を出力します。
    • 最後の手段として、手動エクスポートを提案します:File → Export Library → Better BibTeX → target path
  4. mtime が進んだら、新しく追加した citekey を grep で検索します。すべて存在する必要があります。1つでも欠けている場合は失敗として報告してください(項目を捏造してはいけません)。
Step 2.5.3:zotero_collection.json への記録

Step 2.3 で書き込んだ JSON に次を追加します。

{
  "refs_bib_path": "manuscript/_src/refs.bib",
  "refs_bib_mtime": "2026-04-24T14:32:11Z",
  "refs_bib_refreshed": true,
  "citekeys_verified": ["smithDeepLearningRadiology2024", "..."]
}

更新に失敗した場合は refs_bib_refreshed: false を設定し、reason を含めてください。


Phase 2.7:全文の取得(オプトイン、所有者のみ)

ユーザーが全文を求めた場合に限り実行します(例:「PDF をダウンロードして」「全文を取得して」、またはその意図が示された作業リスト)。デフォルトの /lit-sync はメタデータのみを扱い、ネットワークへのアクセスを最小限に抑えます。このフェーズを自動実行してはいけません。Zotero へのアイテム追加(Phase 2)とスナップショットの確認(Phase 2.5)の後、Obsidian のノート作成(Phase 3)の前に実行します。

以下の両方の方法を提示し、1つのレポートにまとめてください。全文はこれらの方法でのみ取得してください。認証済みブラウザーセッションを自動操作したり、ペイウォールやアクセス制御を回避したり、このスキルに機関プロキシ、認証情報、ホストをハードコードしたりしてはいけません。どちらの方法でも取得できない資料は、機関アクセス、図書館間貸出、または著者への問い合わせに案内してください。

ルート A — ディスク上の OA PDF(後続のスキル向け)

/fulltext-retrieval エンジンに委譲してください(OA カスケードを再実装したり、そのコードを インポートしたりせず、パスを指定して呼び出してください)。

ENGINE="${CLAUDE_SKILL_DIR}/../fulltext-retrieval/fetch_oa.py"
python3 "$ENGINE" <worklist> -o pdfs/ -e <contact-email> --report pdfs/retrieval_report.json

<worklist> は DOI/PMID(/Title)のリストです。Phase 1 の .bib の DOI、スタンドアロンモードで 指定されたワークリスト、またはプロジェクトコレクションの DOI が該当します。出力:/meta-analysis と pdf_to_md.py 用の pdfs/*.pdf、および pdfs/retrieval_report.json(スキーマ 2:取得の status/source、source_identity、file_sha256)。ファイルの有無と、そのファイルの同一性の 評価は区別してください。

ルート B — ライブラリ内の PDF(Zotero ネイティブ、高い取得率、プロキシ対応)

${CLAUDE_SKILL_DIR}/../fulltext-retrieval/references/find_available_pdf.js を出力し、ユーザーがプロジェクトコレクションを選択した状態で Zotero の ツール → 開発者 → JavaScript を実行 に貼り付けられるようにします。Zotero 自身の addAvailablePDF/addAvailablePDFs を実行するため、ユーザーの OpenURL リゾルバー/機関プロキシが 再利用されます。そのため、OA のみの場合よりも通常は多くの PDF を取得できます。一方、このスキルに 認証情報や機関識別子が渡ることはありません。コードを使わない場合は、右クリック →「利用可能な PDF を検索」を 選択します。出力された JSON から {attached, missing} の概要を記録してください。

レポート

ルート A の pdfs/retrieval_report.json(およびユーザーから報告されたルート B の概要)を references/fulltext_retrieval.json に統合し、references/zotero_collection.json に短い fulltext 件数ブロックを追加してください。書き込む前に ${CLAUDE_SKILL_DIR}/references/fulltext_report.md を読み、スキーマと同一性の証拠に関するルールを確認してください (取得したファイルが論文の確認済みを意味するわけではありません。consistent ステータスは参考情報であり、 未評価の項目は identity_review_needed に入れます)。


フェーズ 3:Obsidian の文献ノート

ノートを作成する前に、Vault の既存の構成を確認してください。Vault にすでにフォルダー構成がある場合 (韓国語のものも含む)は、その構成を尊重してください。ユーザーのフォルダー名を黙って変更してはいけません。新規または 構成が不明な Vault では、英語のフォルダー Literature/ と Concepts/、および英語のテンプレート (ステップ 3.2、ステップ 4.3)をデフォルトとして使用してください。韓国語のフォルダー構成の Vault、または韓国語の ノートを希望するユーザーには、韓国語のフォルダー構成と、韓国語の見出しを使ったテンプレートを ${CLAUDE_SKILL_DIR}/references/locale/ko/note_templates.md から使用してください (ハブノート名は Vault 独自のものを使用します)。

ステップ 3.1:既存の文献ノートを確認する
# Default English layout; substitute the vault's existing folder if one is present
# (e.g. "02 연구/문헌/" for a Korean-structured vault — see references/locale/ko/note_templates.md).
ls "$VAULT/Literature/" | grep -v "📊" | wc -l
ステップ 3.2:文献ノートを作成する

各 .bib エントリについて、Literature/{citekey}.md(または Vault に既存の文献フォルダー)を作成してください。 ファイルがすでに存在する場合はスキップし、上書きしないでください。ユーザーがハイライトや個人的なメモを 追加している可能性があります。

citekey の出所 — ノートのファイル名はライブラリについての主張です

ノートのファイル名と citekey: フィールドは、そのキーを持つエントリが Zotero に存在すると示すものです。原稿内の [@key]、ノート間の [[key]]、Zotero Integration プラグインの {{citekey}}.md はすべてこれに依存しており、何にも解決されないキーでも正しく見えてしまいます。そのため、キーは読み取り、決して生成しません。

  1. Better BibTeX からエクスポートされた refs.bib のエントリから取得するか、Better BibTeX に問い合わせます(json-rpc 経由の item.search — ${CLAUDE_SKILL_DIR}/references/bbt_lookup.md を参照)。
  2. 論文が Zotero にない場合は、まず追加し(zotero_add_by_doi)、BBT にキーを生成させます。この手順をフェーズ 2 で行うのには理由があります。ライブラリエントリより先に書かれたノートには、正しいかどうかを確かめるキーがありません。
  3. 追加できない場合(DOI がない、オフラインなど)は、citekey: "" とタグ _needs-citekey を付けてノートを書きます。空のフィールドは後から修正できますが、捏造したキーは修正できません。後続の処理では、実在するキーと区別できないためです。

完了前に検証します。

python3 "${CLAUDE_SKILL_DIR}/scripts/check_citekey_provenance.py" --vault "$VAULT" --bib "$REFS_BIB"

INVENTED は、ノートにキーがない一方で、DOI から実在するキーが解決されることを意味します。ここで修正してください。 MISMATCH は、キーは実在するものの、ノートの DOI が別のキーに紐づくライブラリの DOI であるため、別の論文を引用していることを意味します。提示されたキーに合わせて名前を変更してください(既知の制限:ノートの DOI の表記が、引用符や波括弧で囲まれているなど、ライブラリの表記と異なる場合は検出されません)。UNPARSED は、文献ノートのフロントマターが閉じられていないため、キーが読み取られなかったことを意味します。--strict を指定すると、これら 3 つは終了コード 1 を返し、文献ノートが見つからないスキャンは終了コード 2 を返します。ノートと論文の照合にはキーではなく DOI を使い(AMBIGUOUS、UNUSABLE、NO_IDENTIFIER についてはスクリプトの --help を参照)、不足として報告された項目をインポートする前に、ライブラリ全体を検索(--live)してください。

テンプレート
---
notetype: literature
citekey: "{citekey}"
title: "{title}"
authors: "{authors}"
journal: "{journal}"
year: {year}
doi: "{doi}"
pmid: "{pmid}"
created: "{today}"
tags:
  - type/literature
  - _unread
---

# {title}

## Bibliographic info
- **Authors**: {authors}
- **Journal**: {journal}{volume_issue_pages}
- **Year**: {year}
- **DOI**: [{doi}](https://doi.org/{doi})
{pmid_line}

## Key points (in my own words)

## My thoughts

## Related notes
- [[Research Hub]]
- [[Papers & Reviews]]
-
-

ルール:

  • notetype: literature — Zotero Integration テンプレートと互換性があります。
  • _unread タグ — Zotero でユーザーが PDF を読み、ハイライトを追加した後で、_read に変更します。
  • ## Key points と ## My thoughts は空欄のままにします — ここにはユーザー自身が記入します。
  • ## Related notes には、ハブへのリンク 2 件と空の枠 2 つが含まれます(後でコンセプトノートとリンクするための予約枠です)。
  • PMID がある場合は、PubMed へのリンクを追加します。
ステップ 3.3:結果レポート
Obsidian Literature Notes:
  Created:   8 notes (new)
  Skipped:   3 notes (already exist)
  Location:  Literature/
  Total in vault: 12 literature notes

フェーズ 4:コンセプト抽出(条件付き)

実行条件

Vault に文献ノートが 10 件以上ある場合にのみ、このフェーズを実行します。それより少ない場合は、「文献ノート N 件 — コンセプト抽出は 10 件以上で利用可能」のようなステータスメッセージを出力して停止します。

ステップ 4.1:分野横断的なコンセプトのスキャン

Literature/*.md(または Vault の既存の文献フォルダー)内のすべてのファイルを読み、各論文のタイトル、ジャーナル、タグからキーワードを抽出し、.bib エントリのタイトルから主要なコンセプトを抽出して、3 件以上の文献ノートに共通して現れるコンセプトを特定します。

ステップ 4.2:フィルタリング(5 つの除外ルール)

コンセプト候補から、モデル名(GPT-4、Claude など)、データセット名(MedQA、ImageNet など)、ジャーナル名、機関名、一般的すぎる手法名を除外します。残ったものをコンセプトノートの候補とします。

ステップ 4.3:コンセプトノートの下書き

${CLAUDE_SKILL_DIR}/references/concept_note_template.md のテンプレート(韓国語構成の Vault の場合は references/locale/ko/note_templates.md 内のコンセプトテンプレート)を使って、Concepts/{concept name}.md(または Vault の既存のコンセプトノート用フォルダー)を作成します。

主なルール:

  • ## Definition を自動入力しないでください — > TODO マーカーを残します。第 2 層のノートは、ユーザーが自分の言葉で定義を書いて初めて意味を持ちます。
  • status は常に 🌱Seedling から始めます。
  • ## Related notes の下に、少なくとも 4 件のウィキリンクを記載します(Vault の規約)。
ステップ 4.4:ユーザーに提案する
Concept-note candidates (≥3 papers cross-referenced):
  1. {Concept A} (4 papers)
  2. {Concept B} (3 papers)
  3. {Concept C} (5 papers)

Create? (all / selected / skip)

下書きは自動で作成しますが、ユーザーの確認後にのみ作成します。


スタンドアロンモード

このスキルは、新しい .bib ファイルがなくても実行できます。

概念抽出のみ

明示的な概念抽出の依頼があった場合は、既存の Literature/*.md(またはVault内の既存の文献フォルダー)を確認し、Phase 4のみを実行します。

参考文献の整理

「このプロジェクトの参考文献を整理して」という依頼があった場合は、ワークスペース内の .bib ファイルを探し、Phase 1–3を実行します。

Zoteroの同期のみ

「Zoteroを同期して」という依頼があった場合は、Zoteroコレクションと .bib ファイルを比較し、不足している項目を追加します。

PMIDリストの取り込み(.bibなし)

ユーザーからPMIDのリスト(HANDOFFや同僚から提供されたものなど)を受け取った場合は、まずPubMed esummaryを使ってPMIDをDOIに解決し、そのDOIを使ってPhase 2に進みます。

PMIDS="12345,67890,..."
curl -s "https://eutils.ncbi.nlm.nih.gov/entrez/eutils/esummary.fcgi?db=pubmed&id=${PMIDS}&retmode=json" \
  | jq -r '.result | to_entries[] | select(.key != "uids") | "\(.value.uid)\t\(.value.elocationid)\t\(.value.title)"'

ライブラリにすでにある項目(Step 2.2の検索で見つかったもの)は、再追加せずに zotero_manage_collections を使ってプロジェクトコレクションに追加します。URLやPubMed URLを使って再追加すると、検索時の重複排除を迂回して重複が作成されるためです。references/zotero_collection.json には、added と existing の両方の項目を記録します。PubMedにDOIがないPMID(古い論文やインデックス未登録の論文)の場合は、zotero_add_by_url にPubMed URLを渡す方法に切り替え、エントリに no_doi: true と記録します。

作業リストの取り込み(DOI/PMID/Title、.bibなし)

ユーザーから作業リストファイル(DOI 列と、任意の PMID/Title を含む .tsv/.csv/.md の表、またはDOIを1行ずつ並べたリスト。たとえばSRの採用文献セット)を受け取った場合は、そのファイルから直接Phase 2に進みます。PMIDのみの行はDOIに解決してから(上記のesummaryを参照)、検索による重複排除と追加のループを実行します。同じ作業リストファイルをPhase 2.7 Route Aにも使用します(fetch_oa.py は .tsv/.csv/.md/プレーンテキストをそのまま読み込むため、形式の変換は不要です)。

コメント