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 summarizes scanner evidence for 54yyyu/zotero-mcp, a Python project with 5,318 GitHub stars. SynthScan examined 134,893 lines of code across 464 source files, recording 4897 pattern matches distributed across 20 syntactic categories. The legacy adjusted pattern signal is 57.0 (High pattern signal); it is not an authorship estimate.
Use the findings to ask whether maintainers can explain intent, verify dependencies, simplify unnecessary scaffolding, and focus review effort. Risk Payload v1 adds ownership, supply-chain, over-engineering, and review-effort vectors when available. These are prompts for human verification, not probabilities, security findings, or measured maintenance costs.
Use each vector to guide manual verification of the underlying findings.
Can maintainers explain intent, assumptions, and trade-offs? 3119 finding(s).
Are imports declared and package/API names verified? 233 finding(s).
Are wrappers, guards, and repeated structures necessary? 4439 finding(s).
Which changes need focused human review? 4897 finding(s).
This chart tracks the legacy adjusted pattern score across scan runs. Changes can reflect source changes, scan coverage, or detector/scoring versions; they do not measure AI adoption or prove that a team refactored code. Compare runs only when their scoring versions are compatible. The dashed line tracks the raw pattern-hit count.
Groups findings by configured severity weight. Severity is not a confidence estimate, security rating, or proof that a package is missing. Verify imports against the project's manifests and inspect each finding in its local design context.
This horizontal bar chart shows where configured pattern scores accumulate across directories. Treat the bars as locations to inspect, not as evidence of authorship or code quality. Use the file-level findings to decide whether a directory needs a focused ownership, dependency, complexity, or review check.
The scanner identified 4897 distinct pattern matches across 20 syntactic categories. Each entry below represents a discrete location in the source code where the engine recorded a detector match to inspect in context. 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 configured finding weight (CRITICAL / HIGH / MEDIUM / LOW). The Context column identifies whether the match occurred inside executable code, an inline comment, or a string literal — legacy comment-context matches receive a ×1.5 score weight. The ⚡ bolt icon marks clustered matches: three or more patterns within a 10-line window, each receiving an additional ×1.5 density multiplier under the legacy scoring model.
| 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 | 223 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM | tests/test_citation_import.py | 225 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM | tests/test_citation_import.py | 259 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM | tests/test_citation_import.py | 261 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM | tests/test_citation_import.py | 699 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM | tests/test_citation_import.py | 701 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_citation_import.py | 442 | # --------------------------------------------------------------------------- | STRING |
| MEDIUM⚡ | tests/test_citation_import.py | 450 | # --------------------------------------------------------------------------- | STRING |
| MEDIUM⚡ | tests/test_citation_import.py | 593 | # --------------------------------------------------------------------------- | STRING |
| MEDIUM⚡ | tests/test_citation_import.py | 595 | # --------------------------------------------------------------------------- | STRING |
| MEDIUM⚡ | tests/test_citation_import.py | 627 | # --------------------------------------------------------------------------- | STRING |
| MEDIUM⚡ | tests/test_citation_import.py | 629 | # --------------------------------------------------------------------------- | STRING |
| MEDIUM | tests/test_citation_import.py | 352 | # --------------------------------------------------------------------------- | STRING |
| MEDIUM | tests/test_citation_import.py | 354 | # --------------------------------------------------------------------------- | STRING |
| MEDIUM | tests/test_citation_import.py | 646 | # --------------------------------------------------------------------------- | STRING |
| MEDIUM | tests/test_citation_import.py | 648 | # --------------------------------------------------------------------------- | STRING |
| MEDIUM⚡ | tests/test_shared_helpers.py | 13 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_shared_helpers.py | 15 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_shared_helpers.py | 59 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_shared_helpers.py | 61 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_shared_helpers.py | 81 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_shared_helpers.py | 83 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_shared_helpers.py | 111 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_shared_helpers.py | 113 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_shared_helpers.py | 174 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_shared_helpers.py | 176 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_shared_helpers.py | 219 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_shared_helpers.py | 221 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_shared_helpers.py | 298 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_shared_helpers.py | 300 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_shared_helpers.py | 348 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_shared_helpers.py | 350 | # --------------------------------------------------------------------------- | COMMENT |
| 916 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_unauthorized_local_client(self): | CODE |
| LOW⚡ | tests/test_attach_verification.py | 108 | def test_allows_authorized_local_client(self): | CODE |
| LOW | tests/test_attach_verification.py | 123 | def test_success_passes_through_key(self, dummy_ctx, monkeypatch): | CODE |
| LOW | tests/test_attach_verification.py | 133 | def test_failed_upload_falls_back_to_two_step(self, dummy_ctx, monkeypatch): | CODE |
| LOW | tests/test_attach_verification.py | 146 | def test_reports_failure_when_two_step_also_fails(self, dummy_ctx, monkeypatch): | CODE |
| LOW | tests/test_attach_verification.py | 162 | def test_orphan_shell_deleted_when_upload_fails(self, dummy_ctx, monkeypatch): | CODE |
| LOW | tests/test_attach_verification.py | 176 | def test_unauthorized_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_default_read_backend.py | 24 | def test_unset_follows_local_mode(unset, monkeypatch, local, expected): | CODE |
| LOW⚡ | tests/test_default_read_backend.py | 29 | def test_explicit_api_opts_out_in_local_mode(unset, monkeypatch): | CODE |
| LOW⚡ | tests/test_default_read_backend.py | 36 | def test_explicit_sqlite_wins(unset, monkeypatch, var): | CODE |
| LOW⚡ | tests/test_default_read_backend.py | 67 | def test_unsupported_query_is_answered_by_the_api(): | CODE |
| LOW⚡ | tests/test_default_read_backend.py | 74 | def test_supported_query_never_builds_the_api_client(): | CODE |
| LOW⚡ | tests/test_default_read_backend.py | 82 | def test_a_real_failure_is_not_hidden_by_the_fallback(): | CODE |
| LOW⚡ | tests/test_default_read_backend.py | 88 | def test_get_library_backend_wraps_sqlite_with_the_fallback(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 |
| 2822 more matches not shown… | ||||
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| 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 | 109 | 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 | 101 | except Exception: | CODE |
| LOW | tests/live/conftest.py | 168 | except Exception as exc: | CODE |
| LOW | tests/live/conftest.py | 173 | except Exception as exc: | CODE |
| LOW | tests/live/conftest.py | 247 | 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 | tests/live/test_sqlite_only_reads.py | 72 | except Exception as exc: | 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 | scripts/measure_read_backend.py | 114 | except Exception as exc: | CODE |
| LOW | src/zotero_mcp/library.py | 153 | except Exception: | CODE |
| LOW | src/zotero_mcp/library.py | 312 | except Exception: | CODE |
| LOW | src/zotero_mcp/library.py | 336 | except Exception: | CODE |
| LOW | src/zotero_mcp/library.py | 347 | except Exception: | CODE |
| LOW | src/zotero_mcp/library.py | 357 | except Exception: | CODE |
| LOW | src/zotero_mcp/better_bibtex_client.py | 126 | except Exception: | CODE |
| LOW | src/zotero_mcp/better_bibtex_client.py | 150 | except Exception as e: | CODE |
| LOW | src/zotero_mcp/better_bibtex_client.py | 194 | except Exception as e: | CODE |
| LOW | src/zotero_mcp/better_bibtex_client.py | 248 | except Exception as e: | CODE |
| LOW | src/zotero_mcp/better_bibtex_client.py | 322 | except Exception as e: | CODE |
| LOW | src/zotero_mcp/better_bibtex_client.py | 404 | except Exception as e: | CODE |
| LOW | src/zotero_mcp/pdf_utils.py | 399 | except Exception: | CODE |
| LOW | src/zotero_mcp/pdf_utils.py | 789 | 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 | 272 | except Exception: | CODE |
| LOW | src/zotero_mcp/client.py | 364 | except Exception: | CODE |
| LOW | src/zotero_mcp/client.py | 512 | except Exception: | CODE |
| LOW | src/zotero_mcp/client.py | 836 | except Exception: | CODE |
| LOW | src/zotero_mcp/client.py | 943 | except Exception as e: | CODE |
| LOW | src/zotero_mcp/client.py | 1064 | except Exception: | CODE |
| LOW | src/zotero_mcp/client.py | 1149 | except Exception: | CODE |
| LOW | src/zotero_mcp/client.py | 1228 | except Exception as exc: | CODE |
| LOW | src/zotero_mcp/client.py | 1247 | except Exception as exc: | CODE |
| LOW | src/zotero_mcp/client.py | 1278 | except Exception as exc: | CODE |
| MEDIUM⚡ | src/zotero_mcp/setup_helper.py | 569 | 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 | 613 | print(f"Error: Config file at {config_path} is not valid JSON. Creating new config.") | CODE |
| MEDIUM | src/zotero_mcp/setup_helper.py | 677 | print(f"Error writing config file: {str(e)}") | CODE |
| MEDIUM | src/zotero_mcp/setup_helper.py | 786 | print("Error: Could not find zotero-mcp executable.") | CODE |
| MEDIUM | src/zotero_mcp/setup_helper.py | 799 | print("Error: Could not determine Claude Desktop config path.") | CODE |
| LOW⚡ | src/zotero_mcp/setup_helper.py | 568 | except Exception as e: | CODE |
| LOW⚡ | src/zotero_mcp/setup_helper.py | 579 | except Exception: | CODE |
| LOW | src/zotero_mcp/setup_helper.py | 93 | except Exception as e: | CODE |
| LOW | src/zotero_mcp/setup_helper.py | 595 | except Exception as e: | CODE |
| LOW | src/zotero_mcp/setup_helper.py | 676 | except Exception as e: | CODE |
| 399 more matches not shown… | ||||
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| HIGH | tests/test_ollama_embedding.py | 72 | except Exception: | CODE |
| HIGH | tests/test_embedding_function_registration.py | 132 | except Exception: | CODE |
| HIGH | scripts/check_openai_ratelimits.py | 178 | except ValueError: | CODE |
| HIGH | scripts/check_openai_ratelimits.py | 185 | except ValueError: | CODE |
| HIGH | src/zotero_mcp/config.py | 153 | except (json.JSONDecodeError, OSError): | CODE |
| HIGH | src/zotero_mcp/library.py | 153 | except Exception: | CODE |
| HIGH | src/zotero_mcp/pdf_utils.py | 789 | except Exception: | CODE |
| HIGH | src/zotero_mcp/html_metadata.py | 164 | except Exception: | CODE |
| HIGH | src/zotero_mcp/client.py | 272 | except Exception: | CODE |
| HIGH | src/zotero_mcp/client.py | 1149 | except Exception: | CODE |
| HIGH | src/zotero_mcp/client.py | 1276 | except WebDAVNotConfiguredError: | CODE |
| HIGH | src/zotero_mcp/setup_helper.py | 38 | except OSError: | CODE |
| HIGH | src/zotero_mcp/setup_helper.py | 880 | except Exception: | CODE |
| HIGH | src/zotero_mcp/cli_standalone.py | 544 | except _cli_json.CliError: | CODE |
| HIGH | src/zotero_mcp/updater.py | 117 | except Exception: | CODE |
| HIGH | src/zotero_mcp/updater.py | 389 | except (OSError, ValueError, AttributeError): | CODE |
| HIGH | src/zotero_mcp/updater.py | 849 | except Exception: | CODE |
| HIGH | src/zotero_mcp/updater.py | 146 | except Exception: | CODE |
| HIGH | src/zotero_mcp/updater.py | 72 | except Exception: | CODE |
| HIGH | src/zotero_mcp/updater.py | 88 | except Exception: | CODE |
| HIGH | src/zotero_mcp/updater.py | 590 | except Exception: | CODE |
| HIGH | src/zotero_mcp/batch_common.py | 111 | except OSError: | CODE |
| HIGH | src/zotero_mcp/_context.py | 31 | except Exception: | CODE |
| HIGH | src/zotero_mcp/cli.py | 204 | except Exception: | CODE |
| HIGH | src/zotero_mcp/cli.py | 405 | except Exception: | CODE |
| HIGH | src/zotero_mcp/cli.py | 357 | except Exception: | CODE |
| HIGH | src/zotero_mcp/cli.py | 719 | except (AttributeError, ValueError, OSError): | CODE |
| HIGH | src/zotero_mcp/utils.py | 70 | except OSError: | CODE |
| HIGH | src/zotero_mcp/utils.py | 208 | except Exception: | CODE |
| HIGH | src/zotero_mcp/chroma_client.py | 138 | except Exception: | CODE |
| HIGH | src/zotero_mcp/pdf_layout.py | 648 | except Exception: | CODE |
| HIGH | src/zotero_mcp/pdf_layout.py | 711 | except Exception: | CODE |
| HIGH | src/zotero_mcp/pdf_layout.py | 726 | except Exception: | CODE |
| HIGH | src/zotero_mcp/pdf_layout.py | 661 | except Exception: | CODE |
| HIGH | src/zotero_mcp/pdf_layout.py | 692 | except Exception: | CODE |
| HIGH | src/zotero_mcp/fulltext_cache.py | 60 | except OSError: | CODE |
| HIGH | src/zotero_mcp/schema.py | 107 | except OSError: | CODE |
| HIGH | src/zotero_mcp/schema.py | 114 | except OSError: | CODE |
| HIGH | src/zotero_mcp/semantic_search.py | 124 | except Exception: | CODE |
| HIGH | src/zotero_mcp/semantic_search.py | 333 | except Exception: | CODE |
| HIGH | src/zotero_mcp/semantic_search.py | 2083 | except Exception: | CODE |
| HIGH | src/zotero_mcp/semantic_search.py | 2122 | except Exception: | CODE |
| HIGH | src/zotero_mcp/semantic_search.py | 1539 | except Exception: | CODE |
| HIGH | src/zotero_mcp/semantic_search.py | 2770 | except Exception: | CODE |
| HIGH | src/zotero_mcp/semantic_search.py | 2933 | except Exception: | CODE |
| HIGH | src/zotero_mcp/semantic_search.py | 3076 | except Exception: | CODE |
| HIGH | src/zotero_mcp/semantic_search.py | 2118 | except Exception: | CODE |
| HIGH | src/zotero_mcp/semantic_search.py | 2779 | except Exception: | CODE |
| HIGH | src/zotero_mcp/semantic_search.py | 2811 | except Exception: | CODE |
| HIGH | src/zotero_mcp/semantic_search.py | 3022 | except Exception: | CODE |
| HIGH | src/zotero_mcp/semantic_search.py | 3062 | except Exception: | CODE |
| HIGH | src/zotero_mcp/semantic_search.py | 3438 | except Exception: | CODE |
| HIGH | src/zotero_mcp/semantic_search.py | 3472 | except queue.Empty: | CODE |
| HIGH | src/zotero_mcp/semantic_search.py | 1638 | except Exception: | CODE |
| HIGH | src/zotero_mcp/semantic_search.py | 1643 | except Exception: | CODE |
| HIGH | src/zotero_mcp/semantic_search.py | 1902 | except Exception: | CODE |
| HIGH | src/zotero_mcp/semantic_search.py | 2293 | except Exception: | CODE |
| HIGH | src/zotero_mcp/semantic_search.py | 1720 | except Exception: | CODE |
| HIGH | src/zotero_mcp/semantic_search.py | 2688 | except Exception: | CODE |
| HIGH | src/zotero_mcp/semantic_search.py | 2520 | except Exception: | CODE |
| 17 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_local_note_search_scope.py | 12 | 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 | 6 | CODE | |
| LOW | tests/test_add_from_file.py | 7 | CODE | |
| LOW | tests/test_add_from_file.py | 7 | CODE | |
| LOW | tests/test_add_from_file.py | 10 | CODE | |
| LOW | tests/test_add_from_file.py | 10 | 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 | 11 | 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 | 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_tool_descriptions_name_registered_tools.py | 13 | CODE | |
| LOW | tests/test_regression_bugs.py | 8 | CODE | |
| LOW | tests/test_semantic_search_filters_example.py | 13 | 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 | scripts/measure_read_backend.py | 39 | CODE | |
| LOW | scripts/profile_recent_items.py | 32 | 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 | |
| 158 more matches not shown… | ||||
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | tests/test_chroma_client_real.py | 369 | 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 | 67 | CODE | |
| LOW | src/zotero_mcp/webdav.py | 222 | CODE | |
| LOW | src/zotero_mcp/note_html.py | 134 | CODE | |
| LOW | src/zotero_mcp/library.py | 318 | CODE | |
| LOW | src/zotero_mcp/library.py | 363 | CODE | |
| LOW | src/zotero_mcp/library.py | 473 | CODE | |
| LOW | src/zotero_mcp/better_bibtex_client.py | 129 | CODE | |
| LOW | src/zotero_mcp/better_bibtex_client.py | 252 | CODE | |
| LOW | src/zotero_mcp/config_light.py | 57 | CODE | |
| LOW | src/zotero_mcp/client.py | 898 | 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 | 214 | CODE | |
| LOW | src/zotero_mcp/setup_helper.py | 532 | CODE | |
| LOW | src/zotero_mcp/setup_helper.py | 600 | CODE | |
| LOW | src/zotero_mcp/setup_helper.py | 727 | CODE | |
| LOW | src/zotero_mcp/pdfannots_downloader.py | 136 | CODE | |
| LOW | src/zotero_mcp/cli_standalone.py | 243 | CODE | |
| LOW | src/zotero_mcp/cli_standalone.py | 300 | CODE | |
| LOW | src/zotero_mcp/cli_standalone.py | 426 | CODE | |
| LOW | src/zotero_mcp/cli_standalone.py | 524 | CODE | |
| LOW | src/zotero_mcp/cli_standalone.py | 634 | CODE | |
| LOW | src/zotero_mcp/cli_standalone.py | 701 | CODE | |
| LOW | src/zotero_mcp/cli_standalone.py | 756 | CODE | |
| LOW | src/zotero_mcp/cli_standalone.py | 869 | CODE | |
| LOW | src/zotero_mcp/updater.py | 46 | CODE | |
| LOW | src/zotero_mcp/updater.py | 123 | CODE | |
| LOW | src/zotero_mcp/updater.py | 495 | CODE | |
| LOW | src/zotero_mcp/updater.py | 681 | CODE | |
| LOW | src/zotero_mcp/local_db.py | 755 | CODE | |
| LOW | src/zotero_mcp/local_db.py | 813 | CODE | |
| LOW | src/zotero_mcp/local_db.py | 2359 | CODE | |
| LOW | src/zotero_mcp/local_db.py | 2579 | CODE | |
| LOW | src/zotero_mcp/epub_utils.py | 471 | CODE | |
| LOW | src/zotero_mcp/epub_utils.py | 702 | CODE | |
| LOW | src/zotero_mcp/cli.py | 67 | CODE | |
| LOW | src/zotero_mcp/cli.py | 723 | CODE | |
| LOW | src/zotero_mcp/utils.py | 176 | CODE | |
| LOW | src/zotero_mcp/chroma_client.py | 51 | CODE | |
| LOW | src/zotero_mcp/chroma_client.py | 276 | CODE | |
| LOW | src/zotero_mcp/citation_import.py | 198 | CODE | |
| LOW | src/zotero_mcp/citation_import.py | 225 | CODE | |
| LOW | src/zotero_mcp/citation_import.py | 395 | CODE | |
| LOW | src/zotero_mcp/citation_import.py | 600 | CODE | |
| LOW | src/zotero_mcp/pdf_layout.py | 164 | CODE | |
| LOW | src/zotero_mcp/pdf_layout.py | 248 | CODE | |
| LOW | src/zotero_mcp/pdf_layout.py | 356 | CODE | |
| LOW | src/zotero_mcp/pdf_layout.py | 452 | CODE | |
| LOW | src/zotero_mcp/fulltext_cache.py | 217 | CODE | |
| LOW | src/zotero_mcp/gemini_batch.py | 277 | CODE | |
| LOW | src/zotero_mcp/extract.py | 169 | CODE | |
| LOW | src/zotero_mcp/extract.py | 443 | CODE | |
| LOW | src/zotero_mcp/extract.py | 490 | CODE | |
| LOW | src/zotero_mcp/semantic_search.py | 489 | CODE | |
| LOW | src/zotero_mcp/semantic_search.py | 873 | CODE | |
| LOW | src/zotero_mcp/semantic_search.py | 1210 | CODE | |
| 74 more matches not shown… | ||||
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM | CHANGELOG.md | 21 | - **Tables without closing tags no longer flood HTML extraction with empty cells** (#687). `html.parser` nests an unterm | CODE |
| MEDIUM | CHANGELOG.md | 446 | - **`update-db` no longer accepts `chunking.enabled` in silence on the OpenAI Batch API path (#416).** The batch path bu | CODE |
| MEDIUM⚡ | tests/test_chroma_persist_directory.py | 31 | "1", # default embedding model | CODE |
| MEDIUM | tests/test_fulltext_web_mode.py | 344 | # --------- 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 | tests/test_extract.py | 262 | # Five columns is past the scaffolding run length: a header with text | COMMENT |
| MEDIUM | src/zotero_mcp/setup_helper.py | 247 | # Choose embedding model | COMMENT |
| MEDIUM | src/zotero_mcp/chroma_client.py | 566 | # Pick the embedding model. config.json is the richer, authoritative source | COMMENT |
| MEDIUM | src/zotero_mcp/extract.py | 436 | #: A run of four or more empty (or ``---``) table cells is layout scaffolding. | COMMENT |
| MEDIUM | src/zotero_mcp/semantic_search.py | 3204 | # Truncate to fit the configured embedding model's token limit | COMMENT |
| MEDIUM | src/zotero_mcp/tools/write.py | 1451 | # the embedding model in semantic_search, which reads "<i>" as tokens | COMMENT |
| MEDIUM | src/zotero_mcp/tools/search.py | 1670 | # guard, but neither loads an embedding model or a Zotero client. | COMMENT |
| MEDIUM | src/zotero_mcp/tools/search.py | 1684 | # Read status without loading any embedding model (fast, no network). | 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/library.py | 277 | "filter, or a boolean itemType expression like 'book || journalArticle'" | CODE |
| HIGH | src/zotero_mcp/tools/search.py | 151 | or `a || b`. Returns None when a filter cannot be honoured here (a `*` or | STRING |
| HIGH | src/zotero_mcp/tools/_helpers.py | 867 | # A bare JSON scalar (int/float/bool/null) — e.g. an all-digit | 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 |
|---|---|---|---|---|
| LOW | pyproject.toml | 21 | dependencies = [ | COMMENT |
| LOW | pyproject.toml | 41 | # HTML snapshots only. | COMMENT |
| LOW | tests/test_citation_import.py | 441 | COMMENT | |
| LOW | tests/test_identifier_lock.py | 201 | t = threading.Thread(target=holder) | COMMENT |
| LOW | tests/live/conftest.py | 121 | # provider / fault-injection / Chroma-roundtrip live tests). The half above is | COMMENT |
| LOW | plugin/src/ui/markdown.ts | 1 | // Markdown to a small node tree. PURE: no DOM, so it runs under `node --test` and the XSS corpus can | COMMENT |
| LOW | plugin/src/ui/diagram-svg.ts | 1 | // Diagrams, the pure half: the agent's ```svg text to a small, safe element tree. No DOM, so it runs under | COMMENT |
| LOW | plugin/src/agent/backends.ts | 1 | // Which coding agent answers: a small table of ACP bridges, one entry per vendor. An entry is a | COMMENT |
| LOW | plugin/src/agent/session.ts | 1 | // One conversation with an agent: the ACP session on top of an AcpClient, translated into ChatEvents. | COMMENT |
| LOW | plugin/src/agent/session.ts | 261 | #catalog: Offers; | COMMENT |
| LOW | src/zotero_mcp/library.py | 321 | for start in range(0, len(keys), self._ITEMKEY_BATCH): | COMMENT |
| LOW | src/zotero_mcp/identifiers.py | 241 | m = _ARXIV_EXTRA_RE.search(extra or "") | COMMENT |
| LOW | src/zotero_mcp/local_db.py | 41 | # the ``pdf_max_pages`` extraction config or ``ZOTERO_PDF_MAXPAGES``. | COMMENT |
| LOW | src/zotero_mcp/local_db.py | 261 | # --------------------------------------------------------------------------- | COMMENT |
| LOW | src/zotero_mcp/local_db.py | 281 | # separately since they're multi-valued). | COMMENT |
| LOW | src/zotero_mcp/local_db.py | 401 | "itemType": "it.typeName", | COMMENT |
| LOW | src/zotero_mcp/local_db.py | 441 | value_expr="SUBSTR(v.value, 1, 4)", | COMMENT |
| LOW | src/zotero_mcp/local_db.py | 881 | ) | COMMENT |
| LOW | src/zotero_mcp/local_db.py | 1861 | # Restrict the corpus to the configured collections, including | COMMENT |
| LOW | src/zotero_mcp/local_db.py | 2661 | variants = _generate_search_variants(query) | COMMENT |
| LOW | src/zotero_mcp/local_db.py | 3181 | # | COMMENT |
| LOW | src/zotero_mcp/chroma_client.py | 561 | file_config = json.load(f) | COMMENT |
| LOW | src/zotero_mcp/semantic_search.py | 361 | # one-time migration of existing documents. Version 2 (#163) added the | COMMENT |
| LOW | src/zotero_mcp/semantic_search.py | 1561 | ): | COMMENT |
| LOW | src/zotero_mcp/tools/write.py | 1441 | item_data[field] = value | COMMENT |
| LOW | src/zotero_mcp/tools/write.py | 1821 | """ | COMMENT |
| LOW | src/zotero_mcp/tools/write.py | 1921 | policy="thin_only" if is_batch else page_check, | COMMENT |
| LOW | src/zotero_mcp/tools/write.py | 2221 | if_exists: Literal["duplicate", "file", "skip"] = "duplicate", | COMMENT |
| LOW | src/zotero_mcp/tools/write.py | 3221 | item["data"] = data | COMMENT |
| LOW | src/zotero_mcp/tools/write.py | 4281 | # --------------------------------------------------------------------------- | COMMENT |
| LOW | src/zotero_mcp/tools/write.py | 4301 | # Child script. It imports ONLY PyMuPDF — never zotero_mcp — so the subprocess | COMMENT |
| LOW | src/zotero_mcp/tools/search.py | 1141 | return in_subtree if operation == "is" else not in_subtree | COMMENT |
| LOW | src/zotero_mcp/tools/search.py | 1261 | if sort_field in {"date", "year", "publicationDate"}: | COMMENT |
| LOW | src/zotero_mcp/tools/_helpers.py | 61 | return _client.load_zotero_mcp_config() | COMMENT |
| LOW | src/zotero_mcp/tools/_helpers.py | 241 | return read_zot, write_zot | COMMENT |
| LOW | src/zotero_mcp/tools/_helpers.py | 1361 | # Measured at limit=50 a real book ('Stochastic Processes', 83 | COMMENT |
| LOW | src/zotero_mcp/tools/_helpers.py | 1461 | # The Zotero API lock exists to protect the single-threaded local API on port | COMMENT |
| LOW | src/zotero_mcp/tools/_helpers.py | 2321 | 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 | # v2 models this value means "effective budget for the TEXT BODY" — | COMMENT |
| LOW | src/zotero_mcp/embeddings/providers/openai.py | 21 | collides with the built-in, whose build_from_config rejects our | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW⚡ | src/zotero_mcp/config.py | 21 | logger = logging.getLogger(__name__) | CODE |
| LOW | src/zotero_mcp/library.py | 44 | logger = logging.getLogger(__name__) | CODE |
| LOW | src/zotero_mcp/better_bibtex_client.py | 14 | logger = logging.getLogger(__name__) | CODE |
| LOW | src/zotero_mcp/config_light.py | 29 | logger = logging.getLogger(__name__) | CODE |
| LOW | src/zotero_mcp/identifiers.py | 27 | __all__ = [ | CODE |
| LOW | src/zotero_mcp/client.py | 45 | logger = logging.getLogger(__name__) | CODE |
| LOW | src/zotero_mcp/client.py | 140 | 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/pdfannots_downloader.py | 14 | logger = logging.getLogger(__name__) | CODE |
| LOW | src/zotero_mcp/updater.py | 24 | logger = logging.getLogger(__name__) | CODE |
| LOW | src/zotero_mcp/local_db.py | 38 | logger = logging.getLogger(__name__) | CODE |
| LOW | src/zotero_mcp/chroma_client.py | 45 | logger = logging.getLogger(__name__) | CODE |
| LOW | src/zotero_mcp/chroma_client.py | 511 | def update_metadatas(self, ids: list[str], metadatas: list[dict[str, Any]]) -> None: | CODE |
| LOW | src/zotero_mcp/pdfannots_helper.py | 14 | logger = logging.getLogger(__name__) | CODE |
| LOW | src/zotero_mcp/fulltext_cache.py | 49 | logger = logging.getLogger(__name__) | CODE |
| LOW | src/zotero_mcp/gemini_batch.py | 46 | logger = logging.getLogger(__name__) | CODE |
| LOW | src/zotero_mcp/extract.py | 32 | 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 | 61 | 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/ratelimit.py | 41 | logger = logging.getLogger(__name__) | CODE |
| LOW | src/zotero_mcp/embeddings/base.py | 58 | 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 |
|---|---|---|---|---|
| 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 | 2501 | # changes. The robust fix is the same one noted in add_by_doi's | COMMENT |
| MEDIUM⚡ | src/zotero_mcp/tools/read_pdf.py | 384 | # A line-break hyphenation ("robust-\nness") or a soft hyphen: "ness" is mid-word. | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM | tests/test_regression_bugs.py | 192 | # Create a dummy file | COMMENT |
| MEDIUM | plugin/src/ui/create-skill.ts | 11 | # Create a skill | COMMENT |
| MEDIUM | src/zotero_mcp/client.py | 349 | # Create a local client - library_id 0 is the default for local. | COMMENT |
| MEDIUM | src/zotero_mcp/pdfannots_downloader.py | 158 | # Create a temporary directory for the download | COMMENT |
| MEDIUM | src/zotero_mcp/_app.py | 125 | # Create an MCP server (fastmcp 2.14+ no longer accepts `dependencies`) | COMMENT |
| MEDIUM | src/zotero_mcp/tools/write.py | 4707 | # Create the metadata item. With if_exists='file' and a known DOI, | COMMENT |
| MEDIUM | src/zotero_mcp/tools/write.py | 4724 | # Create a basic item | COMMENT |
| MEDIUM | src/zotero_mcp/tools/retrieval.py | 450 | # Create a mapping of collection IDs to their data | COMMENT |
| MEDIUM | src/zotero_mcp/tools/retrieval.py | 453 | # Create a mapping of parent to child collections | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| HIGH | docs/getting-started.md | 56 | export ZOTERO_API_KEY=your_api_key | CODE |
| HIGH | docs/getting-started.md | 86 | "ZOTERO_API_KEY": "YOUR_API_KEY", | CODE |
| HIGH | docs/configuration.md | 17 | zotero-mcp setup --no-local --api-key YOUR_API_KEY --library-id YOUR_LIBRARY_ID | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | src/zotero_mcp/pdfannots_downloader.py | 72 | # Check if we have a URL for this platform/architecture | COMMENT |
| LOW⚡ | src/zotero_mcp/updater.py | 63 | # Check if we're in a uv-managed project | COMMENT |
| LOW⚡ | src/zotero_mcp/updater.py | 78 | # Check if we're in a uv virtual environment | COMMENT |
| LOW | src/zotero_mcp/updater.py | 102 | # Check if pipx is available | COMMENT |
| LOW | src/zotero_mcp/updater.py | 721 | # Check if update is needed. Only a strictly newer release counts — a | COMMENT |
| LOW | src/zotero_mcp/cli.py | 1026 | # Check if installed via uv | COMMENT |
| LOW | src/zotero_mcp/chroma_client.py | 114 | # Check if the stored config matches what we want; if not, recreate. | COMMENT |
| LOW | src/zotero_mcp/tools/write.py | 5120 | # Check if relation already exists (match both URI prefix variants) | COMMENT |
| LOW | src/zotero_mcp/tools/annotations.py | 315 | # Check if Zotero with Better BibTeX is running | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW⚡ | docs/integration-test-plan.md | 551 | ### Step 1: Verify all per-phase cleanups were done | COMMENT |
| LOW⚡ | docs/integration-test-plan.md | 559 | ### Step 2: Identify test-created items | COMMENT |
| LOW⚡ | docs/integration-test-plan.md | 565 | ### Step 3: Tell the user to delete manually | COMMENT |
| LOW | src/zotero_mcp/better_bibtex_client.py | 271 | # Step 1: Get citation key from item key. BBT's JSON-RPC expects | COMMENT |
| LOW | src/zotero_mcp/better_bibtex_client.py | 289 | # Step 2: Export BibTeX using the citation key. | COMMENT |
| LOW⚡ | src/zotero_mcp/tools/annotations.py | 848 | # Step 3: Map immediate parent keys to stable paper/attachment context. | 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/semantic_search.py | 2525 | CODE | |
| LOW | src/zotero_mcp/tools/write.py | 839 | CODE | |
| LOW | src/zotero_mcp/tools/write.py | 4087 | CODE | |
| LOW | src/zotero_mcp/embeddings/ratelimit.py | 69 | CODE | |
| LOW | src/zotero_mcp/embeddings/providers/gemini.py | 71 | CODE | |
| LOW | src/zotero_mcp/embeddings/providers/openai.py | 58 | CODE | |
| LOW | src/zotero_mcp/embeddings/providers/ollama.py | 43 | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW⚡ | tests/test_place_field_reads.py | 129 | fake_data = { | CODE |
| LOW | tests/test_place_field_reads.py | 144 | return {"key": key, "data": fake_data} | CODE |
| LOW | tests/test_add_by_isbn.py | 101 | "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 |
|---|---|---|---|---|
| LOW | src/zotero_mcp/better_bibtex_client.py | 271 | # Step 1: Get citation key from item key. BBT's JSON-RPC expects | COMMENT |
| LOW | src/zotero_mcp/better_bibtex_client.py | 289 | # Step 2: Export BibTeX using the citation key. | COMMENT |
| LOW⚡ | src/zotero_mcp/tools/annotations.py | 848 | # Step 3: Map immediate parent keys to stable paper/attachment context. | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| HIGH | src/zotero_mcp/better_bibtex_client.py | 253 | Export BibTeX for a specific item using its item key. Args: item_key: Zotero item key to e | STRING |