docslicer 0.2.0__tar.gz → 0.2.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (158) hide show
  1. {docslicer-0.2.0/src/docslicer.egg-info → docslicer-0.2.2}/PKG-INFO +263 -10
  2. {docslicer-0.2.0 → docslicer-0.2.2}/README.md +259 -9
  3. {docslicer-0.2.0 → docslicer-0.2.2}/pyproject.toml +8 -1
  4. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/__init__.py +1 -1
  5. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/_config.py +2 -1
  6. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/_orchestrator.py +6 -1
  7. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/_result.py +83 -14
  8. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/cli.py +7 -2
  9. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/html/html_orchestrator.py +46 -17
  10. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/html/step_01_box_extractor.py +25 -6
  11. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/html/step_01_static_box_extractor.py +219 -110
  12. docslicer-0.2.2/src/docslicer/mcp/__init__.py +16 -0
  13. docslicer-0.2.2/src/docslicer/mcp/_store.py +343 -0
  14. docslicer-0.2.2/src/docslicer/mcp/server.py +1186 -0
  15. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/metadata/schema.py +4 -0
  16. docslicer-0.2.2/src/docslicer/ocr/__init__.py +9 -0
  17. docslicer-0.2.2/src/docslicer/ocr/_availability.py +61 -0
  18. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/pdf/pdf_orchestrator.py +34 -0
  19. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/shared/shared_orchestrator.py +7 -1
  20. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/shared/step_05_heading_detector.py +21 -10
  21. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/shared/step_07_block_merger.py +36 -5
  22. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/shared/step_08_chunk_builder.py +199 -42
  23. {docslicer-0.2.0 → docslicer-0.2.2/src/docslicer.egg-info}/PKG-INFO +263 -10
  24. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer.egg-info/SOURCES.txt +6 -1
  25. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer.egg-info/entry_points.txt +1 -0
  26. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer.egg-info/requires.txt +4 -0
  27. docslicer-0.2.2/tests/test_table_cell_newlines.py +92 -0
  28. docslicer-0.2.0/src/docslicer/ocr/__init__.py +0 -3
  29. {docslicer-0.2.0 → docslicer-0.2.2}/LICENSE +0 -0
  30. {docslicer-0.2.0 → docslicer-0.2.2}/LICENSE-COMMERCIAL.md +0 -0
  31. {docslicer-0.2.0 → docslicer-0.2.2}/setup.cfg +0 -0
  32. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/_utils/__init__.py +0 -0
  33. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/_utils/color_utils.py +0 -0
  34. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/_utils/cpu.py +0 -0
  35. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/_utils/df_aggregation/__init__.py +0 -0
  36. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/_utils/df_aggregation/registry_aggregator.py +0 -0
  37. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/_utils/df_aggregation/text_merge.py +0 -0
  38. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/_utils/df_export/__init__.py +0 -0
  39. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/_utils/df_export/export_debug.py +0 -0
  40. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/_utils/df_export/reorder_columns.py +0 -0
  41. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/_utils/io/__init__.py +0 -0
  42. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/_utils/io/yaml_compilers/__init__.py +0 -0
  43. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/_utils/io/yaml_compilers/exhibit_patterns.py +0 -0
  44. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/_utils/io/yaml_compilers/hierarchy_type_patterns.py +0 -0
  45. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/_utils/io/yaml_compilers/page_label_patterns.py +0 -0
  46. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/_utils/io/yaml_loader.py +0 -0
  47. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/_utils/layout/__init__.py +0 -0
  48. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/_utils/layout/gutter_detector.py +0 -0
  49. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/_utils/layout/layouts.py +0 -0
  50. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/_utils/layout/line_merger.py +0 -0
  51. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/_utils/layout/line_number_detector.py +0 -0
  52. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/_utils/layout/reading_order.py +0 -0
  53. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/_utils/layout/shape_processor.py +0 -0
  54. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/_utils/oxm_package.py +0 -0
  55. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/_utils/parallel.py +0 -0
  56. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/_utils/password.py +0 -0
  57. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/_utils/safe_call.py +0 -0
  58. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/_utils/table/__init__.py +0 -0
  59. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/_utils/table/table_header.py +0 -0
  60. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/_utils/table/table_normalize.py +0 -0
  61. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/_utils/table/table_schema.py +0 -0
  62. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/_utils/text_utils.py +0 -0
  63. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/_utils/timing.py +0 -0
  64. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/config/common_author_names.csv +0 -0
  65. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/config/exhibit_patterns.yaml +0 -0
  66. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/config/hierarchy_type_patterns.yaml +0 -0
  67. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/config/page_label_patterns.yaml +0 -0
  68. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/docx/__init__.py +0 -0
  69. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/docx/docx_orchestrator.py +0 -0
  70. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/docx/native_metadata.py +0 -0
  71. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/docx/step_01_package_reader.py +0 -0
  72. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/docx/step_02_run_extractor.py +0 -0
  73. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/docx/step_03_chart_point_builder.py +0 -0
  74. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/docx/step_04_table_cell_builder.py +0 -0
  75. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/docx/step_05_paragraph_builder.py +0 -0
  76. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/docx/step_06_line_builder.py +0 -0
  77. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/docx/step_07_style_prefiller.py +0 -0
  78. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/html/__init__.py +0 -0
  79. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/html/extract_boxes.js +0 -0
  80. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/html/native_metadata.py +0 -0
  81. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/html/step_02_box_cleaner.py +0 -0
  82. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/html/step_03_page_label_detector.py +0 -0
  83. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/html/step_04_line_builder.py +0 -0
  84. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/html/step_05_table_extractor.py +0 -0
  85. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/html/step_06_style_prefiller.py +0 -0
  86. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/metadata/__init__.py +0 -0
  87. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/metadata/consolidate.py +0 -0
  88. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/metadata/generator.py +0 -0
  89. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/metadata/ocr_detector.py +0 -0
  90. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/metadata/page_analysis.py +0 -0
  91. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/metadata/text_fallback.py +0 -0
  92. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/ocr/ocr_orchestrator.py +0 -0
  93. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/ocr/step_01_word_extractor.py +0 -0
  94. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/ocr/step_02_word_colorizer.py +0 -0
  95. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/ocr/step_03_shape_extractor.py +0 -0
  96. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/ocr/step_04_text_cleaner.py +0 -0
  97. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/ocr/step_05_font_size_estimator.py +0 -0
  98. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/pdf/__init__.py +0 -0
  99. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/pdf/_utils/__init__.py +0 -0
  100. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/pdf/_utils/coordinates.py +0 -0
  101. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/pdf/_utils/form_fields.py +0 -0
  102. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/pdf/_utils/form_label_link.py +0 -0
  103. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/pdf/_utils/line_classification.py +0 -0
  104. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/pdf/_utils/page_rotation.py +0 -0
  105. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/pdf/_utils/script_thresholds.py +0 -0
  106. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/pdf/_utils/struct_context.py +0 -0
  107. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/pdf/_utils/struct_tree.py +0 -0
  108. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/pdf/native_metadata.py +0 -0
  109. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/pdf/step_01_word_extractor.py +0 -0
  110. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/pdf/step_02_image_extractor.py +0 -0
  111. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/pdf/step_03_shape_extractor.py +0 -0
  112. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/pdf/step_04_link_extractor.py +0 -0
  113. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/pdf/step_05_struct_group.py +0 -0
  114. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/pdf/step_06_style_prefiller.py +0 -0
  115. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/pdf/step_07_stream_group.py +0 -0
  116. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/pdf/step_08_reading_order.py +0 -0
  117. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/pdf/step_09_word_relationships.py +0 -0
  118. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/pdf/step_10_cell_builder.py +0 -0
  119. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/pdf/step_11_page_label_detector.py +0 -0
  120. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/pdf/step_12_cell_grouper.py +0 -0
  121. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/pdf/step_13_line_builder.py +0 -0
  122. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/pdf/step_14_table_builder.py +0 -0
  123. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/pptx/__init__.py +0 -0
  124. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/pptx/native_metadata.py +0 -0
  125. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/pptx/pptx_orchestrator.py +0 -0
  126. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/pptx/step_01_package_reader.py +0 -0
  127. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/pptx/step_02_run_extractor.py +0 -0
  128. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/pptx/step_03_chart_point_builder.py +0 -0
  129. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/pptx/step_04_table_cell_builder.py +0 -0
  130. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/pptx/step_05_paragraph_builder.py +0 -0
  131. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/pptx/step_06_reading_order.py +0 -0
  132. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/pptx/step_07_line_builder.py +0 -0
  133. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/pptx/step_08_style_prefiller.py +0 -0
  134. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/scraping/__init__.py +0 -0
  135. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/scraping/config.py +0 -0
  136. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/scraping/cookie_consent.js +0 -0
  137. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/scraping/dispatcher.py +0 -0
  138. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/scraping/fetchers/__init__.py +0 -0
  139. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/scraping/fetchers/http_fetcher.py +0 -0
  140. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/scraping/fetchers/sec_fetcher.py +0 -0
  141. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/scraping/models.py +0 -0
  142. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/scraping/stealth_init.js +0 -0
  143. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/shared/__init__.py +0 -0
  144. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/shared/step_01_navigation_detector.py +0 -0
  145. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/shared/step_02_toc_detector.py +0 -0
  146. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/shared/step_03_exhibit_detector.py +0 -0
  147. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/shared/step_04_section_classifier.py +0 -0
  148. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer/shared/step_06_hierarchy_builder.py +0 -0
  149. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer.egg-info/dependency_links.txt +0 -0
  150. {docslicer-0.2.0 → docslicer-0.2.2}/src/docslicer.egg-info/top_level.txt +0 -0
  151. {docslicer-0.2.0 → docslicer-0.2.2}/tests/test_api.py +0 -0
  152. {docslicer-0.2.0 → docslicer-0.2.2}/tests/test_charts.py +0 -0
  153. {docslicer-0.2.0 → docslicer-0.2.2}/tests/test_document_parser.py +0 -0
  154. {docslicer-0.2.0 → docslicer-0.2.2}/tests/test_errors.py +0 -0
  155. {docslicer-0.2.0 → docslicer-0.2.2}/tests/test_exports.py +0 -0
  156. {docslicer-0.2.0 → docslicer-0.2.2}/tests/test_loose_box_reconstruction.py +0 -0
  157. {docslicer-0.2.0 → docslicer-0.2.2}/tests/test_packaging.py +0 -0
  158. {docslicer-0.2.0 → docslicer-0.2.2}/tests/test_smoke.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: docslicer
