zra-mcp 0.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. zra_mcp-0.3.0/.env.example +79 -0
  2. zra_mcp-0.3.0/.github/workflows/ci.yml +39 -0
  3. zra_mcp-0.3.0/.gitignore +58 -0
  4. zra_mcp-0.3.0/CHANGELOG.md +218 -0
  5. zra_mcp-0.3.0/CLAUDE.md +119 -0
  6. zra_mcp-0.3.0/DEVELOPMENT_LOG.md +8 -0
  7. zra_mcp-0.3.0/DEVELOPMENT_PLAN.md +148 -0
  8. zra_mcp-0.3.0/LICENSE +21 -0
  9. zra_mcp-0.3.0/PKG-INFO +436 -0
  10. zra_mcp-0.3.0/README.md +394 -0
  11. zra_mcp-0.3.0/README_zh.md +389 -0
  12. zra_mcp-0.3.0/docs/DEVELOPMENT_LOG.md +658 -0
  13. zra_mcp-0.3.0/docs/DEVELOPMENT_LOG_EN.md +488 -0
  14. zra_mcp-0.3.0/docs/cherry-studio-setup-en.md +593 -0
  15. zra_mcp-0.3.0/docs/cherry-studio-setup.md +606 -0
  16. zra_mcp-0.3.0/docs/test-prompts.md +451 -0
  17. zra_mcp-0.3.0/project_a_mcp/__init__.py +1 -0
  18. zra_mcp-0.3.0/project_a_mcp/server.py +1944 -0
  19. zra_mcp-0.3.0/pyproject.toml +76 -0
  20. zra_mcp-0.3.0/research_core/__init__.py +3 -0
  21. zra_mcp-0.3.0/research_core/parsers/__init__.py +12 -0
  22. zra_mcp-0.3.0/research_core/parsers/chunker.py +1014 -0
  23. zra_mcp-0.3.0/research_core/parsers/pdf.py +51 -0
  24. zra_mcp-0.3.0/research_core/parsers/section_detector.py +338 -0
  25. zra_mcp-0.3.0/research_core/parsers/text_cleaner.py +526 -0
  26. zra_mcp-0.3.0/research_core/rag/__init__.py +6 -0
  27. zra_mcp-0.3.0/research_core/rag/bm25_index.py +273 -0
  28. zra_mcp-0.3.0/research_core/rag/database.py +490 -0
  29. zra_mcp-0.3.0/research_core/rag/embedding.py +234 -0
  30. zra_mcp-0.3.0/research_core/rag/embedding_diagnostics.py +373 -0
  31. zra_mcp-0.3.0/research_core/rag/evaluation.py +413 -0
  32. zra_mcp-0.3.0/research_core/rag/indexer.py +157 -0
  33. zra_mcp-0.3.0/research_core/rag/logger.py +321 -0
  34. zra_mcp-0.3.0/research_core/rag/query_dict.json +318 -0
  35. zra_mcp-0.3.0/research_core/rag/query_rewriter.py +267 -0
  36. zra_mcp-0.3.0/research_core/rag/rendering.py +859 -0
  37. zra_mcp-0.3.0/research_core/rag/reranker.py +77 -0
  38. zra_mcp-0.3.0/research_core/rag/retriever.py +674 -0
  39. zra_mcp-0.3.0/research_core/rag/store.py +98 -0
  40. zra_mcp-0.3.0/research_core/rag/sync_state.py +101 -0
  41. zra_mcp-0.3.0/research_core/sources/__init__.py +5 -0
  42. zra_mcp-0.3.0/research_core/sources/cnki/__init__.py +25 -0
  43. zra_mcp-0.3.0/research_core/sources/cnki/browser.py +75 -0
  44. zra_mcp-0.3.0/research_core/sources/cnki/detail.py +173 -0
  45. zra_mcp-0.3.0/research_core/sources/cnki/exceptions.py +17 -0
  46. zra_mcp-0.3.0/research_core/sources/cnki/models.py +27 -0
  47. zra_mcp-0.3.0/research_core/sources/cnki/navigate.py +160 -0
  48. zra_mcp-0.3.0/research_core/sources/cnki/scripts.py +268 -0
  49. zra_mcp-0.3.0/research_core/sources/cnki/search.py +191 -0
  50. zra_mcp-0.3.0/research_core/sources/cnki/zotero_export.py +247 -0
  51. zra_mcp-0.3.0/research_core/sources/crossref.py +145 -0
  52. zra_mcp-0.3.0/research_core/sources/http_client.py +201 -0
  53. zra_mcp-0.3.0/research_core/sources/models.py +51 -0
  54. zra_mcp-0.3.0/research_core/sources/openalex.py +392 -0
  55. zra_mcp-0.3.0/research_core/sources/semantic_scholar.py +221 -0
  56. zra_mcp-0.3.0/research_core/sources/verify.py +164 -0
  57. zra_mcp-0.3.0/research_core/tools/__init__.py +106 -0
  58. zra_mcp-0.3.0/research_core/tools/admin.py +603 -0
  59. zra_mcp-0.3.0/research_core/tools/arguments.py +214 -0
  60. zra_mcp-0.3.0/research_core/tools/citation_network.py +149 -0
  61. zra_mcp-0.3.0/research_core/tools/cite.py +130 -0
  62. zra_mcp-0.3.0/research_core/tools/cnki_detail.py +44 -0
  63. zra_mcp-0.3.0/research_core/tools/cnki_navigate.py +85 -0
  64. zra_mcp-0.3.0/research_core/tools/cnki_zotero.py +68 -0
  65. zra_mcp-0.3.0/research_core/tools/discover_cnki.py +71 -0
  66. zra_mcp-0.3.0/research_core/tools/discover_online.py +270 -0
  67. zra_mcp-0.3.0/research_core/tools/find_related.py +788 -0
  68. zra_mcp-0.3.0/research_core/tools/health.py +425 -0
  69. zra_mcp-0.3.0/research_core/tools/inspect_index.py +292 -0
  70. zra_mcp-0.3.0/research_core/tools/manage.py +819 -0
  71. zra_mcp-0.3.0/research_core/tools/read.py +303 -0
  72. zra_mcp-0.3.0/research_core/tools/reading_note.py +120 -0
  73. zra_mcp-0.3.0/research_core/tools/reading_status.py +169 -0
  74. zra_mcp-0.3.0/research_core/tools/recommend.py +251 -0
  75. zra_mcp-0.3.0/research_core/tools/review.py +192 -0
  76. zra_mcp-0.3.0/research_core/tools/search.py +634 -0
  77. zra_mcp-0.3.0/research_core/tools/suggest_tags.py +173 -0
  78. zra_mcp-0.3.0/research_core/utils.py +72 -0
  79. zra_mcp-0.3.0/research_core/zotero/__init__.py +6 -0
  80. zra_mcp-0.3.0/research_core/zotero/client.py +845 -0
  81. zra_mcp-0.3.0/research_core/zotero/models.py +38 -0
  82. zra_mcp-0.3.0/scripts/audit_index.py +891 -0
  83. zra_mcp-0.3.0/scripts/benchmark_chunk_size.py +369 -0
  84. zra_mcp-0.3.0/scripts/benchmark_e2e.py +198 -0
  85. zra_mcp-0.3.0/scripts/benchmark_int8.py +113 -0
  86. zra_mcp-0.3.0/scripts/benchmark_output_format.py +130 -0
  87. zra_mcp-0.3.0/scripts/compare_output_formats.py +314 -0
  88. zra_mcp-0.3.0/scripts/generate_eval_queries.py +279 -0
  89. zra_mcp-0.3.0/scripts/index_library.py +47 -0
  90. zra_mcp-0.3.0/scripts/index_sample.py +117 -0
  91. zra_mcp-0.3.0/scripts/publish.sh +90 -0
  92. zra_mcp-0.3.0/scripts/run_evaluation.py +173 -0
  93. zra_mcp-0.3.0/scripts/test_names.py +30 -0
  94. zra_mcp-0.3.0/tests/__init__.py +0 -0
  95. zra_mcp-0.3.0/tests/core/__init__.py +0 -0
  96. zra_mcp-0.3.0/tests/core/test_chunker.py +30 -0
  97. zra_mcp-0.3.0/tests/eval_queries.json +554 -0
  98. zra_mcp-0.3.0/tests/mcp/__init__.py +0 -0
  99. zra_mcp-0.3.0/tests/mcp/test_cnki.py +43 -0
  100. zra_mcp-0.3.0/tests/mcp/test_scenarios.py +606 -0
  101. zra_mcp-0.3.0/tests/mcp/test_tools.py +392 -0
  102. zra_mcp-0.3.0/tests/unit/test_cnki_helpers.py +291 -0
