Zotero MCP: Connects your Zotero research library with Claude and other AI assistants via the Model Context Protocol to discuss papers, get summaries, analyze citations, and more.
This report presents the forensic synthetic code analysis of 54yyyu/zotero-mcp, a Python project with 4,829 GitHub stars. SynthScan v2.0 examined 112,248 lines of code across 391 source files, recording 3739 pattern matches distributed across 20 syntactic categories. The overall adjusted score of 52.3 places this repository in the Strong AI signal band.
The scanner applied 160+ deterministic lexical heuristics, multi-line block detectors, abstract syntax tree depth profilers, and a cross-file Jaccard similarity matrix to construct a statistically normalised synthetic code estimate. All matches are individually weighted by severity coefficient and contextual multiplier before summation, and the resulting headline score is temporally discounted to account for the repository's development history relative to the commercial emergence of large language model coding tooling (November 2022 onward).
This chart maps the temporal evolution of the adjusted synthetic code score across successive scan runs. An upward trajectory indicates ongoing incorporation of AI-generated code or expanding LLM-assisted scaffolding; a stable or declining trajectory may reflect active human refactoring, code removal, or the adoption of stricter authorship policies. The dashed secondary line (right axis) independently tracks total raw pattern hit count, which can diverge from the normalised score when codebase size changes significantly between scans.
Classifies detected patterns by their diagnostic confidence and structural impact. CRITICAL patterns (coefficient 10) represent definitive synthetic signatures — hallucinated imports, explicit LLM attribution metadata — virtually never produced by human authors. HIGH (5) indicates strong structural tells such as cross-file repetition or cross-linguistic idioms. MEDIUM (2) covers recognisable conversational padding and AI-specific vocabulary. LOW (1) captures subtle indicators like tautological comments and generic boilerplate that require density to carry independent signal.
This horizontal bar chart decomposes the repository's raw synthetic code score by top-level directory, allowing you to pinpoint precisely which modules or components carry the highest AI authorship density. Directories with disproportionately high scores relative to their size warrant targeted manual review: concentrated AI signatures often trace back to mass-generated configuration layers, auto-ported test suites, LLM-scaffolded boilerplate classes, or entire subsystems authored under heavy copilot assistance. Use this view to prioritise your human code-review effort.
The scanner identified 3739 distinct pattern matches across 20 syntactic categories. Each entry below represents a discrete location in the source code where the engine recorded a statistically significant AI authorship indicator. Expand any category row to inspect the individual file paths, line numbers, code snippets, and the lexical context (CODE, COMMENT, or STRING) in which each match was detected.
Reading the findings table: The Severity column indicates the diagnostic confidence level (CRITICAL / HIGH / MEDIUM / LOW). The Context column identifies whether the match occurred inside executable code, an inline comment, or a string literal — comment-context matches receive a ×1.5 weight because LLMs systematically over-annotate. The ⚡ bolt icon marks clustered matches: three or more patterns within a 10-line window, each receiving an additional ×1.5 density multiplier as dense clusters constitute far stronger evidence of synthetic authorship than isolated hits.
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM⚡ | tests/test_page_layout.py | 48 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_page_layout.py | 50 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_page_layout.py | 110 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_page_layout.py | 112 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_page_layout.py | 212 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_page_layout.py | 214 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM | tests/test_page_layout.py | 23 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM | tests/test_page_layout.py | 25 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM | tests/test_page_layout.py | 136 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM | tests/test_page_layout.py | 138 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM | tests/test_page_layout.py | 301 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM | tests/test_page_layout.py | 303 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM | tests/test_page_layout.py | 560 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM | tests/test_page_layout.py | 562 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_scite.py | 37 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_scite.py | 39 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_scite.py | 58 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_scite.py | 60 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_scite.py | 123 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_scite.py | 126 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_fulltext_cache.py | 186 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_fulltext_cache.py | 188 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_citation_import.py | 117 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_citation_import.py | 119 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM | tests/test_citation_import.py | 48 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM | tests/test_citation_import.py | 50 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM | tests/test_citation_import.py | 160 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM | tests/test_citation_import.py | 162 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM | tests/test_citation_import.py | 201 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM | tests/test_citation_import.py | 203 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM | tests/test_citation_import.py | 237 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM | tests/test_citation_import.py | 239 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM | tests/test_citation_import.py | 677 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM | tests/test_citation_import.py | 679 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_citation_import.py | 420 | # --------------------------------------------------------------------------- | STRING |
| MEDIUM⚡ | tests/test_citation_import.py | 428 | # --------------------------------------------------------------------------- | STRING |
| MEDIUM⚡ | tests/test_citation_import.py | 571 | # --------------------------------------------------------------------------- | STRING |
| MEDIUM⚡ | tests/test_citation_import.py | 573 | # --------------------------------------------------------------------------- | STRING |
| MEDIUM⚡ | tests/test_citation_import.py | 605 | # --------------------------------------------------------------------------- | STRING |
| MEDIUM⚡ | tests/test_citation_import.py | 607 | # --------------------------------------------------------------------------- | STRING |
| MEDIUM | tests/test_citation_import.py | 330 | # --------------------------------------------------------------------------- | STRING |
| MEDIUM | tests/test_citation_import.py | 332 | # --------------------------------------------------------------------------- | STRING |
| MEDIUM | tests/test_citation_import.py | 624 | # --------------------------------------------------------------------------- | STRING |
| MEDIUM | tests/test_citation_import.py | 626 | # --------------------------------------------------------------------------- | STRING |
| MEDIUM⚡ | tests/test_shared_helpers.py | 11 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_shared_helpers.py | 13 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_shared_helpers.py | 57 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_shared_helpers.py | 59 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_shared_helpers.py | 79 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_shared_helpers.py | 81 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_shared_helpers.py | 109 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_shared_helpers.py | 111 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_shared_helpers.py | 138 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_shared_helpers.py | 140 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_shared_helpers.py | 188 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_shared_helpers.py | 190 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_shared_helpers.py | 287 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_shared_helpers.py | 289 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_shared_helpers.py | 313 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_shared_helpers.py | 315 | # --------------------------------------------------------------------------- | COMMENT |
| 728 more matches not shown… | ||||
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW⚡ | tests/test_attach_verification.py | 79 | def test_none_when_attachment_registered(self): | CODE |
| LOW⚡ | tests/test_attach_verification.py | 82 | def test_none_for_unchanged_entry(self): | CODE |
| LOW⚡ | tests/test_attach_verification.py | 86 | def test_reports_failure_payload(self): | CODE |
| LOW⚡ | tests/test_attach_verification.py | 91 | def test_reports_empty_result(self): | CODE |
| LOW⚡ | tests/test_attach_verification.py | 95 | def test_reports_result_with_no_key(self): | CODE |
| LOW⚡ | tests/test_attach_verification.py | 101 | def test_rejects_local_client(self): | CODE |
| LOW | tests/test_attach_verification.py | 114 | def test_success_passes_through_key(self, dummy_ctx, monkeypatch): | CODE |
| LOW | tests/test_attach_verification.py | 124 | def test_failed_upload_falls_back_to_two_step(self, dummy_ctx, monkeypatch): | CODE |
| LOW | tests/test_attach_verification.py | 137 | def test_reports_failure_when_two_step_also_fails(self, dummy_ctx, monkeypatch): | CODE |
| LOW | tests/test_attach_verification.py | 153 | def test_orphan_shell_deleted_when_upload_fails(self, dummy_ctx, monkeypatch): | CODE |
| LOW | tests/test_attach_verification.py | 167 | def test_local_client_fails_fast(self, dummy_ctx): | CODE |
| LOW | tests/test_set_item_parent.py | 18 | def test_schema_requires_an_explicit_nullable_parent_key(): | CODE |
| LOW | tests/test_set_item_parent.py | 72 | def test_sends_minimal_versioned_parent_update( | CODE |
| LOW | tests/test_set_item_parent.py | 94 | def test_surfaces_api_exception(monkeypatch): | CODE |
| LOW | tests/test_set_item_parent.py | 105 | def test_reports_unsuccessful_response(monkeypatch): | CODE |
| LOW | tests/test_set_item_parent.py | 114 | def test_surfaces_item_fetch_error_without_writing(monkeypatch): | CODE |
| LOW⚡ | tests/test_page_layout.py | 53 | def test_figure_caption_with_colon(self): | CODE |
| LOW⚡ | tests/test_page_layout.py | 60 | def test_figure_caption_with_period(self): | CODE |
| LOW⚡ | tests/test_page_layout.py | 66 | def test_fig_abbreviation_with_panel_letter(self): | CODE |
| LOW⚡ | tests/test_page_layout.py | 78 | def test_supplementary_table_caption(self): | CODE |
| LOW⚡ | tests/test_page_layout.py | 84 | def test_extended_data_figure_caption(self): | CODE |
| LOW⚡ | tests/test_page_layout.py | 90 | def test_in_text_reference_rejected(self): | CODE |
| LOW⚡ | tests/test_page_layout.py | 94 | def test_sentence_starting_with_figure_rejected(self): | CODE |
| LOW⚡ | tests/test_page_layout.py | 104 | def test_caption_is_case_insensitive(self): | CODE |
| LOW⚡ | tests/test_page_layout.py | 180 | def test_merges_vertically_adjacent_fragments(self): | CODE |
| LOW⚡ | tests/test_page_layout.py | 189 | def test_keeps_distant_regions_separate(self): | CODE |
| LOW⚡ | tests/test_page_layout.py | 197 | def test_dedupes_same_area_keeping_table(self): | CODE |
| LOW⚡ | tests/test_page_layout.py | 221 | def test_figure_caption_below_figure(self): | CODE |
| LOW⚡ | tests/test_page_layout.py | 231 | def test_table_caption_above_table(self): | CODE |
| LOW⚡ | tests/test_page_layout.py | 240 | def test_caption_too_far_not_attached(self): | CODE |
| LOW⚡ | tests/test_page_layout.py | 250 | def test_two_column_caption_not_cross_attached(self): | CODE |
| LOW⚡ | tests/test_page_layout.py | 260 | def test_caption_attaches_to_nearest_of_two_stacked_figures(self): | CODE |
| LOW⚡ | tests/test_page_layout.py | 708 | def test_rejects_invalid_page_number(self, monkeypatch, fake_zot): | CODE |
| LOW⚡ | tests/test_page_layout.py | 718 | def test_requires_a_zotero_client(self, monkeypatch): | CODE |
| LOW⚡ | tests/test_page_layout.py | 728 | def test_works_with_local_client_only(self, monkeypatch, fake_zot): | CODE |
| LOW | tests/test_page_layout.py | 155 | def test_drops_full_page_background(self): | CODE |
| LOW | tests/test_page_layout.py | 164 | def test_merges_overlapping_fragments(self): | CODE |
| LOW | tests/test_page_layout.py | 274 | def test_each_caption_attaches_to_one_region_only(self): | CODE |
| LOW | tests/test_page_layout.py | 287 | def test_region_without_caption_gets_low_confidence(self): | CODE |
| LOW | tests/test_page_layout.py | 317 | def test_detects_raster_image_with_caption(self): | CODE |
| LOW | tests/test_page_layout.py | 348 | def test_detects_vector_drawing(self): | CODE |
| LOW | tests/test_page_layout.py | 370 | def test_detects_table_keeping_table_source(self): | CODE |
| LOW | tests/test_page_layout.py | 400 | def test_text_only_page_returns_no_regions(self): | CODE |
| LOW | tests/test_page_layout.py | 418 | def test_page_out_of_range_returns_error(self): | CODE |
| LOW | tests/test_page_layout.py | 431 | def test_full_page_scan_returns_warning(self): | CODE |
| LOW | tests/test_page_layout.py | 451 | def test_missing_cluster_drawings_adds_warning(self, monkeypatch): | CODE |
| LOW | tests/test_page_layout.py | 471 | def test_regions_have_sequential_ids_and_normalized_bboxes(self): | CODE |
| LOW | tests/test_page_layout.py | 496 | def test_rotated_page_bboxes_stay_normalized(self): | CODE |
| LOW | tests/test_page_layout.py | 518 | def test_cropped_page_bboxes_stay_normalized(self): | CODE |
| LOW | tests/test_page_layout.py | 549 | def test_invalid_pdf_returns_error(self): | CODE |
| LOW | tests/test_page_layout.py | 611 | def test_happy_path_renders_regions_table(self, monkeypatch, fake_zot): | CODE |
| LOW | tests/test_page_layout.py | 650 | def test_no_regions_returns_guidance(self, monkeypatch, fake_zot): | CODE |
| LOW | tests/test_page_layout.py | 661 | def test_warnings_are_included(self, monkeypatch, fake_zot): | CODE |
| LOW | tests/test_page_layout.py | 675 | def test_detection_error_is_returned(self, monkeypatch, fake_zot): | CODE |
| LOW | tests/test_page_layout.py | 686 | def test_rejects_non_pdf_attachment(self, monkeypatch, fake_zot): | CODE |
| LOW | tests/test_page_layout.py | 697 | def test_rejects_non_attachment_item(self, monkeypatch, fake_zot): | CODE |
| LOW | tests/test_gemini_batch.py | 38 | def test_split_embedding_records_respects_request_limit(): | CODE |
| LOW | tests/test_gemini_batch.py | 48 | def test_parse_embedding_output_matches_by_key(): | CODE |
| LOW | tests/test_gemini_batch.py | 59 | def test_parse_embedding_output_falls_back_to_positional_order(): | CODE |
| LOW | tests/test_gemini_batch.py | 70 | def test_submit_embedding_batches_writes_manifest_and_jsonl(tmp_path): | CODE |
| 1983 more matches not shown… | ||||
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | experiments/library-graph-validation/01_build_corpus.py | 114 | except Exception as e: | STRING |
| MEDIUM | experiments/library-graph-validation/01_build_corpus.py | 111 | def main(): | CODE |
| LOW | tests/test_stringified_params.py | 44 | except Exception: | CODE |
| LOW⚡ | tests/test_stringified_params.py | 117 | except Exception as exc: | CODE |
| LOW⚡ | tests/test_stringified_params.py | 132 | except Exception as exc: | CODE |
| LOW | tests/test_issue_431_server_wedge.py | 302 | except Exception as exc: # pragma: no cover - failure detail | CODE |
| MEDIUM | tests/test_issue_431_server_wedge.py | 296 | def worker(delay): | CODE |
| LOW⚡ | tests/test_ollama_embedding.py | 72 | except Exception: | CODE |
| LOW | tests/test_embedding_function_registration.py | 132 | except Exception: | CODE |
| LOW | tests/test_description_tokens.py | 103 | except Exception: | CODE |
| MEDIUM | tests/test_rate_limiter.py | 341 | def worker(): | CODE |
| LOW | tests/test_rate_limiter.py | 349 | except Exception as exc: # pragma: no cover - failure path | CODE |
| LOW | tests/live/conftest.py | 96 | except Exception: | CODE |
| LOW | tests/live/conftest.py | 163 | except Exception as exc: | CODE |
| LOW | tests/live/conftest.py | 168 | except Exception as exc: | CODE |
| LOW | tests/live/conftest.py | 242 | except Exception: | CODE |
| LOW | tests/live/generate_sentinels.py | 63 | except Exception: | CODE |
| LOW | tests/live/generate_sentinels.py | 106 | except Exception as exc: | CODE |
| LOW | tests/live/_discovery.py | 39 | except Exception: | CODE |
| LOW | tests/live/_discovery.py | 107 | except Exception: | CODE |
| LOW | scripts/check_openai_ratelimits.py | 46 | except Exception as e: | CODE |
| LOW | scripts/check_openai_ratelimits.py | 132 | except Exception as e: | CODE |
| LOW | src/zotero_mcp/better_bibtex_client.py | 123 | except Exception: | CODE |
| LOW | src/zotero_mcp/better_bibtex_client.py | 147 | except Exception as e: | CODE |
| LOW | src/zotero_mcp/better_bibtex_client.py | 191 | except Exception as e: | CODE |
| LOW | src/zotero_mcp/better_bibtex_client.py | 245 | except Exception as e: | CODE |
| MEDIUM | src/zotero_mcp/better_bibtex_client.py | 246 | print(f"Error searching for cite keys: {e}") | CODE |
| LOW | src/zotero_mcp/better_bibtex_client.py | 319 | except Exception as e: | CODE |
| LOW⚡ | src/zotero_mcp/better_bibtex_client.py | 401 | except Exception as e: | CODE |
| MEDIUM⚡ | src/zotero_mcp/better_bibtex_client.py | 402 | print(f"Error processing annotation: {e}") | CODE |
| LOW | src/zotero_mcp/pdf_utils.py | 829 | except Exception: | CODE |
| LOW | src/zotero_mcp/config_light.py | 52 | except Exception as e: | CODE |
| LOW | src/zotero_mcp/config_light.py | 110 | except Exception as e: | CODE |
| LOW | src/zotero_mcp/config_light.py | 137 | except Exception: | CODE |
| LOW | src/zotero_mcp/html_metadata.py | 164 | except Exception: | CODE |
| LOW | src/zotero_mcp/client.py | 264 | except Exception: | CODE |
| LOW | src/zotero_mcp/client.py | 399 | except Exception: | CODE |
| LOW | src/zotero_mcp/client.py | 506 | except Exception as e: | CODE |
| LOW | src/zotero_mcp/client.py | 626 | except Exception: | CODE |
| LOW | src/zotero_mcp/client.py | 711 | except Exception: | CODE |
| LOW | src/zotero_mcp/client.py | 793 | except Exception as exc: | CODE |
| LOW | src/zotero_mcp/client.py | 812 | except Exception as exc: | CODE |
| LOW | src/zotero_mcp/client.py | 843 | except Exception as exc: | CODE |
| MEDIUM⚡ | src/zotero_mcp/setup_helper.py | 557 | print(f"Error saving semantic search config: {e}") | CODE |
| MEDIUM | src/zotero_mcp/setup_helper.py | 94 | print(f"Error searching for zotero-mcp: {e}") | CODE |
| MEDIUM | src/zotero_mcp/setup_helper.py | 601 | print(f"Error: Config file at {config_path} is not valid JSON. Creating new config.") | CODE |
| MEDIUM | src/zotero_mcp/setup_helper.py | 665 | print(f"Error writing config file: {str(e)}") | CODE |
| MEDIUM | src/zotero_mcp/setup_helper.py | 773 | print("Error: Could not find zotero-mcp executable.") | CODE |
| MEDIUM | src/zotero_mcp/setup_helper.py | 786 | print("Error: Could not determine Claude Desktop config path.") | CODE |
| LOW⚡ | src/zotero_mcp/setup_helper.py | 556 | except Exception as e: | CODE |
| LOW⚡ | src/zotero_mcp/setup_helper.py | 567 | except Exception: | CODE |
| LOW | src/zotero_mcp/setup_helper.py | 93 | except Exception as e: | CODE |
| LOW | src/zotero_mcp/setup_helper.py | 583 | except Exception as e: | CODE |
| LOW | src/zotero_mcp/setup_helper.py | 664 | except Exception as e: | CODE |
| LOW | src/zotero_mcp/setup_helper.py | 683 | except Exception: | CODE |
| LOW | src/zotero_mcp/setup_helper.py | 867 | except Exception: | CODE |
| LOW | src/zotero_mcp/setup_helper.py | 934 | except Exception as e: | CODE |
| LOW | src/zotero_mcp/pdfannots_downloader.py | 183 | except Exception as e: | CODE |
| MEDIUM | src/zotero_mcp/pdfannots_downloader.py | 184 | print(f"Error downloading pdfannots2json: {e}") | CODE |
| MEDIUM⚡ | src/zotero_mcp/cli_standalone.py | 936 | print(f"Error: {message}", file=sys.stderr) | CODE |
| 391 more matches not shown… | ||||
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | tests/test_set_item_parent.py | 3 | CODE | |
| LOW | tests/test_shared_helpers.py | 4 | CODE | |
| LOW | tests/test_shared_helpers.py | 4 | CODE | |
| LOW | tests/test_toolsets.py | 13 | CODE | |
| LOW | tests/test_add_by_url.py | 7 | CODE | |
| LOW | tests/test_include_subcollections.py | 18 | CODE | |
| LOW | tests/test_delete_item.py | 9 | CODE | |
| LOW | tests/test_add_by_isbn.py | 8 | CODE | |
| LOW | tests/test_add_by_isbn.py | 10 | CODE | |
| LOW | tests/test_stringified_params.py | 31 | CODE | |
| LOW | tests/test_batch_adapter_compat.py | 20 | CODE | |
| LOW | tests/test_add_from_file.py | 5 | CODE | |
| LOW | tests/test_add_from_file.py | 6 | CODE | |
| LOW | tests/test_add_from_file.py | 6 | CODE | |
| LOW | tests/test_add_from_file.py | 9 | CODE | |
| LOW | tests/test_add_from_file.py | 9 | CODE | |
| LOW | tests/test_update_item.py | 3 | CODE | |
| LOW | tests/test_token_optimization.py | 4 | CODE | |
| LOW | tests/test_token_optimization.py | 6 | CODE | |
| LOW | tests/test_v021_fixes.py | 8 | CODE | |
| LOW | tests/test_v021_fixes.py | 10 | CODE | |
| LOW | tests/test_tag_filter_normalization.py | 10 | CODE | |
| LOW | tests/test_add_by_doi.py | 4 | CODE | |
| LOW | tests/test_api_lock_bounded.py | 14 | CODE | |
| LOW | tests/test_search_improvements.py | 3 | CODE | |
| LOW | tests/test_search_improvements.py | 4 | CODE | |
| LOW | tests/test_search_improvements.py | 5 | CODE | |
| LOW | tests/test_zotero_ft_cache.py | 22 | CODE | |
| LOW | tests/test_pdf_cascade.py | 4 | CODE | |
| LOW | tests/test_pdf_cascade.py | 6 | CODE | |
| LOW | tests/test_pdf_cascade.py | 17 | CODE | |
| LOW | tests/test_item_related.py | 3 | CODE | |
| LOW | tests/test_regression_bugs.py | 8 | CODE | |
| LOW | tests/test_duplicates.py | 6 | CODE | |
| LOW | tests/test_duplicates.py | 8 | CODE | |
| LOW | tests/_search_corpus.py | 21 | CODE | |
| LOW | tests/test_search_parity_offline.py | 22 | CODE | |
| LOW | tests/test_generic_batch_flags.py | 177 | CODE | |
| LOW | tests/test_generic_batch_flags.py | 178 | CODE | |
| LOW | tests/test_pdf_outline.py | 3 | CODE | |
| LOW | tests/test_pdf_outline.py | 5 | CODE | |
| LOW | tests/test_pdf_outline.py | 5 | CODE | |
| LOW | tests/live/generate_sentinels.py | 42 | CODE | |
| LOW | tests/live/test_embedding_sentinels.py | 25 | CODE | |
| LOW | tests/live/test_fault_injection.py | 19 | CODE | |
| LOW | tests/live/test_chroma_roundtrip_live.py | 19 | CODE | |
| LOW | scripts/measure_context_cost.py | 25 | CODE | |
| LOW | scripts/measure_context_cost.py | 100 | CODE | |
| LOW | scripts/gen_basefield_map.py | 21 | CODE | |
| LOW | src/zotero_mcp/webdav.py | 3 | CODE | |
| LOW | src/zotero_mcp/server.py | 14 | CODE | |
| LOW | src/zotero_mcp/server.py | 18 | CODE | |
| LOW | src/zotero_mcp/server.py | 18 | CODE | |
| LOW | src/zotero_mcp/server.py | 18 | CODE | |
| LOW | src/zotero_mcp/server.py | 18 | CODE | |
| LOW | src/zotero_mcp/server.py | 18 | CODE | |
| LOW | src/zotero_mcp/server.py | 18 | CODE | |
| LOW | src/zotero_mcp/server.py | 18 | CODE | |
| LOW | src/zotero_mcp/server.py | 18 | CODE | |
| LOW | src/zotero_mcp/server.py | 18 | CODE | |
| 138 more matches not shown… | ||||
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | …eriments/library-graph-validation/04_build_subgraph.py | 47 | CODE | |
| LOW | tests/test_chroma_client_real.py | 363 | CODE | |
| LOW | tests/test_rate_limiter.py | 326 | CODE | |
| LOW | tests/test_rate_limiter.py | 341 | CODE | |
| LOW | tests/live/_discovery.py | 31 | CODE | |
| LOW | scripts/gen_skill_reference.py | 60 | CODE | |
| LOW | src/zotero_mcp/webdav.py | 222 | CODE | |
| LOW | src/zotero_mcp/better_bibtex_client.py | 126 | CODE | |
| LOW | src/zotero_mcp/better_bibtex_client.py | 249 | CODE | |
| LOW | src/zotero_mcp/config_light.py | 57 | CODE | |
| LOW | src/zotero_mcp/client.py | 461 | CODE | |
| LOW | src/zotero_mcp/toolsets.py | 148 | CODE | |
| LOW | src/zotero_mcp/setup_helper.py | 110 | CODE | |
| LOW | src/zotero_mcp/setup_helper.py | 207 | CODE | |
| LOW | src/zotero_mcp/setup_helper.py | 521 | CODE | |
| LOW | src/zotero_mcp/setup_helper.py | 588 | CODE | |
| LOW | src/zotero_mcp/setup_helper.py | 714 | CODE | |
| LOW | src/zotero_mcp/pdfannots_downloader.py | 133 | CODE | |
| LOW | src/zotero_mcp/cli_standalone.py | 193 | CODE | |
| LOW | src/zotero_mcp/cli_standalone.py | 250 | CODE | |
| LOW | src/zotero_mcp/cli_standalone.py | 363 | CODE | |
| LOW | src/zotero_mcp/cli_standalone.py | 413 | CODE | |
| LOW | src/zotero_mcp/cli_standalone.py | 473 | CODE | |
| LOW | src/zotero_mcp/cli_standalone.py | 636 | CODE | |
| LOW | src/zotero_mcp/updater.py | 45 | CODE | |
| LOW | src/zotero_mcp/updater.py | 122 | CODE | |
| LOW | src/zotero_mcp/updater.py | 319 | CODE | |
| LOW | src/zotero_mcp/local_db.py | 1754 | CODE | |
| LOW | src/zotero_mcp/epub_utils.py | 471 | CODE | |
| LOW | src/zotero_mcp/epub_utils.py | 703 | CODE | |
| LOW | src/zotero_mcp/cli.py | 54 | CODE | |
| LOW | src/zotero_mcp/cli.py | 105 | CODE | |
| LOW | src/zotero_mcp/cli.py | 434 | CODE | |
| LOW | src/zotero_mcp/utils.py | 79 | CODE | |
| LOW | src/zotero_mcp/utils.py | 114 | CODE | |
| LOW | src/zotero_mcp/chroma_client.py | 48 | CODE | |
| LOW | src/zotero_mcp/chroma_client.py | 265 | CODE | |
| LOW | src/zotero_mcp/citation_import.py | 192 | CODE | |
| LOW | src/zotero_mcp/citation_import.py | 219 | CODE | |
| LOW | src/zotero_mcp/citation_import.py | 378 | CODE | |
| LOW | src/zotero_mcp/citation_import.py | 583 | CODE | |
| LOW | src/zotero_mcp/pdf_layout.py | 140 | CODE | |
| LOW | src/zotero_mcp/pdf_layout.py | 218 | CODE | |
| LOW | src/zotero_mcp/pdf_layout.py | 314 | CODE | |
| LOW | src/zotero_mcp/fulltext_cache.py | 218 | CODE | |
| LOW | src/zotero_mcp/gemini_batch.py | 267 | CODE | |
| LOW | src/zotero_mcp/semantic_search.py | 331 | CODE | |
| LOW | src/zotero_mcp/semantic_search.py | 671 | CODE | |
| LOW | src/zotero_mcp/semantic_search.py | 1015 | CODE | |
| LOW | src/zotero_mcp/semantic_search.py | 1302 | CODE | |
| LOW | src/zotero_mcp/semantic_search.py | 1980 | CODE | |
| LOW | src/zotero_mcp/semantic_search.py | 2155 | CODE | |
| LOW | src/zotero_mcp/semantic_search.py | 2776 | CODE | |
| LOW | src/zotero_mcp/semantic_search.py | 2903 | CODE | |
| LOW | src/zotero_mcp/semantic_search.py | 3115 | CODE | |
| LOW | src/zotero_mcp/semantic_search.py | 3138 | CODE | |
| LOW | src/zotero_mcp/semantic_search.py | 3249 | CODE | |
| LOW | src/zotero_mcp/semantic_search.py | 3521 | CODE | |
| LOW | src/zotero_mcp/semantic_search.py | 3598 | CODE | |
| LOW | src/zotero_mcp/semantic_search.py | 2960 | CODE | |
| 58 more matches not shown… | ||||
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM | CHANGELOG.md | 92 | - **`update-db` no longer accepts `chunking.enabled` in silence on the OpenAI Batch API path (#416).** The batch path bu | CODE |
| MEDIUM | …ents/library-graph-validation/data/graph_contexts.json | 3 | "C": "### AlphaFold Meets Flow Matching for Generating Protein Ensembles [via-traversal]\n- [claim] Score-matching | CODE |
| MEDIUM | …ents/library-graph-validation/data/graph_contexts.json | 49 | "C": "### Protein Conformation Generation via Force-Guided SE(3) Diffusion Models [SEED]\n- [claim] MD simulations | CODE |
| MEDIUM | tests/test_fulltext_web_mode.py | 320 | # --------- Integration tests: update_database orchestration ---------- | COMMENT |
| MEDIUM⚡ | tests/test_webdav.py | 286 | # _webdav_first_attach (shell + PUT orchestration) | COMMENT |
| MEDIUM⚡ | tests/test_trashed_collections.py | 23 | # Test scaffolding | COMMENT |
| MEDIUM⚡ | tests/test_identifier_lock.py | 217 | # removed (proving nothing) or deadlocked on its own scaffolding. A flaky or | COMMENT |
| MEDIUM | src/zotero_mcp/setup_helper.py | 240 | # Choose embedding model | COMMENT |
| MEDIUM | src/zotero_mcp/chroma_client.py | 542 | # Pick the embedding model. config.json is the richer, authoritative source | COMMENT |
| MEDIUM | src/zotero_mcp/semantic_search.py | 2861 | # Truncate to fit the configured embedding model's token limit | COMMENT |
| MEDIUM | src/zotero_mcp/tools/search.py | 1519 | # guard, but neither loads an embedding model or a Zotero client. | COMMENT |
| MEDIUM | src/zotero_mcp/tools/search.py | 1533 | # Read status without loading any embedding model (fast, no network). | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| HIGH | tests/test_issue_372_pdf_tools.py | 0 | create table items ( itemid integer primary key, itemtypeid int, dateadded text, datemodified text, clientdatemodified t | STRING |
| HIGH | tests/test_fulltext_sync_watermark.py | 0 | create table items ( itemid integer primary key, itemtypeid int, dateadded text, datemodified text, clientdatemodified t | STRING |
| HIGH | tests/test_collection_scope_expansion.py | 0 | create table items ( itemid integer primary key, itemtypeid int, dateadded text, datemodified text, clientdatemodified t | STRING |
| HIGH | tests/test_linked_file_attachment_download.py | 0 | create table items ( itemid integer primary key, itemtypeid int, dateadded text, datemodified text, clientdatemodified t | STRING |
| HIGH | src/zotero_mcp/openai_batch.py | 0 | submit ``adapter``'s pending chunks while they fit the enqueued-token budget. returns the number of newly submitted chun | STRING |
| HIGH | src/zotero_mcp/gemini_batch.py | 0 | submit ``adapter``'s pending chunks while they fit the enqueued-token budget. returns the number of newly submitted chun | STRING |
| HIGH | src/zotero_mcp/batch_common.py | 0 | submit ``adapter``'s pending chunks while they fit the enqueued-token budget. returns the number of newly submitted chun | STRING |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM | …ents/library-graph-validation/data/graph_contexts.json | 3 | "C": "### AlphaFold Meets Flow Matching for Generating Protein Ensembles [via-traversal]\n- [claim] Score-matching | CODE |
| MEDIUM | …ents/library-graph-validation/data/graph_contexts.json | 49 | "C": "### Protein Conformation Generation via Force-Guided SE(3) Diffusion Models [SEED]\n- [claim] MD simulations | CODE |
| MEDIUM | …ents/library-graph-validation/data/graph_contexts.json | 89 | "C": "### Diffusion Schr\u00f6dinger Bridge Matching [SEED]\n- [claim] Unlike DDMs and Flow Matching Models, which | CODE |
| MEDIUM | …ents/library-graph-validation/data/graph_contexts.json | 133 | "C": "### A DNA language model based on multispecies alignment predicts the effects of genome-wide variants [SEED]\ | CODE |
| MEDIUM | …ents/library-graph-validation/data/graph_contexts.json | 133 | "C": "### A DNA language model based on multispecies alignment predicts the effects of genome-wide variants [SEED]\ | CODE |
| MEDIUM⚡ | tests/test_rate_limiter.py | 317 | """on_success must be robust to missing, empty, non-numeric, or zero-limit headers.""" | STRING |
| MEDIUM | src/zotero_mcp/skill_install.py | 243 | # The command reference does NOT go in the rules directory. A harness that | COMMENT |
| MEDIUM⚡ | src/zotero_mcp/skill_install.py | 293 | #: Every supported harness: how to detect it, and how to install into it. | COMMENT |
| MEDIUM⚡ | src/zotero_mcp/skill_install.py | 295 | #: `detect` answers "is this harness in use here?" from what is on disk. It is | COMMENT |
| MEDIUM⚡ | src/zotero_mcp/skill_install.py | 296 | #: deliberately generous — a directory or file the harness owns is enough, | COMMENT |
| MEDIUM | src/zotero_mcp/tools/write.py | 2193 | # changes. The robust fix is the same one noted in add_by_doi's | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| HIGH⚡ | tests/test_sql_search_backend.py | 218 | result = reader.search_items_sql("quantum", item_type="book || journalArticle", group_id=0) | CODE |
| HIGH | tests/test_better_bibtex_client.py | 150 | """BBT answers ``{"KEY": null}`` — a *successful* RPC carrying no key — | STRING |
| HIGH | tests/test_bibtex_never_empty.py | 9 | ``{"KGBSSZ3W": null}`` — a successful RPC carrying no key. ``export_bibtex`` | STRING |
| HIGH⚡ | tests/test_global_search.py | 242 | pipes = reader.search_items_sql("Paper", tag=["methods || methodology"], limit=50) | CODE |
| HIGH | src/zotero_mcp/tools/_helpers.py | 457 | # A bare JSON scalar (int/float/bool/null) — e.g. an all-digit | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM | tests/test_regression_bugs.py | 192 | # Create a dummy file | COMMENT |
| MEDIUM | src/zotero_mcp/client.py | 251 | # Create a local client - library_id 0 is the default for local. | COMMENT |
| MEDIUM | src/zotero_mcp/pdfannots_downloader.py | 155 | # Create a temporary directory for the download | COMMENT |
| MEDIUM | src/zotero_mcp/_app.py | 113 | # Create an MCP server (fastmcp 2.14+ no longer accepts `dependencies`) | COMMENT |
| MEDIUM | src/zotero_mcp/tools/write.py | 4348 | # Create the metadata item. With if_exists='file' and a known DOI, | COMMENT |
| MEDIUM | src/zotero_mcp/tools/write.py | 4362 | # Create a basic item | COMMENT |
| MEDIUM | src/zotero_mcp/tools/retrieval.py | 389 | # Create a mapping of collection IDs to their data | COMMENT |
| MEDIUM | src/zotero_mcp/tools/retrieval.py | 392 | # Create a mapping of parent to child collections | COMMENT |
| MEDIUM | src/zotero_mcp/tools/annotations.py | 1732 | # Create the annotation using web client | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW⚡ | src/zotero_mcp/config.py | 21 | logger = logging.getLogger(__name__) | CODE |
| LOW | src/zotero_mcp/config_light.py | 29 | logger = logging.getLogger(__name__) | CODE |
| LOW | src/zotero_mcp/identifiers.py | 24 | __all__ = ["normalize_doi"] | CODE |
| LOW | src/zotero_mcp/client.py | 39 | logger = logging.getLogger(__name__) | CODE |
| LOW | src/zotero_mcp/client.py | 134 | def set_active_library(library_id: str, library_type: str) -> None: | CODE |
| LOW | src/zotero_mcp/__init__.py | 14 | __all__ = ["__version__", "mcp"] | CODE |
| LOW | src/zotero_mcp/updater.py | 23 | logger = logging.getLogger(__name__) | CODE |
| LOW | src/zotero_mcp/local_db.py | 32 | logger = logging.getLogger(__name__) | CODE |
| LOW | src/zotero_mcp/chroma_client.py | 42 | logger = logging.getLogger(__name__) | CODE |
| LOW | src/zotero_mcp/chroma_client.py | 487 | def update_metadatas(self, ids: list[str], metadatas: list[dict[str, Any]]) -> None: | CODE |
| LOW | src/zotero_mcp/fulltext_cache.py | 47 | logger = logging.getLogger(__name__) | CODE |
| LOW | src/zotero_mcp/gemini_batch.py | 46 | logger = logging.getLogger(__name__) | CODE |
| LOW | src/zotero_mcp/extract.py | 27 | logger = logging.getLogger(__name__) | CODE |
| LOW⚡ | src/zotero_mcp/scite_client.py | 19 | logger = logging.getLogger(__name__) | CODE |
| LOW | src/zotero_mcp/semantic_search.py | 50 | logger = logging.getLogger(__name__) | CODE |
| LOW⚡ | src/zotero_mcp/tools/scite.py | 28 | logger = logging.getLogger(__name__) | CODE |
| LOW | src/zotero_mcp/embeddings/__init__.py | 22 | __all__ = ["BaseEmbeddingFunction"] | CODE |
| LOW | src/zotero_mcp/embeddings/base.py | 54 | logger = logging.getLogger(__name__) | CODE |
| LOW | src/zotero_mcp/embeddings/providers/__init__.py | 32 | logger = logging.getLogger(__name__) | CODE |
| LOW | src/zotero_mcp/embeddings/providers/__init__.py | 34 | __all__ = [ | CODE |
| LOW | src/zotero_mcp/embeddings/providers/huggingface.py | 11 | logger = logging.getLogger(__name__) | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| HIGH | README.md | 335 | "ZOTERO_API_KEY": "YOUR_API_KEY", | CODE |
| HIGH | README.md | 349 | - Click [here](https://www.zotero.org/settings/security#applications) to generate a Zotero API key and replace `YOUR_ | CODE |
| HIGH | README.md | 420 | zotero-mcp setup --no-local --api-key YOUR_API_KEY --library-id YOUR_LIBRARY_ID | CODE |
| HIGH | docs/getting-started.md | 45 | export ZOTERO_API_KEY=your_api_key | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | pyproject.toml | 21 | dependencies = [ | COMMENT |
| LOW | tests/test_identifier_lock.py | 201 | t = threading.Thread(target=holder) | COMMENT |
| LOW | tests/live/conftest.py | 121 | # server, or a paid provider API using the machine's production config) | COMMENT |
| LOW | src/zotero_mcp/client.py | 41 | # Load environment variables | COMMENT |
| LOW | src/zotero_mcp/setup_helper.py | 481 | extraction_config = dict( | COMMENT |
| LOW | src/zotero_mcp/local_db.py | 41 | # therefore does not widen what the index sees; it only stops extraction | COMMENT |
| LOW | src/zotero_mcp/local_db.py | 221 | # | COMMENT |
| LOW | src/zotero_mcp/local_db.py | 281 | # returning it) — so a condition needs a DIFFERENT substring depending on the | COMMENT |
| LOW | src/zotero_mcp/local_db.py | 1301 | COMMENT | |
| LOW | src/zotero_mcp/cli.py | 1161 | # Ensure environment is initialized (Claude config or standalone config) | COMMENT |
| LOW | src/zotero_mcp/chroma_client.py | 541 | COMMENT | |
| LOW | src/zotero_mcp/semantic_search.py | 201 | max_chars = max_tokens * 2 | COMMENT |
| LOW | src/zotero_mcp/semantic_search.py | 1361 | # is safe to write: it extracts under the *indexing* page | COMMENT |
| LOW | src/zotero_mcp/semantic_search.py | 2361 | # (_prepare_index_records) and has no chunking step, so a config | COMMENT |
| LOW | src/zotero_mcp/tools/write.py | 1581 | # held the lock across the ENTIRE recursive multi-DOI loop, one PDF | COMMENT |
| LOW | src/zotero_mcp/tools/write.py | 1681 | # holding several of them at once deadlock-free. | COMMENT |
| LOW | src/zotero_mcp/tools/write.py | 1921 | create_missing_collections: bool = False, | COMMENT |
| LOW | src/zotero_mcp/tools/write.py | 2281 | # arXiv always has a free PDF — try to attach it. Outside the lock: the | COMMENT |
| LOW | src/zotero_mcp/tools/write.py | 3921 | COMMENT | |
| LOW | src/zotero_mcp/tools/write.py | 3941 | # prints — is noise to be discarded (#455). | COMMENT |
| LOW | src/zotero_mcp/tools/search.py | 1001 | # SQLite backend evaluates the identical rules — see that module's | COMMENT |
| LOW | src/zotero_mcp/tools/search.py | 1041 | "and global search has no fallback — the client-side path " | COMMENT |
| LOW | src/zotero_mcp/tools/_helpers.py | 21 | from zotero_mcp import client as _client | COMMENT |
| LOW | src/zotero_mcp/tools/_helpers.py | 841 | # failure here degrades to "no match" and the caller creates the item, | COMMENT |
| LOW | src/zotero_mcp/tools/_helpers.py | 921 | # fetch now sits between the dedup check and the create, so two callers can | COMMENT |
| LOW | src/zotero_mcp/embeddings/providers/gemini.py | 21 | # task instruction in the prompt text itself, which empirically shifts | COMMENT |
| LOW | src/zotero_mcp/embeddings/providers/gemini.py | 41 | # prefix tokens are reserved separately (see V2_PREFIX_TOKEN_BUDGET). | COMMENT |
| LOW | src/zotero_mcp/embeddings/providers/openai.py | 21 | """ | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | src/zotero_mcp/pdfannots_downloader.py | 69 | # Check if we have a URL for this platform/architecture | COMMENT |
| LOW⚡ | src/zotero_mcp/updater.py | 62 | # Check if we're in a uv-managed project | COMMENT |
| LOW⚡ | src/zotero_mcp/updater.py | 77 | # Check if we're in a uv virtual environment | COMMENT |
| LOW | src/zotero_mcp/updater.py | 101 | # Check if pipx is available | COMMENT |
| LOW | src/zotero_mcp/updater.py | 468 | # Check if update is needed. Only a strictly newer release counts — a | COMMENT |
| LOW | src/zotero_mcp/cli.py | 711 | # Check if installed via uv | COMMENT |
| LOW | src/zotero_mcp/chroma_client.py | 103 | # Check if the stored config matches what we want; if not, recreate. | COMMENT |
| LOW | src/zotero_mcp/tools/write.py | 4757 | # Check if relation already exists (match both URI prefix variants) | COMMENT |
| LOW | src/zotero_mcp/tools/annotations.py | 257 | # Check if Zotero with Better BibTeX is running | COMMENT |
| LOW | src/zotero_mcp/tools/annotations.py | 1299 | # Check if creation was successful | COMMENT |
| LOW | src/zotero_mcp/tools/annotations.py | 1735 | # Check if creation was successful | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW⚡ | docs/integration-test-plan.md | 491 | ### Step 1: Verify all per-phase cleanups were done | COMMENT |
| LOW⚡ | docs/integration-test-plan.md | 499 | ### Step 2: Identify test-created items | COMMENT |
| LOW⚡ | docs/integration-test-plan.md | 505 | ### Step 3: Tell the user to delete manually | COMMENT |
| LOW | src/zotero_mcp/better_bibtex_client.py | 268 | # Step 1: Get citation key from item key. BBT's JSON-RPC expects | COMMENT |
| LOW | src/zotero_mcp/better_bibtex_client.py | 286 | # Step 2: Export BibTeX using the citation key. | COMMENT |
| LOW⚡ | src/zotero_mcp/tools/annotations.py | 799 | # Step 2: Batch-fetch the grandparents (papers) | COMMENT |
| LOW⚡ | src/zotero_mcp/tools/annotations.py | 823 | # Step 3: Map immediate parent keys to stable paper/attachment context. | COMMENT |
| LOW | src/zotero_mcp/tools/annotations.py | 768 | # Step 1: Batch-fetch the immediate parents (attachments) | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | tests/test_update_item.py | 13 | CODE | |
| LOW | tests/test_update_item.py | 59 | CODE | |
| LOW | tests/test_update_item.py | 86 | CODE | |
| LOW | tests/test_update_item.py | 122 | CODE | |
| LOW | tests/test_remote_embedding_base.py | 142 | CODE | |
| LOW | src/zotero_mcp/utils.py | 450 | CODE | |
| LOW | src/zotero_mcp/semantic_search.py | 2155 | CODE | |
| LOW | src/zotero_mcp/tools/write.py | 831 | CODE | |
| LOW | src/zotero_mcp/tools/write.py | 3730 | CODE | |
| LOW | src/zotero_mcp/embeddings/ratelimit.py | 62 | CODE | |
| LOW | src/zotero_mcp/embeddings/providers/gemini.py | 70 | CODE | |
| LOW | src/zotero_mcp/embeddings/providers/openai.py | 56 | CODE | |
| LOW | src/zotero_mcp/embeddings/providers/ollama.py | 43 | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | src/zotero_mcp/better_bibtex_client.py | 268 | # Step 1: Get citation key from item key. BBT's JSON-RPC expects | COMMENT |
| LOW | src/zotero_mcp/better_bibtex_client.py | 286 | # Step 2: Export BibTeX using the citation key. | COMMENT |
| LOW⚡ | src/zotero_mcp/tools/annotations.py | 799 | # Step 2: Batch-fetch the grandparents (papers) | COMMENT |
| LOW⚡ | src/zotero_mcp/tools/annotations.py | 823 | # Step 3: Map immediate parent keys to stable paper/attachment context. | COMMENT |
| LOW | src/zotero_mcp/tools/annotations.py | 768 | # Step 1: Batch-fetch the immediate parents (attachments) | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW⚡ | tests/test_place_field_reads.py | 128 | fake_data = { | CODE |
| LOW | tests/test_place_field_reads.py | 143 | return {"key": key, "data": fake_data} | CODE |
| LOW | tests/test_add_by_isbn.py | 104 | "authors": ["Jane Doe"], | CODE |
| LOW⚡ | tests/test_batch_crossref.py | 110 | monkeypatch.setenv("ZOTERO_MCP_CONTACT_EMAIL", "me@example.org") | CODE |
| LOW⚡ | tests/test_batch_crossref.py | 115 | assert calls[0]["params"]["mailto"] == "me@example.org" | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| HIGH | src/zotero_mcp/better_bibtex_client.py | 250 | Export BibTeX for a specific item using its item key. Args: item_key: Zotero item key to e | STRING |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM | src/zotero_mcp/local_db.py | 1039 | CODE |