3
- Version: 0.2.0
3
+ Version: 0.2.2
4
4
  Summary: Deterministic hierarchical document parser and chunker
5
5
  Author-email: "Market Framer Inc." <jelle@docslicer.ai>
6
6
  License-Expression: AGPL-3.0-only
@@ -50,6 +50,9 @@ Provides-Extra: crypto
50
50
  Requires-Dist: msoffcrypto-tool>=5.0; extra == "crypto"
51
51
  Provides-Extra: parquet
52
52
  Requires-Dist: pyarrow>=18.0; extra == "parquet"
53
+ Provides-Extra: mcp
54
+ Requires-Dist: mcp>=2.0; extra == "mcp"
55
+ Requires-Dist: tiktoken>=0.10; extra == "mcp"
53
56
  Provides-Extra: dev
54
57
  Requires-Dist: pytest>=8.0; extra == "dev"
55
58
  Requires-Dist: pytest-asyncio>=0.24; extra == "dev"
@@ -57,11 +60,22 @@ Dynamic: license-file
57
60
 
58
61
  # DocSlicer
59
62
 
60
- [![License: AGPL v3](https://img.shields.io/badge/License-AGPL_v3-blue.svg)](LICENSE) [![Commercial license available](https://img.shields.io/badge/License-Commercial-green.svg)](LICENSE-COMMERCIAL.md)
63
+ [![PyPI](https://img.shields.io/pypi/v/docslicer.svg)](https://pypi.org/project/docslicer/) [![Python versions](https://img.shields.io/pypi/pyversions/docslicer.svg)](https://pypi.org/project/docslicer/) [![License: AGPL v3](https://img.shields.io/badge/License-AGPL_v3-blue.svg)](LICENSE) [![Commercial license available](https://img.shields.io/badge/License-Commercial-green.svg)](LICENSE-COMMERCIAL.md)
61
64
 
62
- Lightning-fast, deterministic hierarchical document parser and chunker for business documents. No LLM calls or heavy ML models.
65
+ [![Install in VS Code](https://img.shields.io/badge/VS_Code-Install_MCP_server-0098FF?logo=visualstudiocode&logoColor=white)](https://insiders.vscode.dev/redirect/mcp/install?name=docslicer&config=%7B%22name%22%3A%22docslicer%22%2C%22command%22%3A%22uvx%22%2C%22args%22%3A%5B%22--from%22%2C%22docslicer%5Bmcp%5D%22%2C%22docslicer-mcp%22%5D%7D) [![Add to Cursor](https://img.shields.io/badge/Cursor-Add_MCP_server-000000?logo=cursor&logoColor=white)](https://cursor.com/install-mcp?name=docslicer&config=eyJjb21tYW5kIjoidXZ4IiwiYXJncyI6WyItLWZyb20iLCJkb2NzbGljZXJbbWNwXSIsImRvY3NsaWNlci1tY3AiXX0%3D) [![Download .mcpb for Claude Desktop](https://img.shields.io/badge/Claude_Desktop-Download_.mcpb-D97757?logo=claude&logoColor=white)](https://github.com/DocSlicer/DocSlicer/releases/latest)
63
66
 
64
- DocSlicer turns PDFs, Word documents, HTML pages, and PowerPoint files into clean chunks, structured blocks, tables, charts, and a navigable heading hierarchy preserving the document's own structure instead of guessing at it.
67
+ Lightning-fast (31 pages/sec), deterministic document parser and chunker for business documents. No LLM calls or heavy ML models.
68
+
69
+ DocSlicer turns PDFs, Word documents, HTML pages, and PowerPoint files into clean chunks, structured blocks, tables, charts, markdown and a navigable heading hierarchy.
70
+
71
+ Top score on [BizDocBench](https://github.com/DocSlicer/BizDocBench) (0.88 overall vs 0.70 for the next-best tool). 0.80 table accuracy, 0.98 content faithfulness, 0.85 heading recognition and hierarchy preservation, and 0.76 RAG retrieval performance.
72
+
73
+ **Two ways to use it:**
74
+
75
+ - **As a Python library — classic RAG.** The layout-aware chunker gives you clean, non-overlapping chunks, each carrying its full heading breadcrumb, ready to embed. [Jump to the API ↓](#parsing)
76
+ - **As an MCP server — vectorless RAG.** For when you want an answer out of a document right now. Claude, Cursor, or VS Code pulls the outline, picks the section it needs, and reads only that — no embedding, and no 200-page document in the context window. [Jump to setup ↓](#mcp-server)
77
+
78
+ ### Quick start
65
79
 
66
80
  ```python
67
81
  import docslicer
@@ -117,6 +131,23 @@ if __name__ == "__main__":
117
131
 
118
132
  ---
119
133
 
134
+ ## Benchmarks
135
+
136
+ Measured with [BizDocBench](https://github.com/DocSlicer/BizDocBench) — an open benchmark for multi-format business document parsing. All scores are 0–1 (higher is better); `pages_per_sec_aggregate` is throughput across the full corpus.
137
+
138
+ | Tool | Score | Coverage | Speed | Hierarchy | Faithfulness | Tables | Retrieval | Pages/sec |
139
+ |---|---|---|---|---|---|---|---|---|
140
+ | **docslicer** | **0.8796** | 1.0000 | 0.8836 | 0.8466 | 0.9824 | 0.8047 | 0.7601 | 31.27 |
141
+ | docling | 0.7036 | 1.0000 | 0.3805 | 0.4905 | 0.8927 | 0.7467 | 0.7111 | 3.46 |
142
+ | markitdown | 0.5838 | 1.0000 | 0.8513 | 0.0604 | 0.7972 | 0.2584 | 0.5357 | 27.42 |
143
+ | unstructured | 0.5798 | 0.9091 | 0.1073 | 0.4327 | 0.9057 | 0.4812 | 0.6430 | 0.52 |
144
+ | opendataloader | 0.5359 | 0.5844 | 1.0000 | 0.3853 | 0.6484 | 0.2655 | 0.3317 | 117.26 |
145
+ | pymupdf4llm | 0.4519 | 0.5974 | 0.6492 | 0.1089 | 0.6456 | 0.3551 | 0.3552 | 11.84 |
146
+ | mineru | 0.4107 | 0.5974 | 0.1353 | 0.4220 | 0.6176 | 0.3012 | 0.3910 | 0.70 |
147
+ | marker | 0.3735 | 0.5974 | 0.1598 | 0.1926 | 0.6121 | 0.3012 | 0.3778 | 0.87 |
148
+
149
+ ---
150
+
120
151
  ## Install
121
152
 
122
153
  ```bash
@@ -135,6 +166,7 @@ pip install 'docslicer[ocr]' # scanned PDF support via Tesseract + OpenCV
135
166
  # Linux: apt install tesseract-ocr
136
167
  # macOS: brew install tesseract
137
168
 
169
+ pip install 'docslicer[mcp]' # MCP server for LLM clients (Claude, Cursor, …)
138
170
  pip install 'docslicer[llm]' # exact token counts via tiktoken (exact_tokens=True)
139
171
  pip install 'docslicer[crypto]' # password-protected Office files (msoffcrypto-tool)
140
172
  pip install 'docslicer[parquet]' # Parquet export support
@@ -437,22 +469,51 @@ result.tables_by_page(14)
437
469
  result.charts_by_page(14)
438
470
  ```
439
471
 
472
+ ### Parse once, navigate many times
473
+
474
+ A parsed result is plain data, so you can persist it and reload it later. When an
475
+ agent asks many questions about the same document, there's no need to parse it
476
+ again on every question:
477
+
478
+ ```python
479
+ from pathlib import Path
480
+ import docslicer
481
+
482
+ cache = Path("annual_report.json")
483
+
484
+ if cache.exists():
485
+ result = docslicer.ParseResult.load(cache)
486
+ else:
487
+ result = docslicer.parse_document("annual_report.pdf")
488
+ result.save(cache)
489
+ ```
490
+
491
+ A reloaded result supports the full API — `hierarchy`, `find_heading`,
492
+ `chunks_under`, `tables` — so a long-running agent session or document server can
493
+ keep documents open across requests without re-parsing.
494
+
440
495
  ---
441
496
 
442
497
  ## Export
443
498
 
499
+ `save()` decides what to write from the path you give it.
500
+
444
501
  ```python
445
- # Save everything
446
- result.save("output/")
447
- # output/chunks.parquet, blocks.parquet, tables.parquet, metadata.json
448
- # (+ charts.parquet when the document has charts)
502
+ # Save the whole result and reload it later — keeps the heading hierarchy
503
+ result.save("result.json") # same output as result.to_json()
504
+ result = docslicer.ParseResult.load("result.json")
449
505
 
450
- # Specific formats
506
+ # A single collection, in the format you name
451
507
  result.save("chunks.csv")
452
508
  result.save("charts.jsonl") # stems: chunks | blocks | tables | charts | metadata
453
- result.save("result.json") # full parse result as JSON
454
509
  result.export_chunks_jsonl("chunks.jsonl")
455
510
 
511
+ # One file per collection
512
+ result.save("output/")
513
+ # → output/chunks.parquet, blocks.parquet, tables.parquet, metadata.json
514
+ # (+ charts.parquet when the document has charts)
515
+ # Falls back to .csv unless the [parquet] extra is installed.
516
+
456
517
  # Render as Markdown or plain text
457
518
  md = result.export_to_markdown(include_tables=True)
458
519
  txt = result.export_to_text()
@@ -461,6 +522,9 @@ txt = result.export_to_text()
461
522
  df = result.chunks_df()
462
523
  ```
463
524
 
525
+ Only `result.json` round-trips — the collection and directory forms write flat rows
526
+ without the heading hierarchy, so `ParseResult.load()` can't read them back.
527
+
464
528
  ### Debug mode
465
529
 
466
530
  ```python
@@ -492,6 +556,142 @@ pip install 'docslicer[ocr]'
492
556
 
493
557
  ---
494
558
 
559
+ ## MCP server
560
+
561
+ DocSlicer ships an [MCP](https://modelcontextprotocol.io) server, so LLM clients
562
+ (Claude Desktop, Claude Code, Cursor, …) can parse and read documents directly.
563
+
564
+ ```bash
565
+ pip install 'docslicer[mcp]'
566
+ docslicer-mcp # stdio — what desktop clients launch
567
+ docslicer-mcp --transport http --port 8000
568
+ ```
569
+
570
+ ### Claude Desktop / Cowork — one-click install
571
+
572
+ Download `docslicer-X.Y.Z.mcpb` from the
573
+ [latest release](https://github.com/DocSlicer/DocSlicer/releases/latest) and
574
+ double-click it, or drag it onto the Claude Desktop window. You pick the folder
575
+ DocSlicer is allowed to read and write during install; no config file, and no
576
+ Python of your own — `uv` provisions the interpreter.
577
+
578
+ ### Other clients
579
+
580
+ Every client below launches the server over stdio. `uvx` needs nothing
581
+ installed ahead of time:
582
+
583
+ ```jsonc
584
+ {
585
+ "mcpServers": {
586
+ "docslicer": {
587
+ "command": "uvx",
588
+ "args": ["--from", "docslicer[mcp]", "docslicer-mcp"],
589
+ "env": { "DOCSLICER_MCP_ROOT": "/Users/you/Documents" }
590
+ }
591
+ }
592
+ }
593
+ ```
594
+
595
+ If you'd rather install it once and skip the resolve on every launch, use
596
+ `pip install 'docslicer[mcp]'` (or `uv tool install`) and set
597
+ `"command": "docslicer-mcp"` with no `args`.
598
+
599
+ | Client | Where the config goes |
600
+ | --- | --- |
601
+ | Claude Code | `claude mcp add docslicer -- uvx --from 'docslicer[mcp]' docslicer-mcp` |
602
+ | Cursor | `~/.cursor/mcp.json`, or `.cursor/mcp.json` per project |
603
+ | VS Code | `.vscode/mcp.json` (use a `servers` key instead of `mcpServers`) |
604
+ | Windsurf | `~/.codeium/windsurf/mcp_config.json` |
605
+ | Zed | `settings.json`, under `context_servers` |
606
+
607
+ > **On GUI-launched clients, prefer the `.mcpb`.** An app started from the dock
608
+ > does not inherit your shell `PATH` — on macOS that excludes
609
+ > `/opt/homebrew/bin` — so a bare `uvx` or `docslicer-mcp` can work in a
610
+ > terminal and fail when the client spawns it. Use an absolute path
611
+ > (`which uvx`) if you hit this. The extension sidesteps it entirely.
612
+
613
+ ### How it works
614
+
615
+ A parsed document is far larger than a model's context window, so the server
616
+ never returns one in a single call. `parse` registers the document and hands
617
+ back a `doc_id` handle plus a heading outline. Every other tool takes that
618
+ handle and returns a bounded slice — the model pulls in only what it needs.
619
+
620
+ | Tool | Returns |
621
+ | --- | --- |
622
+ | `parse` | `doc_id` handle, title, page count, heading outline |
623
+ | `get_outline` | The outline again, for when it scrolls out of context |
624
+ | `read` | The text under one or more headings, named from the outline |
625
+ | `search` | Headings to `read`, ranked, each with a snippet |
626
+ | `to_markdown` | Writes the whole document to disk; returns the path |
627
+
628
+ Every outline line carries what reading it would cost:
629
+
630
+ ```
631
+ - Financial statements ~48k
632
+ - Note 14 — Segment reporting ~900
633
+ - Note 15 — Income taxes ~2.1k
634
+ ```
635
+
636
+ That figure is the same estimate `read` reports back, so a budget made from the
637
+ outline holds when it is spent. Sizes are cumulative — a parent never costs less
638
+ than the children beneath it — which is what makes "descend or just read it" a
639
+ decision the model can make before spending the context rather than after.
640
+
641
+ `read` takes heading text exactly as the outline prints it. Where a heading
642
+ appears twice, prefixing any ancestor disambiguates it (`"Notes > Revenue"`);
643
+ the full chain is never required. Returned text is interleaved with `[Page X]`
644
+ markers using the document's own page labels (`S-23`, `iv`), so a quotation can
645
+ be cited to the page it actually came from rather than to wherever its section
646
+ began.
647
+
648
+ `search` is the fallback for when the outline does not settle the question —
649
+ headings that name nothing useful (`Note 14`, `Item 7A`), or a figure buried in
650
+ a table no heading mentions. It combines a whole-word literal match with BM25
651
+ over the chunks, and returns *places*, not answers: each hit is a heading to
652
+ pass to `read`. Query terms that appear nowhere in the document are reported
653
+ back, so a query that scored well on one rare word can be recognised as the bad
654
+ query it was.
655
+
656
+ `to_markdown` is the escape hatch for when the user wants the document itself
657
+ rather than an answer drawn from it. It writes to disk and returns a path, so
658
+ nothing enters the model's context and document size stops mattering.
659
+
660
+ Parsed results are cached on disk, so re-parsing the same file with the same
661
+ options is free. The cache key includes the file's size and mtime — edit the
662
+ document and the next `parse` re-parses it automatically.
663
+
664
+ ### Configuration
665
+
666
+ | Variable | Effect |
667
+ | --- | --- |
668
+ | `DOCSLICER_MCP_ROOT` | Restrict file sources **and** written output to this directory tree |
669
+ | `DOCSLICER_MCP_ALLOW_URLS` | Set to `0` to reject `http(s)` sources |
670
+ | `DOCSLICER_MCP_CACHE` | Where parsed results are persisted (default `~/.cache/docslicer-mcp`) |
671
+ | `DOCSLICER_MCP_CACHE_MAX_MB` | Cache size ceiling, oldest pruned first (default `2048`; `0` disables) |
672
+
673
+ Set `DOCSLICER_MCP_ROOT` when exposing the server to anything but yourself —
674
+ without it, any readable path on the machine is parseable, and `to_markdown`
675
+ can write anywhere the server process can.
676
+
677
+ ---
678
+
679
+ ## Command line
680
+
681
+ `docslicer` parses one document to JSON on stdout — for a quick look at a file,
682
+ or to pipe into `jq`.
683
+
684
+ ```bash
685
+ docslicer report.pdf # chunks as JSON
686
+ docslicer report.pdf -o chunks.json # write to a file
687
+ docslicer report.pdf --no-chunking # blocks instead of chunks
688
+ ```
689
+
690
+ It takes the same parsing and chunking options as `parse_document`; run
691
+ `docslicer --help` for the full list.
692
+
693
+ ---
694
+
495
695
  ## Format-specific functions
496
696
 
497
697
  If you know the format upfront and want explicit failure on unexpected input, use the
@@ -506,6 +706,47 @@ docslicer.parse_html("filing.html")
506
706
 
507
707
  ---
508
708
 
709
+ ## Privacy Policy
710
+
711
+ Full policy: <https://docslicer.ai/privacy>
712
+
713
+ **What is collected.** Nothing. DocSlicer has no telemetry, analytics, crash
714
+ reporting, or usage tracking, and requires no account, licence key, or
715
+ registration.
716
+
717
+ **How your documents are used.** Parsing runs entirely on your own machine, in a
718
+ local process. Document contents are used only to produce the outline, text
719
+ slices, search results, and markdown you ask for, and are returned only to the
720
+ caller. Documents are never uploaded to DocSlicer or to any third party. When
721
+ running as an MCP server, `DOCSLICER_MCP_ROOT` bounds which directory tree may
722
+ be read from and written to.
723
+
724
+ **Where data is stored, and for how long.** Parsed results are cached on your
725
+ own disk — by default `~/.cache/docslicer-mcp`, configurable with
726
+ `DOCSLICER_MCP_CACHE`. The cache is pruned to a size ceiling
727
+ (`DOCSLICER_MCP_CACHE_MAX_MB`, default 2048 MB); otherwise it persists until you
728
+ delete it, and deleting the directory removes it permanently with no copy
729
+ retained elsewhere. Nothing is written outside the cache directory and any
730
+ output path you supply.
731
+
732
+ **Network access and third parties.** No network request is made for a local
733
+ file. Requests leave your machine only when you pass an `http(s)` source: that
734
+ URL is fetched directly from the host you named, and for HTML pages Playwright
735
+ may load the subresources that page references, exactly as a browser would.
736
+ Requests to `sec.gov` send a `User-Agent` header identifying the client, as the
737
+ SEC fair-access policy requires. These hosts are third parties chosen by you,
738
+ not by DocSlicer, and their own policies govern what they log. Set
739
+ `DOCSLICER_MCP_ALLOW_URLS=0` to reject remote sources entirely.
740
+
741
+ **Third-party clients.** When DocSlicer runs as an MCP server, the client
742
+ (Claude, Cursor, …) handles the conversation under its own privacy policy.
743
+ DocSlicer is not a party to that and receives nothing from it.
744
+
745
+ **Contact.** Privacy questions: jelle@docslicer.ai · Issues:
746
+ <https://github.com/DocSlicer/DocSlicer/issues>
747
+
748
+ ---
749
+
509
750
  ## License
510
751
 
511
752
  DocSlicer is **dual-licensed**:
@@ -514,3 +755,15 @@ DocSlicer is **dual-licensed**:
514
755
  - **[Commercial license](LICENSE-COMMERCIAL.md)** — for embedding DocSlicer in a closed-source or proprietary product, or offering it as part of a hosted/SaaS service without releasing your source.
515
756
 
516
757
  See [LICENSE-COMMERCIAL.md](LICENSE-COMMERCIAL.md) for details, or reach out about a commercial license.
758
+
759
+ ---
760
+
761
+ <!--
762
+ Ownership marker for the official MCP registry (registry.modelcontextprotocol.io).
763
+ The registry verifies a PyPI package by looking for this exact line in the
764
+ project description, which is this README — so it only takes effect once a
765
+ release carrying it is published. Namespace matches the GitHub org that owns
766
+ the repository. Leave it in place; removing it breaks re-publication.
767
+ -->
768
+
769
+ mcp-name: io.github.DocSlicer/docslicer
@@ -1,10 +1,21 @@
1
1
  # DocSlicer
2
2
 
3
- [![License: AGPL v3](https://img.shields.io/badge/License-AGPL_v3-blue.svg)](LICENSE) [![Commercial license available](https://img.shields.io/badge/License-Commercial-green.svg)](LICENSE-COMMERCIAL.md)
3
+ [![PyPI](https://img.shields.io/pypi/v/docslicer.svg)](https://pypi.org/project/docslicer/) [![Python versions](https://img.shields.io/pypi/pyversions/docslicer.svg)](https://pypi.org/project/docslicer/) [![License: AGPL v3](https://img.shields.io/badge/License-AGPL_v3-blue.svg)](LICENSE) [![Commercial license available](https://img.shields.io/badge/License-Commercial-green.svg)](LICENSE-COMMERCIAL.md)
4
4
 
5
- Lightning-fast, deterministic hierarchical document parser and chunker for business documents. No LLM calls or heavy ML models.
5
+ [![Install in VS Code](https://img.shields.io/badge/VS_Code-Install_MCP_server-0098FF?logo=visualstudiocode&logoColor=white)](https://insiders.vscode.dev/redirect/mcp/install?name=docslicer&config=%7B%22name%22%3A%22docslicer%22%2C%22command%22%3A%22uvx%22%2C%22args%22%3A%5B%22--from%22%2C%22docslicer%5Bmcp%5D%22%2C%22docslicer-mcp%22%5D%7D) [![Add to Cursor](https://img.shields.io/badge/Cursor-Add_MCP_server-000000?logo=cursor&logoColor=white)](https://cursor.com/install-mcp?name=docslicer&config=eyJjb21tYW5kIjoidXZ4IiwiYXJncyI6WyItLWZyb20iLCJkb2NzbGljZXJbbWNwXSIsImRvY3NsaWNlci1tY3AiXX0%3D) [![Download .mcpb for Claude Desktop](https://img.shields.io/badge/Claude_Desktop-Download_.mcpb-D97757?logo=claude&logoColor=white)](https://github.com/DocSlicer/DocSlicer/releases/latest)
6
6
 
7
- DocSlicer turns PDFs, Word documents, HTML pages, and PowerPoint files into clean chunks, structured blocks, tables, charts, and a navigable heading hierarchy preserving the document's own structure instead of guessing at it.
7
+ Lightning-fast (31 pages/sec), deterministic document parser and chunker for business documents. No LLM calls or heavy ML models.
8
+
9
+ DocSlicer turns PDFs, Word documents, HTML pages, and PowerPoint files into clean chunks, structured blocks, tables, charts, markdown and a navigable heading hierarchy.
10
+
11
+ Top score on [BizDocBench](https://github.com/DocSlicer/BizDocBench) (0.88 overall vs 0.70 for the next-best tool). 0.80 table accuracy, 0.98 content faithfulness, 0.85 heading recognition and hierarchy preservation, and 0.76 RAG retrieval performance.
12
+
13
+ **Two ways to use it:**
14
+
15
+ - **As a Python library — classic RAG.** The layout-aware chunker gives you clean, non-overlapping chunks, each carrying its full heading breadcrumb, ready to embed. [Jump to the API ↓](#parsing)
16
+ - **As an MCP server — vectorless RAG.** For when you want an answer out of a document right now. Claude, Cursor, or VS Code pulls the outline, picks the section it needs, and reads only that — no embedding, and no 200-page document in the context window. [Jump to setup ↓](#mcp-server)
17
+
18
+ ### Quick start
8
19
 
9
20
  ```python
10
21
  import docslicer
@@ -60,6 +71,23 @@ if __name__ == "__main__":
60
71
 
61
72
  ---
62
73
 
74
+ ## Benchmarks
75
+
76
+ Measured with [BizDocBench](https://github.com/DocSlicer/BizDocBench) — an open benchmark for multi-format business document parsing. All scores are 0–1 (higher is better); `pages_per_sec_aggregate` is throughput across the full corpus.
77
+
78
+ | Tool | Score | Coverage | Speed | Hierarchy | Faithfulness | Tables | Retrieval | Pages/sec |
79
+ |---|---|---|---|---|---|---|---|---|
80
+ | **docslicer** | **0.8796** | 1.0000 | 0.8836 | 0.8466 | 0.9824 | 0.8047 | 0.7601 | 31.27 |
81
+ | docling | 0.7036 | 1.0000 | 0.3805 | 0.4905 | 0.8927 | 0.7467 | 0.7111 | 3.46 |
82
+ | markitdown | 0.5838 | 1.0000 | 0.8513 | 0.0604 | 0.7972 | 0.2584 | 0.5357 | 27.42 |
83
+ | unstructured | 0.5798 | 0.9091 | 0.1073 | 0.4327 | 0.9057 | 0.4812 | 0.6430 | 0.52 |
84
+ | opendataloader | 0.5359 | 0.5844 | 1.0000 | 0.3853 | 0.6484 | 0.2655 | 0.3317 | 117.26 |
85
+ | pymupdf4llm | 0.4519 | 0.5974 | 0.6492 | 0.1089 | 0.6456 | 0.3551 | 0.3552 | 11.84 |
86
+ | mineru | 0.4107 | 0.5974 | 0.1353 | 0.4220 | 0.6176 | 0.3012 | 0.3910 | 0.70 |
87
+ | marker | 0.3735 | 0.5974 | 0.1598 | 0.1926 | 0.6121 | 0.3012 | 0.3778 | 0.87 |
88
+
89
+ ---
90
+
63
91
  ## Install
64
92
 
65
93
  ```bash
@@ -78,6 +106,7 @@ pip install 'docslicer[ocr]' # scanned PDF support via Tesseract + OpenCV
78
106
  # Linux: apt install tesseract-ocr
79
107
  # macOS: brew install tesseract
80
108
 
109
+ pip install 'docslicer[mcp]' # MCP server for LLM clients (Claude, Cursor, …)
81
110
  pip install 'docslicer[llm]' # exact token counts via tiktoken (exact_tokens=True)
82
111
  pip install 'docslicer[crypto]' # password-protected Office files (msoffcrypto-tool)
83
112
  pip install 'docslicer[parquet]' # Parquet export support
@@ -380,22 +409,51 @@ result.tables_by_page(14)
380
409
  result.charts_by_page(14)
381
410
  ```
382
411
 
412
+ ### Parse once, navigate many times
413
+
414
+ A parsed result is plain data, so you can persist it and reload it later. When an
415
+ agent asks many questions about the same document, there's no need to parse it
416
+ again on every question:
417
+
418
+ ```python
419
+ from pathlib import Path
420
+ import docslicer
421
+
422
+ cache = Path("annual_report.json")
423
+
424
+ if cache.exists():
425
+ result = docslicer.ParseResult.load(cache)
426
+ else:
427
+ result = docslicer.parse_document("annual_report.pdf")
428
+ result.save(cache)
429
+ ```
430
+
431
+ A reloaded result supports the full API — `hierarchy`, `find_heading`,
432
+ `chunks_under`, `tables` — so a long-running agent session or document server can
433
+ keep documents open across requests without re-parsing.
434
+
383
435
  ---
384
436
 
385
437
  ## Export
386
438
 
439
+ `save()` decides what to write from the path you give it.
440
+
387
441
  ```python
388
- # Save everything
389
- result.save("output/")
390
- # output/chunks.parquet, blocks.parquet, tables.parquet, metadata.json
391
- # (+ charts.parquet when the document has charts)
442
+ # Save the whole result and reload it later — keeps the heading hierarchy
443
+ result.save("result.json") # same output as result.to_json()
444
+ result = docslicer.ParseResult.load("result.json")
392
445
 
393
- # Specific formats
446
+ # A single collection, in the format you name
394
447
  result.save("chunks.csv")
395
448
  result.save("charts.jsonl") # stems: chunks | blocks | tables | charts | metadata
396
- result.save("result.json") # full parse result as JSON
397
449
  result.export_chunks_jsonl("chunks.jsonl")
398
450
 
451
+ # One file per collection
452
+ result.save("output/")
453
+ # → output/chunks.parquet, blocks.parquet, tables.parquet, metadata.json
454
+ # (+ charts.parquet when the document has charts)
455
+ # Falls back to .csv unless the [parquet] extra is installed.
456
+
399
457
  # Render as Markdown or plain text
400
458
  md = result.export_to_markdown(include_tables=True)
401
459
  txt = result.export_to_text()
@@ -404,6 +462,9 @@ txt = result.export_to_text()
404
462
  df = result.chunks_df()
405
463
  ```
406
464
 
465
+ Only `result.json` round-trips — the collection and directory forms write flat rows
466
+ without the heading hierarchy, so `ParseResult.load()` can't read them back.
467
+
407
468
  ### Debug mode
408
469
 
409
470
  ```python
@@ -435,6 +496,142 @@ pip install 'docslicer[ocr]'
435
496
 
436
497
  ---
437
498
 
499
+ ## MCP server
500
+
501
+ DocSlicer ships an [MCP](https://modelcontextprotocol.io) server, so LLM clients
502
+ (Claude Desktop, Claude Code, Cursor, …) can parse and read documents directly.
503
+
504
+ ```bash
505
+ pip install 'docslicer[mcp]'
506
+ docslicer-mcp # stdio — what desktop clients launch
507
+ docslicer-mcp --transport http --port 8000
508
+ ```
509
+
510
+ ### Claude Desktop / Cowork — one-click install
511
+
512
+ Download `docslicer-X.Y.Z.mcpb` from the
513
+ [latest release](https://github.com/DocSlicer/DocSlicer/releases/latest) and
514
+ double-click it, or drag it onto the Claude Desktop window. You pick the folder
515
+ DocSlicer is allowed to read and write during install; no config file, and no
516
+ Python of your own — `uv` provisions the interpreter.
517
+
518
+ ### Other clients
519
+
520
+ Every client below launches the server over stdio. `uvx` needs nothing
521
+ installed ahead of time:
522
+
523
+ ```jsonc
524
+ {
525
+ "mcpServers": {
526
+ "docslicer": {
527
+ "command": "uvx",
528
+ "args": ["--from", "docslicer[mcp]", "docslicer-mcp"],
529
+ "env": { "DOCSLICER_MCP_ROOT": "/Users/you/Documents" }
530
+ }
531
+ }
532
+ }
533
+ ```
534
+
535
+ If you'd rather install it once and skip the resolve on every launch, use
536
+ `pip install 'docslicer[mcp]'` (or `uv tool install`) and set
537
+ `"command": "docslicer-mcp"` with no `args`.
538
+
539
+ | Client | Where the config goes |
540
+ | --- | --- |
541
+ | Claude Code | `claude mcp add docslicer -- uvx --from 'docslicer[mcp]' docslicer-mcp` |
542
+ | Cursor | `~/.cursor/mcp.json`, or `.cursor/mcp.json` per project |
543
+ | VS Code | `.vscode/mcp.json` (use a `servers` key instead of `mcpServers`) |
544
+ | Windsurf | `~/.codeium/windsurf/mcp_config.json` |
545
+ | Zed | `settings.json`, under `context_servers` |
546
+
547
+ > **On GUI-launched clients, prefer the `.mcpb`.** An app started from the dock
548
+ > does not inherit your shell `PATH` — on macOS that excludes
549
+ > `/opt/homebrew/bin` — so a bare `uvx` or `docslicer-mcp` can work in a
550
+ > terminal and fail when the client spawns it. Use an absolute path
551
+ > (`which uvx`) if you hit this. The extension sidesteps it entirely.
552
+
553
+ ### How it works
554
+
555
+ A parsed document is far larger than a model's context window, so the server
556
+ never returns one in a single call. `parse` registers the document and hands
557
+ back a `doc_id` handle plus a heading outline. Every other tool takes that
558
+ handle and returns a bounded slice — the model pulls in only what it needs.
559
+
560
+ | Tool | Returns |
561
+ | --- | --- |
562
+ | `parse` | `doc_id` handle, title, page count, heading outline |
563
+ | `get_outline` | The outline again, for when it scrolls out of context |
564
+ | `read` | The text under one or more headings, named from the outline |
565
+ | `search` | Headings to `read`, ranked, each with a snippet |
566
+ | `to_markdown` | Writes the whole document to disk; returns the path |
567
+
568
+ Every outline line carries what reading it would cost:
569
+
570
+ ```
571
+ - Financial statements ~48k
572
+ - Note 14 — Segment reporting ~900
573
+ - Note 15 — Income taxes ~2.1k
574
+ ```
575
+
576
+ That figure is the same estimate `read` reports back, so a budget made from the
577
+ outline holds when it is spent. Sizes are cumulative — a parent never costs less
578
+ than the children beneath it — which is what makes "descend or just read it" a
579
+ decision the model can make before spending the context rather than after.
580
+
581
+ `read` takes heading text exactly as the outline prints it. Where a heading
582
+ appears twice, prefixing any ancestor disambiguates it (`"Notes > Revenue"`);
583
+ the full chain is never required. Returned text is interleaved with `[Page X]`
584
+ markers using the document's own page labels (`S-23`, `iv`), so a quotation can
585
+ be cited to the page it actually came from rather than to wherever its section
586
+ began.
587
+
588
+ `search` is the fallback for when the outline does not settle the question —
589
+ headings that name nothing useful (`Note 14`, `Item 7A`), or a figure buried in
590
+ a table no heading mentions. It combines a whole-word literal match with BM25
591
+ over the chunks, and returns *places*, not answers: each hit is a heading to
592
+ pass to `read`. Query terms that appear nowhere in the document are reported
593
+ back, so a query that scored well on one rare word can be recognised as the bad
594
+ query it was.
595
+
596
+ `to_markdown` is the escape hatch for when the user wants the document itself
597
+ rather than an answer drawn from it. It writes to disk and returns a path, so
598
+ nothing enters the model's context and document size stops mattering.
599
+
600
+ Parsed results are cached on disk, so re-parsing the same file with the same
601
+ options is free. The cache key includes the file's size and mtime — edit the
602
+ document and the next `parse` re-parses it automatically.
603
+
604
+ ### Configuration
605
+
606
+ | Variable | Effect |
607
+ | --- | --- |
608
+ | `DOCSLICER_MCP_ROOT` | Restrict file sources **and** written output to this directory tree |
609
+ | `DOCSLICER_MCP_ALLOW_URLS` | Set to `0` to reject `http(s)` sources |
610
+ | `DOCSLICER_MCP_CACHE` | Where parsed results are persisted (default `~/.cache/docslicer-mcp`) |
611
+ | `DOCSLICER_MCP_CACHE_MAX_MB` | Cache size ceiling, oldest pruned first (default `2048`; `0` disables) |
612
+
613
+ Set `DOCSLICER_MCP_ROOT` when exposing the server to anything but yourself —
614
+ without it, any readable path on the machine is parseable, and `to_markdown`
615
+ can write anywhere the server process can.
616
+
617
+ ---
618
+
619
+ ## Command line
620
+
621
+ `docslicer` parses one document to JSON on stdout — for a quick look at a file,
622
+ or to pipe into `jq`.
623
+
624
+ ```bash
625
+ docslicer report.pdf # chunks as JSON
626
+ docslicer report.pdf -o chunks.json # write to a file
627
+ docslicer report.pdf --no-chunking # blocks instead of chunks
628
+ ```
629
+
630
+ It takes the same parsing and chunking options as `parse_document`; run
631
+ `docslicer --help` for the full list.
632
+
633
+ ---
634
+
438
635
  ## Format-specific functions
439
636
 
440
637
  If you know the format upfront and want explicit failure on unexpected input, use the
@@ -449,6 +646,47 @@ docslicer.parse_html("filing.html")
449
646
 
450
647
  ---
451
648
 
649
+ ## Privacy Policy
650
+
651
+ Full policy: <https://docslicer.ai/privacy>
652
+
653
+ **What is collected.** Nothing. DocSlicer has no telemetry, analytics, crash
654
+ reporting, or usage tracking, and requires no account, licence key, or
655
+ registration.
656
+
657
+ **How your documents are used.** Parsing runs entirely on your own machine, in a
658
+ local process. Document contents are used only to produce the outline, text
659
+ slices, search results, and markdown you ask for, and are returned only to the
660
+ caller. Documents are never uploaded to DocSlicer or to any third party. When
661
+ running as an MCP server, `DOCSLICER_MCP_ROOT` bounds which directory tree may
662
+ be read from and written to.
663
+
664
+ **Where data is stored, and for how long.** Parsed results are cached on your
665
+ own disk — by default `~/.cache/docslicer-mcp`, configurable with
666
+ `DOCSLICER_MCP_CACHE`. The cache is pruned to a size ceiling
667
+ (`DOCSLICER_MCP_CACHE_MAX_MB`, default 2048 MB); otherwise it persists until you
668
+ delete it, and deleting the directory removes it permanently with no copy
669
+ retained elsewhere. Nothing is written outside the cache directory and any
670
+ output path you supply.
671
+
672
+ **Network access and third parties.** No network request is made for a local
673
+ file. Requests leave your machine only when you pass an `http(s)` source: that
674
+ URL is fetched directly from the host you named, and for HTML pages Playwright
675
+ may load the subresources that page references, exactly as a browser would.
676
+ Requests to `sec.gov` send a `User-Agent` header identifying the client, as the
677
+ SEC fair-access policy requires. These hosts are third parties chosen by you,
678
+ not by DocSlicer, and their own policies govern what they log. Set
679
+ `DOCSLICER_MCP_ALLOW_URLS=0` to reject remote sources entirely.
680
+
681
+ **Third-party clients.** When DocSlicer runs as an MCP server, the client
682
+ (Claude, Cursor, …) handles the conversation under its own privacy policy.
683
+ DocSlicer is not a party to that and receives nothing from it.
684
+
685
+ **Contact.** Privacy questions: jelle@docslicer.ai · Issues:
686
+ <https://github.com/DocSlicer/DocSlicer/issues>
687
+
688
+ ---
689
+
452
690
  ## License
453
691
 
454
692
  DocSlicer is **dual-licensed**:
@@ -457,3 +695,15 @@ DocSlicer is **dual-licensed**:
457
695
  - **[Commercial license](LICENSE-COMMERCIAL.md)** — for embedding DocSlicer in a closed-source or proprietary product, or offering it as part of a hosted/SaaS service without releasing your source.
458
696
 
459
697
  See [LICENSE-COMMERCIAL.md](LICENSE-COMMERCIAL.md) for details, or reach out about a commercial license.
698
+
699
+ ---
700
+
701
+ <!--
702
+ Ownership marker for the official MCP registry (registry.modelcontextprotocol.io).
703
+ The registry verifies a PyPI package by looking for this exact line in the
704
+ project description, which is this README — so it only takes effect once a
705
+ release carrying it is published. Namespace matches the GitHub org that owns
706
+ the repository. Leave it in place; removing it breaks re-publication.
707
+ -->
708
+
709
+ mcp-name: io.github.DocSlicer/docslicer