@@ -0,0 +1,79 @@
1
+ # ── Zotero ──
2
+ # Local mode: reads from Zotero desktop app (fast, no auth needed)
3
+ ZOTERO_LOCAL=true
4
+ # Web API credentials (required for writes and hybrid mode)
5
+ # Get your key at https://www.zotero.org/settings/keys
6
+ ZOTERO_LIBRARY_ID=0
7
+ ZOTERO_LIBRARY_TYPE=user
8
+ ZOTERO_API_KEY=
9
+ # Hybrid mode: set ZOTERO_LOCAL=true AND provide ZOTERO_API_KEY + ZOTERO_LIBRARY_ID
10
+ # → reads stay local (fast), writes go through web API (writable)
11
+ #
12
+ # Note: this is an MCP *server* — the LLM is supplied by your MCP client
13
+ # (Cursor / Claude / Cherry Studio / …), so no OpenAI/Anthropic key is needed here.
14
+
15
+ # ── Embedding ──
16
+ # Backend selection (auto / onnx_int8 / sentence_transformers):
17
+ # auto — ONNX INT8 if available, otherwise FP32 (default)
18
+ # onnx_int8 — ONNX Runtime INT8 quantized (~347MB, 2-3x faster, 4x smaller)
19
+ # sentence_transformers — Original FP32 PyTorch (~2.3GB, highest precision)
20
+ # ONNX INT8 is recommended for most users — it is 2-3x faster on CPU,
21
+ # uses 4x less disk space, and has <2% retrieval precision impact.
22
+ # EMBEDDING_BACKEND=auto
23
+
24
+ # Model name (used by sentence_transformers backend only; ONNX INT8 uses a
25
+ # pre-quantized model automatically).
26
+ EMBEDDING_MODEL=BAAI/bge-m3
27
+ # HuggingFace mirror for users in China (uncomment if download is slow)
28
+ # HF_ENDPOINT=https://hf-mirror.com
29
+
30
+ # Cap embedding sequence length to bound memory (chunks are short; 1024 is
31
+ # ample and prevents GPU/MPS out-of-memory on pathological long inputs).
32
+ # EMBEDDING_MAX_SEQ_LEN=1024
33
+
34
+ # ── Tables & figures ──
35
+ # Tables and figures are indexed as lightweight caption-anchored records (where
36
+ # they are + caption + rough content), not structured into cells. No setting is
37
+ # needed. For true table structuring, preprocess your PDFs with a visual parser
38
+ # (docling / open-parse / unstructured) — see the README "Tables & figures" note.
39
+
40
+ # ── Reranker (optional, improves search precision) ──
41
+ # cross-encoder model for re-scoring retrieved chunks
42
+ # Default: cross-encoder/ms-marco-MiniLM-L-6-v2 (~80MB, auto-downloaded)
43
+ # Set to "none" to disable reranking entirely
44
+ RERANKER_MODEL=cross-encoder/ms-marco-MiniLM-L-6-v2
45
+
46
+ # ── ChromaDB ──
47
+ CHROMA_PERSIST_DIR=.chroma_db
48
+
49
+ # ── Text Cleaning ──
50
+ # Strip journal boilerplate (headers, footers, article-info blocks) before
51
+ # chunking. 52 blacklist rules cover EN+CN journals. Set to "false" to disable.
52
+ ZRA_CLEAN_ENABLED=true
53
+
54
+ # ── Server Behavior ──
55
+ # Auto-sync index on MCP server startup (set to "false" to disable)
56
+ ZRA_AUTO_SYNC=true
57
+
58
+ # ── Online literature search (optional) ──
59
+ # Semantic Scholar: higher rate limits — https://www.semanticscholar.org/product/api
60
+ SEMANTIC_SCHOLAR_API_KEY=
61
+ # OpenAlex polite pool — your contact email
62
+ OPENALEX_MAILTO=
63
+ # Unpaywall + OA PDF waterfall contact email
64
+ UNPAYWALL_EMAIL=
65
+ # CORE repository full-text — https://core.ac.uk/services/api
66
+ CORE_API_KEY=
67
+
68
+ # ── CNKI / 知网 (optional, disabled by default) ──
69
+ # CNKI browser automation via Chrome DevTools Protocol (inspired by cookjohn/cnki-skills).
70
+ # Only needed when you want to search Chinese journal papers.
71
+ # Setup: see README.md "CNKI 中文文献检索" section.
72
+ CNKI_ENABLED=false
73
+ # Start Chrome with: --remote-debugging-port=9222, then log in to CNKI.
74
+ CNKI_CDP_URL=http://127.0.0.1:9222
75
+ # Alternative: Playwright storage state JSON exported after manual login
76
+ # CNKI_STORAGE_STATE=.cnki_storage_state.json
77
+ # CNKI_HEADLESS=false
78
+ # CNKI_USE_CHROME=true
79
+ # CNKI_TIMEOUT_MS=45000
@@ -0,0 +1,39 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ concurrency:
9
+ group: ci-${{ github.ref }}
10
+ cancel-in-progress: true
11
+
12
+ jobs:
13
+ lint-and-test:
14
+ runs-on: ubuntu-latest
15
+ strategy:
16
+ fail-fast: false
17
+ matrix:
18
+ python-version: ["3.11", "3.12"]
19
+ steps:
20
+ - uses: actions/checkout@v4
21
+
22
+ - name: Set up Python ${{ matrix.python-version }}
23
+ uses: actions/setup-python@v5
24
+ with:
25
+ python-version: ${{ matrix.python-version }}
26
+ cache: pip
27
+
28
+ - name: Install dependencies
29
+ run: |
30
+ python -m pip install --upgrade pip
31
+ pip install -e ".[dev]"
32
+
33
+ - name: Lint (ruff)
34
+ run: ruff check .
35
+
36
+ - name: Unit + core tests
37
+ # tests/mcp are integration tests that need a live Zotero + index, so
38
+ # they are intentionally excluded from CI.
39
+ run: pytest tests/core tests/unit -q
@@ -0,0 +1,58 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ dist/
6
+ build/
7
+ *.egg
8
+ .venv/
9
+
10
+ # Environment / credentials (contain API keys & tokens)
11
+ .env
12
+ .pypirc
13
+ *.pypirc
14
+
15
+ # IDE
16
+ .vscode/
17
+ .idea/
18
+ .cursor/
19
+ .claude/
20
+ *.swp
21
+ *.swo
22
+
23
+ # ChromaDB / Vector store
24
+ .chroma_db/
25
+
26
+ # Sync state + retrieval logs (generated per-machine, inside .chroma_db/)
27
+ _sync_state.json
28
+ _retrieval_log.jsonl
29
+ _retrieval_log.idx
30
+
31
+ # User-defined query synonyms (per-machine)
32
+ query_dict_user.json
33
+
34
+ # Evaluation baseline (per-machine, depends on local library content)
35
+ tests/eval_baseline.json
36
+
37
+ # Ruff
38
+ .ruff_cache/
39
+
40
+ # uv
41
+ .local/
42
+ uv.lock
43
+
44
+ # pytest
45
+ .pytest_cache/
46
+ htmlcov/
47
+ .coverage
48
+
49
+ # Docs (internal guides, not for distribution)
50
+ docs/internal/
51
+
52
+ # Development progress / roadmap (internal)
53
+ DEVELOPMENT.md
54
+ ARCHITECTURE.md
55
+
56
+ # OS
57
+ .DS_Store
58
+ Thumbs.db
@@ -0,0 +1,218 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented here.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ### Added
11
+ - **BM25 sparse keyword index on chunk texts** — `rank_bm25` with CJK-aware
12
+ tokenizer (character unigrams+bigrams for CN, alpha words for EN). Two-way
13
+ RRF fusion: BM25 (lexical) + ChromaDB (semantic). Persists to
14
+ `.chroma_db/_bm25_index.pkl`. Auto-rebuilt on every sync.
15
+ - **Contextual chunk enrichment** — each chunk text is prepended with
16
+ `[Keywords: ...] [Title: ...] [Section: ...]` before embedding and BM25
17
+ indexing. Implements Anthropic "Contextual Retrieval" 2024 technique
18
+ using existing metadata — zero additional cost. Keywords are filtered
19
+ from Zotero tags (excludes organizational labels).
20
+ - **Dual-format output (JSON + Markdown context_block)** — key retrieval tools now
21
+ return a `context_block` field containing pre-rendered LLM-optimized Markdown
22
+ alongside the existing JSON items. Blockquote (>) for evidence, star ratings
23
+ (★★★) for relevance tiers, sentence-boundary truncation. Covers all 8
24
+ retrieval/insight tools: `search_papers`, `get_paper_content`,
25
+ `generate_review_note`, `suggest_citations`, `find_similar_papers`,
26
+ `find_arguments`, `generate_reading_note`, `suggest_tags`.
27
+ - **Relevance tiers** — each `search_papers` result now carries a `relevance_tier`
28
+ field ("high"/"medium"/"low") computed from Cross-Encoder score percentiles.
29
+ - **Full-pipeline evaluation mode** — `run_evaluation.py --full-pipeline` tests
30
+ the complete `search_papers()` pipeline (BM25 + CE rerank + MMR + RRF fusion),
31
+ not just raw semantic search. `evaluate_full_pipeline()` in evaluation.py.
32
+ - **Retrieval log rotation** — auto-cleans log entries older than 90 days.
33
+ Triggers on server startup and after each `sync_index`. Uses `f.tell()` for
34
+ cross-platform byte offset accuracy.
35
+ - **Expanded section detection** — Roman numerals (I., II.), Chapter prefix
36
+ (CHAPTER 1:), section symbol (§1.), letter subsections (A., B.). ~50 new
37
+ type classification keywords covering non-standard academic headings.
38
+ Number-only fallback for papers with bare numbered markers.
39
+ - **ONNX INT8 embedding backend** (`EMBEDDING_BACKEND=auto` / `onnx_int8`) —
40
+ 2-3x faster, 4x smaller (347MB vs 2.3GB) embedding on CPU. Zero-config.
41
+ - **MMR (Maximal Marginal Relevance) diversity reranking** — chunk-level MMR
42
+ with per-document cap (max 3 chunks per paper) and per-document penalty.
43
+ Default λ=0.4 (grid-search tuned from 0.6).
44
+ - **Multi-layer bilingual query expansion** — 3-layer system with ~300 built-in
45
+ methodology term pairs, auto-extracted Zotero tags, and user-defined synonyms.
46
+ - **Neighbor chunk expansion** — `expand_neighbors=True` returns hit chunk ±1
47
+ adjacent chunk within the same section.
48
+ - **Min chunk size floor (200 chars)** — post-chunking merge pass eliminating
49
+ fragments below the FloTorch 2026 threshold for e2e accuracy.
50
+
51
+ ### Changed
52
+ - **Two-way RRF fusion** — simplified from three-way (Zotero API + BM25 + Dense)
53
+ to two-way (BM25 + Dense). Zotero API remains for paper metadata, filters,
54
+ and empty query mode.
55
+ - **MMR diversity_weight tuned: 0.6 → 0.4** — grid search on 10 queries.
56
+ - **Chunk quality scoring simplified** — dropped unused heuristic fields
57
+ (coherence_score, information_density, boilerplate_ratio).
58
+ - **MCP tools: 32 → 36** with add_query_synonym, recent_retrievals,
59
+ retrieval_trace, and retrieval_stats.
60
+ - **CHUNKING_VERSION: v2.9.0 → v3.1.0-contextual-chunks**.
61
+
62
+ ### Fixed
63
+ - **SQLite authors field** — was empty string, now populated from Zotero item metadata.
64
+ - **sync_index dead parameters** — removed unused `chunk_size` and `chunk_overlap`.
65
+ - **Section detection `\s*` newline bug** — `\s*` in heading patterns now replaced
66
+ with `[ \\t]*` to prevent greedy newline consumption causing false headings.
67
+ - **inspect_index pagination bug** — `len(docs)` was only the last page's count,
68
+ now uses `total_count` for accurate stats.
69
+ - **diversity_weight docstring** — corrected from 0.6 to 0.4 in both search.py
70
+ and server.py.
71
+
72
+ ## [0.3.0] - 2026-07-06
73
+
74
+ A major RAG quality release — production-grade retrieval pipeline with text cleaning,
75
+ chunk quality scoring, SQLite metadata layer, section-parent context expansion,
76
+ embedding diagnostics, retrieval observability, and systematic evaluation.
77
+
78
+ ### Added
79
+
80
+ **RAG Pipeline — Data Quality**
81
+ - **Text cleaning engine** (`research_core/parsers/text_cleaner.py`): 52 blacklist
82
+ regex rules removing journal boilerplate — EN article-info blocks, CN volume/issue
83
+ lines, CLC numbers, funding footers, page numbers, DOI lines. Controlled by
84
+ `ZRA_CLEAN_ENABLED=true` (default on). Cleaning stats reported in `sync_index`.
85
+
86
+ **RAG Pipeline — Chunk Quality**
87
+ - **Chunk quality scoring** (`research_core/parsers/chunker.py`, v2.9.0): 7 quality
88
+ fields per chunk — `coherence_score`, `information_density`, `boilerplate_ratio`,
89
+ `sentence_count`, `starts_with_conjunction`, `language` (zh/en/mixed), `quality_flag`
90
+ (good/noisy/incomplete/boilerplate). Lightweight heuristic scoring for quality-aware
91
+ retrieval filtering.
92
+
93
+ **RAG Pipeline — Metadata & Context**
94
+ - **SQLite metadata database** (`research_core/rag/database.py`): 7 relational tables
95
+ (`papers`, `sections`, `chunks_meta`, `figures`, `table_records`, and cross-reference
96
+ tables) inside `.chroma_db/papers.db`. Zero user setup — auto-created on first sync.
97
+ - **Section-parent context expansion** (`Retriever.expand_to_section()`): hit a chunk
98
+ → fetch its entire enclosing section from SQLite + ChromaDB, providing LLM with
99
+ complete paragraph context instead of isolated fragments. Cache-batched.
100
+ - **Result enrichment** (`Retriever.enrich()`): batch-fetches paper + section metadata
101
+ (abstract, authors, year, DOI, keywords, section heading/type) via SQLite JOIN.
102
+
103
+ **RAG Pipeline — Diagnostics & Evaluation**
104
+ - **Embedding quality diagnostics** (`research_core/rag/embedding_diagnostics.py`):
105
+ 6-phase analysis — per-paper intra-similarity, cross-paper separation ratio, outlier
106
+ chunk detection (centroid coherence < 0.3), chunk length-similarity Pearson
107
+ correlation, section-type embedding separation, automated issues + fix suggestions.
108
+ - **Evaluation framework** (`research_core/rag/evaluation.py`): Recall@5/10/20, MRR,
109
+ NDCG@10 metrics. 60 golden queries (`tests/eval_queries.json`) across direct hit,
110
+ cross-document, and no-answer categories. `scripts/run_evaluation.py` with
111
+ `--save-baseline` / `--compare` for A/B testing.
112
+ - **Index audit script** (`scripts/audit_index.py`): 7-phase full-library quality
113
+ audit — paginated scan, per-paper scoring, library coverage, noise detection,
114
+ embedding separation, health scoring, and actionable recommendations.
115
+
116
+ **RAG Pipeline — Observability**
117
+ - **Retrieval logging** (`research_core/rag/logger.py`): JSONL append-only trace
118
+ logging with byte-offset index. Captures: query, strategy, candidate counts,
119
+ reranker details, top-20 results with scores/sources, latency breakdown
120
+ (keyword/semantic/rerank/total). 3 new MCP tools: `recent_retrievals`,
121
+ `retrieval_trace` (replay by trace ID), `retrieval_stats` (aggregate analytics).
122
+
123
+ **MCP Tools**
124
+ - Total tool count: 32 → 35 (3 retrieval log tools: `recent_retrievals`,
125
+ `retrieval_trace`, `retrieval_stats`).
126
+
127
+ **Documentation**
128
+ - READMEs (EN/CN) updated with all new RAG features, enhanced client setup sections
129
+ for Claude Desktop, Cherry Studio, and Codex CLI.
130
+ - `DEVELOPMENT_PLAN.md` updated: Phase 0/1/2 marked complete, decision log synced.
131
+ - `DEVELOPMENT_LOG.md` — developer-facing detailed change log from project inception
132
+ to v0.3.0.
133
+
134
+ ### Changed
135
+
136
+ - **Chunking overlap rewritten** (`chunker.py`, v2.8.0): sentence-based (1 sentence)
137
+ → character-based (100 chars) with sentence-boundary completion. Forward-then-backward
138
+ search algorithm ensures overlap always captures meaningful context.
139
+ - **Section detection** (`section_detector.py`): IMRaD classification via regex heading
140
+ patterns (EN numbered "1. Introduction", CN "一、引言"). Quality-aware — skips
141
+ boilerplate/incomplete chunks for heading detection.
142
+ - **Retriever search** now auto-enriches results with paper + section metadata from
143
+ SQLite (zero latency cost — single JOIN). `expand_context` parameter enables
144
+ section-parent context expansion.
145
+ - **`search_papers`** emits JSONL retrieval trace on every call with full latency
146
+ breakdown.
147
+ - **Architecture**: `research_core/rag/` now includes `database.py`, `evaluation.py`,
148
+ `logger.py`, `embedding_diagnostics.py`.
149
+ - Removed emoji from all terminal output for Windows GBK compatibility.
150
+
151
+ ### Fixed
152
+
153
+ - Sentence-boundary overlap algorithm: forward-then-backward search now correctly
154
+ finds sentence boundaries within the overlap region. Previous backward-only search
155
+ missed boundaries, producing only 1% overlap for English text.
156
+ - Syntax errors in `search.py` from duplicate `matched_passage`/`matched_page` lines
157
+ after edit.
158
+ - `.claude/` directory added to `.gitignore`.
159
+
160
+ ## [0.2.0] - 2026-06-11
161
+
162
+ A large reliability- and productization-focused release. The project is now a
163
+ standalone MCP server (no agent scaffold) with 32 single-intent tools.
164
+
165
+ ### Added
166
+ - **Standalone MCP server** exposing 32 tools across Discover / Read / Write /
167
+ Cite / Insight / Admin, each mapping to one user intent and composing via `item_key`.
168
+ - **`expand_citation_network`** — forward/backward citation-graph expansion via
169
+ OpenAlex, with multi-seed DOI support.
170
+ - **`find_related_literature`** — one call runs 5 parallel strategies
171
+ (Corpus-First, keyword, citation network, S2 recommendations, OpenAlex related
172
+ works) with three-index verification.
173
+ - **Table & figure cross-referencing** — prose that cites "Table 3 / Figure 2"
174
+ is linked to caption-anchored table/figure records, resolved together in
175
+ `get_paper_content` (`referenced_tables` / `referenced_figures`).
176
+ - **CJK-aware chunking** — sentence splitting and soft-wrap repair so Chinese
177
+ text is no longer cut mid-word.
178
+ - Shared HTTP client with global concurrency cap, per-host rate limiting,
179
+ retry/backoff, and a short-TTL response cache.
180
+ - Configurable `EMBEDDING_MAX_SEQ_LEN` (bounds GPU/MPS memory) and `HF_ENDPOINT`
181
+ (HuggingFace mirror, e.g. for users in China).
182
+ - Bilingual README and a Cherry Studio setup guide (English + Chinese) aimed at
183
+ non-developers.
184
+
185
+ ### Changed
186
+ - **Tables and figures are now caption-anchored records** instead of being parsed
187
+ into structured cells. Reliable table structuring is a vision problem;
188
+ geometric/line-based detection produced garbage on borderless academic tables
189
+ and mis-segmented multi-column prose. Table *values* stay searchable; users who
190
+ need true structure are pointed to docling / open-parse / unstructured.
191
+ - Hardened MCP tools: write operations are dry-run by default and require explicit
192
+ confirmation; responses are size-capped to protect the LLM context window;
193
+ errors return structured bilingual diagnostics.
194
+ - Improved RAG recall and speed; thread-safe lazy initialization of Zotero client,
195
+ retriever, and indexer.
196
+ - Incremental index sync by default (Zotero version tracking); auto full rebuild
197
+ when the embedding model or chunking version changes.
198
+
199
+ ### Removed
200
+ - Built-in structured table extraction (PyMuPDF "lite" mode + Table Transformer
201
+ "ml" mode), the `[tables]` optional extra, and `table_ml.py`.
202
+ - Legacy agent scaffold (the project is MCP-server only).
203
+
204
+ ### Fixed
205
+ - Correct Zotero item filtering and ChromaDB `search_ef` modification.
206
+ - Hard chunk-size cap to prevent embedding out-of-memory on pathological inputs.
207
+ - Unit tests updated for the shared HTTP client refactor.
208
+
209
+ ## [0.1.1] - earlier
210
+
211
+ - Initial public iteration: literature search, RAG pipeline, reading analysis,
212
+ citation management, review/reading-note generation, tag suggestions, and the
213
+ first Cherry Studio setup guide.
214
+
215
+ [Unreleased]: https://github.com/qiobn/zotero-research-assistant/compare/v0.3.0...main
216
+ [0.3.0]: https://github.com/qiobn/zotero-research-assistant/releases/tag/v0.3.0
217
+ [0.2.0]: https://github.com/qiobn/zotero-research-assistant/releases/tag/v0.2.0
218
+ [0.1.1]: https://github.com/qiobn/zotero-research-assistant/releases/tag/v0.1.1
@@ -0,0 +1,119 @@
1
+ # CLAUDE.md
2
+
3
+ ## Project Overview
4
+
5
+ This is **Zotero Research Assistant** — an MCP (Model Context Protocol) server that turns a Zotero reference library into an AI-searchable knowledge base. 36 MCP tools across 6 categories. **Core focus: production-grade RAG pipeline (chunking + retrieval quality) for academic papers.**
6
+
7
+ - **Author:** qiobn
8
+ - **Language:** Python 3.11+
9
+ - **Package:** `zotero-research-assistant`
10
+ - **Entry:** `project_a_mcp/server.py` → `zra-mcp` CLI command
11
+ - **Key deps:** ChromaDB, onnxruntime (INT8 default), sentence-transformers (FP32 fallback), PyMuPDF, FastMCP, PyZotero
12
+
13
+ ## Architecture
14
+
15
+ ```
16
+ research_core/
17
+ parsers/ — PDF extraction, text cleaner (52 rules), chunker, section detector
18
+ rag/ — ChromaDB store, retriever, SQLite metadata DB, evaluation, logger, diagnostics
19
+ tools/ — 36 MCP tool implementations
20
+ zotero/ — Zotero local + web API client
21
+ project_a_mcp/ — MCP server entry point (stdio)
22
+ scripts/ — CLI utils (index_library, audit_index, run_evaluation, publish)
23
+ tests/ — pytest suite + 60 golden eval queries
24
+ docs/ — Setup guides (Cherry Studio CN/EN)
25
+ ```
26
+
27
+ ## Development Rules
28
+
29
+ ### Commit & Log Protocol
30
+
31
+ **On every significant change (new feature, bug fix, non-trivial refactor), you MUST:**
32
+
33
+ 1. **Update `DEVELOPMENT_LOG.md`** — Record:
34
+ - What was changed (with commit hash)
35
+ - What problem it solved
36
+ - Technical decisions made and their rationale
37
+ - Future optimization directions
38
+ - Known issues introduced
39
+
40
+ 2. **Update `CHANGELOG.md`** — Keep a Changelog format (`Added`/`Changed`/`Fixed`/`Removed` sections under current version). User-facing, less technical than DEVELOPMENT_LOG.
41
+
42
+ 3. **Commit with a conventional commit message — ALWAYS include a body, never an empty commit:**
43
+ - First line: `<type>: <summary>` (50-80 chars, imperative mood)
44
+ - Then a BLANK LINE
45
+ - Then body paragraphs explaining **what** changed, **why**, and any design notes
46
+ - Types: `feat:` / `fix:` / `docs:` / `refactor:` / `chore:`
47
+ - Example:
48
+ ```
49
+ feat: add query rewrite for bilingual academic search
50
+
51
+ Dictionary-based CN<->EN term expansion with three layers:
52
+ Layer 1: ~300 built-in methodology pairs from query_dict.json
53
+ Layer 2: auto-extracted from user's Zotero tags during sync
54
+ Layer 3: user-defined via add_query_synonym MCP tool
55
+
56
+ Expansion runs in search_papers() — zero added latency, no LLM
57
+ dependency. Each expanded term runs independent semantic search
58
+ with RRF merging and expansion weight scoring.
59
+ ```
60
+
61
+ 4. **Push** after each logical unit of work (not after every micro-edit).
62
+
63
+ ### Code Style
64
+
65
+ - Follow surrounding code patterns: comment density, naming, idiom
66
+ - Use `from __future__ import annotations` in all new files
67
+ - Use dataclasses for data containers
68
+ - Return typed dataclass instances, not raw dicts
69
+ - All write operations default to dry-run preview; require explicit confirmation
70
+ - MCP tools: one tool per user intent, compose via `item_key`
71
+ - Windows compatibility: no emoji in terminal output (use ASCII alternatives), use `os.path` or `pathlib`
72
+
73
+ ### Testing
74
+
75
+ - Unit tests in `tests/` with pytest
76
+ - Run: `pytest tests/ -v`
77
+ - Lint: `ruff check .`
78
+ - Format: `ruff format .`
79
+ - Evaluation: `python scripts/run_evaluation.py`
80
+
81
+ ### Environment
82
+
83
+ - `.env` is gitignored; `.env.example` is committed (template only, no real secrets)
84
+ - All configurable values have env var overrides
85
+ - Defaults are zero-config: users should get basic functionality without any `.env` edits
86
+ - New env vars must be documented in: `.env.example`, both READMEs' config tables
87
+
88
+ ### Documentation Sync
89
+
90
+ When adding/removing features or changing behavior, update:
91
+ 1. `README.md` (English) + `README_zh.md` (Chinese) — keep in sync
92
+ 2. `CHANGELOG.md` — keep-a-changelog format
93
+ 3. `DEVELOPMENT_LOG.md` — technical details and decisions
94
+ 4. `DEVELOPMENT_PLAN.md` — progress bars and task checkboxes
95
+ 5. `.env.example` — new env vars with comments
96
+
97
+ ## Key Files Reference
98
+
99
+ | File | Purpose |
100
+ |------|---------|
101
+ | `DEVELOPMENT_LOG.md` | Detailed technical changelog (dev-facing) |
102
+ | `CHANGELOG.md` | Release changelog (user-facing) |
103
+ | `DEVELOPMENT_PLAN.md` | Task tracking + progress bars |
104
+ | `README.md` / `README_zh.md` | Bilingual project READMEs |
105
+ | `research_core/parsers/chunker.py` | Chunking with CHUNKING_VERSION for auto-rebuild |
106
+ | `research_core/parsers/text_cleaner.py` | 52 blacklist rules, CLEANER_VERSION |
107
+ | `research_core/rag/database.py` | SQLite metadata DB (auto-created on first sync) |
108
+ | `research_core/rag/logger.py` | JSONL retrieval trace logging |
109
+ | `research_core/rag/evaluation.py` | Recall@K, MRR, NDCG |
110
+ | `research_core/rag/retriever.py` | ChromaDB retriever with section expansion + enrichment |
111
+
112
+ ## Current State (v0.4.0-dev)
113
+
114
+ - Phase 0/1/2/3 (partial) complete; Phase 4 (v0.4.0) in progress
115
+ - 36 MCP tools (32 always-on + 4 CNKI-conditional), all operational
116
+ - Key features: BM25+Dense hybrid retrieval, ONNX INT8 embedding, MMR diversity,
117
+ bilingual query expansion, contextual chunk enrichment, dual-format output
118
+ - Known issues: evaluation only run on small test set (8 papers)
119
+ - Next priorities: run full-pipeline evaluation on real library, chunk size tuning
@@ -0,0 +1,8 @@
1
+ # Development Log
2
+
3
+ > Detailed technical development log with problem/solution/decision records.
4
+
5
+ - **[中文版](./docs/DEVELOPMENT_LOG.md)** — 完整中文版
6
+ - **[English](./docs/DEVELOPMENT_LOG_EN.md)** — Full English version
7
+
8
+ See also: [CHANGELOG.md](./CHANGELOG.md) for user-facing release notes.
@@ -0,0 +1,148 @@
1
+ # Development Plan — RAG Full-Pipeline Optimization
2
+
3
+ > Last updated: 2026-07-13 | Current version: v0.4.0-dev
4
+
5
+ ---
6
+
7
+ ## Progress Overview
8
+
9
+ ```
10
+ Phase 0 (Audit) ████████████████████ 100% ✅ DONE
11
+ Phase 1 (P0) ████████████████████ 100% ✅ DONE
12
+ Phase 2 (P1) ████████████████████ 100% ✅ DONE
13
+ Phase 3 (P2) ████████░░░░░░░░░░░░ 40% (completed: 3.1, 3.3; remaining: 3.2, 3.4, 3.5)
14
+ Phase 4 (v0.4.0) ░░░░░░░░░░░░░░░░░░░░ 0% Architecture audit findings
15
+ ```
16
+
17
+ ---
18
+
19
+ ## Phase 3: P2 — Refinements (Partial)
20
+
21
+ ### 3.1 Query Rewrite (Academic Scene) ✅
22
+
23
+ > Implemented: `research_core/rag/query_rewriter.py`. Three-layer bilingual expansion (built-in ~310 pairs + Zotero tags + user synonyms). LRU-cached. `research_core/rag/query_dict.json`.
24
+
25
+ ### 3.2 Adaptive Chunk Granularity ⬜
26
+
27
+ > **RESEARCHED & DEFERRED**: 2025-2026 literature (NAACL, Chroma, PaperQA2) shows fixed-size chunking is the strong baseline. Semantic/adaptive chunking does not consistently beat it. PaperQA2 uses fixed ~9000 chars with downstream LLM reranking. Chunk size is the dominant variable, not the splitter method.
28
+
29
+ ### 3.3 Search Result Post-Processing ✅
30
+
31
+ > All three originally planned items completed:
32
+ > - MMR diversity (λ=0.4, grid-search tuned, max 3 chunks/paper)
33
+ > - Neighbor chunk expansion (±1 chunk, section-constrained)
34
+ > - Source diversity (MMR cap + per-document penalty)
35
+
36
+ ### 3.4 Comprehensive Diagnostic MCP Tool ⬜
37
+
38
+ > **DEFERRED**: Normal users don't trace why results ranked a certain way. De-prioritized.
39
+
40
+ ### 3.5 Metadata-Enhanced Re-Ranking ⬜
41
+
42
+ > **RESEARCHED & DEFERRED**: Marginal improvement for personal libraries (5-10%). The strongest signals (citation count, journal tier) are external and add latency. Not worth the complexity at this scale.
43
+
44
+ ---
45
+
46
+ ## Phase 4: v0.4.0 — Architecture Audit Findings (2026-07-13)
47
+
48
+ Issues identified during full architecture review:
49
+
50
+ ### 4.1 Evaluation Tests Full Pipeline 🔴 HIGH (in progress)
51
+
52
+ > **Problem**: `evaluate_retrieval()` only tests `retriever.search()` (pure semantic). BM25, CE reranker, MMR, and RRF fusion have NEVER been evaluated. Every retrieval component you've built lacks quantitative validation.
53
+ >
54
+ > **Fix**: Add `--full-pipeline` mode to `run_evaluation.py` that calls `search_papers()` instead of `retriever.search()`. Compare semantic-only vs full-pipeline metrics. Save baselines for future regression testing.
55
+
56
+ **Estimate:** 1 day
57
+
58
+ ---
59
+
60
+ ### 4.2 Log Rotation ⬜ 🟡 MEDIUM
61
+
62
+ > **Problem**: `_retrieval_log.jsonl` grows unboundedly. Large libraries could hit GB-scale log files.
63
+ >
64
+ > **Fix**: Add size-based rotation (e.g., keep last 100MB / 10K entries). Or time-based (keep 30 days).
65
+
66
+ **Estimate:** 0.5 day
67
+
68
+ ---
69
+
70
+ ### 4.3 Authors Field in SQLite ⬜ 🟡 MEDIUM
71
+
72
+ > **Problem**: SQLite `papers.authors` is always `""`. Comment says "ZoteroItem doesn't expose authors as JSON" but `Item.authors` is `list[str]` — it's available.
73
+ >
74
+ > **Fix**: `json.dumps(item.authors)` when writing to SQLite in `_index_metadata()`.
75
+
76
+ **Estimate:** 5 minutes
77
+
78
+ ---
79
+
80
+ ### 4.4 Section Parent Linking ⬜ 🟡 MEDIUM
81
+
82
+ > **Problem**: `section_detector.py` computes `parent_idx` (subsection hierarchy) but SQLite `sections.parent_id` is always NULL. Subsections of Methods, etc. are flattened.
83
+ >
84
+ > **Fix**: Store parent-child relationships in SQLite during `_index_metadata()`.
85
+
86
+ **Estimate:** 0.5 day
87
+
88
+ ---
89
+
90
+ ### 4.5 Dead Parameters in sync_index ⬜ 🟢 LOW
91
+
92
+ > **Problem**: `sync_index(chunk_size=800, chunk_overlap=120)` accepts parameters that the chunker ignores (uses its own `target_chunk_size=600` internally since v2).
93
+ >
94
+ > **Fix**: Remove dead parameters or add deprecation warning.
95
+
96
+ **Estimate:** 5 minutes
97
+
98
+ ---
99
+
100
+ ### 4.6 Docstring Bug ⬜ 🟢 LOW
101
+
102
+ > **Problem**: `search_papers()` docstring says `diversity_weight=0.6`, actual default is `0.4` (grid-search tuned).
103
+ >
104
+ > **Fix**: Update docstring.
105
+
106
+ **Estimate:** 1 minute
107
+
108
+ ---
109
+
110
+ ### 4.7 BM25 Chinese Tokenization ⬜ 🟢 LOW
111
+
112
+ > **Problem**: Character bigrams work but jieba segmentation would be more accurate for Chinese BM25 queries.
113
+ >
114
+ > **Fix**: Optional jieba dependency, use if available, fall back to bigrams.
115
+
116
+ **Estimate:** 0.5 day
117
+
118
+ ---
119
+
120
+ ### 4.8 Personalized Re-Ranking ⬜ 🔵 FUTURE
121
+
122
+ > **Problem**: User engagement signals (annotations, reading depth, saved notes) are never used for ranking.
123
+ >
124
+ > **Fix**: Light boost for papers with user annotations/notes. Available from Zotero local API.
125
+
126
+ **Estimate:** 1 day
127
+
128
+ ---
129
+
130
+ ## Summary
131
+
132
+ | Phase | # Tasks | Completed | Remaining |
133
+ |-------|---------|-----------|-----------|
134
+ | Phase 0 (Audit) | 5 | 5 | 0 |
135
+ | Phase 1 (P0) | 3 | 3 | 0 |
136
+ | Phase 2 (P1) | 5 | 4 | 1 (deferred) |
137
+ | Phase 3 (P2) | 5 | 2 | 3 (deferred) |
138
+ | Phase 4 (v0.4.0) | 8 | 0 | 8 |
139
+ | **Total** | **26** | **14** | **12** |
140
+
141
+ ### Immediate Next Steps
142
+
143
+ ```
144
+ → Phase 4.1: Full-pipeline evaluation (in progress)
145
+ → Phase 4.3: Authors field fix (trivial, quick win)
146
+ → Phase 4.6: Docstring fix (trivial)
147
+ → Phase 4.5: Dead parameters cleanup
148
+ ```