docpull 4.0.0__tar.gz → 4.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (106) hide show
  1. {docpull-4.0.0/src/docpull.egg-info → docpull-4.1.0}/PKG-INFO +184 -27
  2. docpull-4.1.0/README.md +476 -0
  3. {docpull-4.0.0 → docpull-4.1.0}/pyproject.toml +6 -2
  4. {docpull-4.0.0 → docpull-4.1.0}/src/docpull/__init__.py +1 -6
  5. {docpull-4.0.0 → docpull-4.1.0}/src/docpull/cache/__init__.py +4 -0
  6. docpull-4.1.0/src/docpull/cache/frontier.py +199 -0
  7. {docpull-4.0.0 → docpull-4.1.0}/src/docpull/cache/manager.py +140 -23
  8. {docpull-4.0.0 → docpull-4.1.0}/src/docpull/cli.py +44 -17
  9. {docpull-4.0.0 → docpull-4.1.0}/src/docpull/conversion/chunking.py +56 -23
  10. {docpull-4.0.0 → docpull-4.1.0}/src/docpull/conversion/extractor.py +129 -1
  11. docpull-4.1.0/src/docpull/conversion/markdown.py +516 -0
  12. {docpull-4.0.0 → docpull-4.1.0}/src/docpull/conversion/special_cases.py +108 -3
  13. {docpull-4.0.0 → docpull-4.1.0}/src/docpull/core/fetcher.py +111 -53
  14. {docpull-4.0.0 → docpull-4.1.0}/src/docpull/discovery/__init__.py +0 -4
  15. {docpull-4.0.0 → docpull-4.1.0}/src/docpull/discovery/crawler.py +0 -19
  16. {docpull-4.0.0 → docpull-4.1.0}/src/docpull/discovery/filters.py +1 -6
  17. {docpull-4.0.0 → docpull-4.1.0}/src/docpull/discovery/sitemap.py +0 -4
  18. {docpull-4.0.0 → docpull-4.1.0}/src/docpull/doctor.py +0 -13
  19. docpull-4.1.0/src/docpull/fixtures/__init__.py +1 -0
  20. docpull-4.1.0/src/docpull/fixtures/parallel-search-extract.json +103 -0
  21. {docpull-4.0.0 → docpull-4.1.0}/src/docpull/http/client.py +80 -11
  22. {docpull-4.0.0 → docpull-4.1.0}/src/docpull/mcp/server.py +299 -1
  23. {docpull-4.0.0 → docpull-4.1.0}/src/docpull/mcp/sources.py +1 -0
  24. {docpull-4.0.0 → docpull-4.1.0}/src/docpull/mcp/tools.py +92 -17
  25. {docpull-4.0.0 → docpull-4.1.0}/src/docpull/models/__init__.py +16 -3
  26. docpull-4.1.0/src/docpull/models/document.py +78 -0
  27. {docpull-4.0.0 → docpull-4.1.0}/src/docpull/models/events.py +41 -2
  28. {docpull-4.0.0 → docpull-4.1.0}/src/docpull/models/profiles.py +6 -8
  29. docpull-4.1.0/src/docpull/models/run.py +60 -0
  30. docpull-4.1.0/src/docpull/pack_tools.py +500 -0
  31. docpull-4.1.0/src/docpull/parallel_workflows.py +5809 -0
  32. {docpull-4.0.0 → docpull-4.1.0}/src/docpull/pipeline/base.py +60 -12
  33. docpull-4.1.0/src/docpull/pipeline/manifest.py +74 -0
  34. {docpull-4.0.0 → docpull-4.1.0}/src/docpull/pipeline/steps/convert.py +46 -6
  35. {docpull-4.0.0 → docpull-4.1.0}/src/docpull/pipeline/steps/dedup.py +11 -4
  36. {docpull-4.0.0 → docpull-4.1.0}/src/docpull/pipeline/steps/fetch.py +20 -32
  37. {docpull-4.0.0 → docpull-4.1.0}/src/docpull/pipeline/steps/save.py +53 -6
  38. {docpull-4.0.0 → docpull-4.1.0}/src/docpull/pipeline/steps/save_json.py +30 -11
  39. {docpull-4.0.0 → docpull-4.1.0}/src/docpull/pipeline/steps/save_ndjson.py +39 -21
  40. {docpull-4.0.0 → docpull-4.1.0}/src/docpull/pipeline/steps/save_sqlite.py +68 -10
  41. {docpull-4.0.0 → docpull-4.1.0}/src/docpull/pipeline/steps/validate.py +10 -7
  42. docpull-4.1.0/src/docpull/security/__init__.py +13 -0
  43. docpull-4.1.0/src/docpull/security/download_policy.py +339 -0
  44. {docpull-4.0.0 → docpull-4.1.0}/src/docpull/security/url_validator.py +0 -6
  45. docpull-4.1.0/src/docpull/source_scoring.py +98 -0
  46. {docpull-4.0.0 → docpull-4.1.0/src/docpull.egg-info}/PKG-INFO +184 -27
  47. {docpull-4.0.0 → docpull-4.1.0}/src/docpull.egg-info/SOURCES.txt +15 -0
  48. {docpull-4.0.0 → docpull-4.1.0}/src/docpull.egg-info/requires.txt +4 -0
  49. {docpull-4.0.0 → docpull-4.1.0}/tests/test_chunking.py +24 -0
  50. docpull-4.1.0/tests/test_cli.py +116 -0
  51. {docpull-4.0.0 → docpull-4.1.0}/tests/test_conversion.py +88 -0
  52. {docpull-4.0.0 → docpull-4.1.0}/tests/test_convert_step_new.py +31 -8
  53. docpull-4.1.0/tests/test_frontier_resume.py +35 -0
  54. {docpull-4.0.0 → docpull-4.1.0}/tests/test_integration.py +67 -0
  55. {docpull-4.0.0 → docpull-4.1.0}/tests/test_mcp_server.py +13 -0
  56. {docpull-4.0.0 → docpull-4.1.0}/tests/test_mcp_tools.py +40 -1
  57. docpull-4.1.0/tests/test_outputs_e2e.py +120 -0
  58. docpull-4.1.0/tests/test_pack_tools.py +241 -0
  59. docpull-4.1.0/tests/test_parallel_workflows.py +2205 -0
  60. {docpull-4.0.0 → docpull-4.1.0}/tests/test_save_ndjson.py +2 -0
  61. docpull-4.1.0/tests/test_save_sqlite.py +52 -0
  62. {docpull-4.0.0 → docpull-4.1.0}/tests/test_security_hardening.py +299 -0
  63. {docpull-4.0.0 → docpull-4.1.0}/tests/test_special_cases.py +99 -1
  64. docpull-4.0.0/README.md +0 -322
  65. docpull-4.0.0/src/docpull/conversion/markdown.py +0 -280
  66. docpull-4.0.0/src/docpull/security/__init__.py +0 -6
  67. docpull-4.0.0/tests/test_cli.py +0 -20
  68. {docpull-4.0.0 → docpull-4.1.0}/LICENSE +0 -0
  69. {docpull-4.0.0 → docpull-4.1.0}/setup.cfg +0 -0
  70. {docpull-4.0.0 → docpull-4.1.0}/src/docpull/__main__.py +0 -0
  71. {docpull-4.0.0 → docpull-4.1.0}/src/docpull/cache/streaming_dedup.py +0 -0
  72. {docpull-4.0.0 → docpull-4.1.0}/src/docpull/conversion/__init__.py +0 -0
  73. {docpull-4.0.0 → docpull-4.1.0}/src/docpull/conversion/protocols.py +0 -0
  74. {docpull-4.0.0 → docpull-4.1.0}/src/docpull/conversion/trafilatura_extractor.py +0 -0
  75. {docpull-4.0.0 → docpull-4.1.0}/src/docpull/core/__init__.py +0 -0
  76. {docpull-4.0.0 → docpull-4.1.0}/src/docpull/discovery/_fetch.py +0 -0
  77. {docpull-4.0.0 → docpull-4.1.0}/src/docpull/discovery/composite.py +0 -0
  78. {docpull-4.0.0 → docpull-4.1.0}/src/docpull/discovery/link_extractors/__init__.py +0 -0
  79. {docpull-4.0.0 → docpull-4.1.0}/src/docpull/discovery/link_extractors/enhanced.py +0 -0
  80. {docpull-4.0.0 → docpull-4.1.0}/src/docpull/discovery/link_extractors/protocols.py +0 -0
  81. {docpull-4.0.0 → docpull-4.1.0}/src/docpull/discovery/link_extractors/static.py +0 -0
  82. {docpull-4.0.0 → docpull-4.1.0}/src/docpull/discovery/protocols.py +0 -0
  83. {docpull-4.0.0 → docpull-4.1.0}/src/docpull/http/__init__.py +0 -0
  84. {docpull-4.0.0 → docpull-4.1.0}/src/docpull/http/protocols.py +0 -0
  85. {docpull-4.0.0 → docpull-4.1.0}/src/docpull/http/rate_limiter.py +0 -0
  86. {docpull-4.0.0 → docpull-4.1.0}/src/docpull/mcp/__init__.py +0 -0
  87. {docpull-4.0.0 → docpull-4.1.0}/src/docpull/metadata_extractor.py +0 -0
  88. {docpull-4.0.0 → docpull-4.1.0}/src/docpull/models/config.py +0 -0
  89. {docpull-4.0.0 → docpull-4.1.0}/src/docpull/pipeline/__init__.py +0 -0
  90. {docpull-4.0.0 → docpull-4.1.0}/src/docpull/pipeline/steps/__init__.py +0 -0
  91. {docpull-4.0.0 → docpull-4.1.0}/src/docpull/pipeline/steps/chunk.py +0 -0
  92. {docpull-4.0.0 → docpull-4.1.0}/src/docpull/pipeline/steps/metadata.py +0 -0
  93. {docpull-4.0.0 → docpull-4.1.0}/src/docpull/py.typed +0 -0
  94. {docpull-4.0.0 → docpull-4.1.0}/src/docpull/security/robots.py +0 -0
  95. {docpull-4.0.0 → docpull-4.1.0}/src/docpull/time_utils.py +0 -0
  96. {docpull-4.0.0 → docpull-4.1.0}/src/docpull.egg-info/dependency_links.txt +0 -0
  97. {docpull-4.0.0 → docpull-4.1.0}/src/docpull.egg-info/entry_points.txt +0 -0
  98. {docpull-4.0.0 → docpull-4.1.0}/src/docpull.egg-info/top_level.txt +0 -0
  99. {docpull-4.0.0 → docpull-4.1.0}/tests/test_cache_conditional_get.py +0 -0
  100. {docpull-4.0.0 → docpull-4.1.0}/tests/test_ci_policy.py +0 -0
  101. {docpull-4.0.0 → docpull-4.1.0}/tests/test_discovery.py +0 -0
  102. {docpull-4.0.0 → docpull-4.1.0}/tests/test_link_extractors.py +0 -0
  103. {docpull-4.0.0 → docpull-4.1.0}/tests/test_naming.py +0 -0
  104. {docpull-4.0.0 → docpull-4.1.0}/tests/test_pipeline.py +0 -0
  105. {docpull-4.0.0 → docpull-4.1.0}/tests/test_real_site_regressions.py +0 -0
  106. {docpull-4.0.0 → docpull-4.1.0}/tests/test_time_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: docpull
3
- Version: 4.0.0
3
+ Version: 4.1.0
4
4
  Summary: Pull documentation from the web and convert to clean markdown
5
5
  Author-email: Zachary Roth <support@raintree.technology>
6
6
  Maintainer-email: Raintree Technology <support@raintree.technology>
@@ -62,6 +62,8 @@ Requires-Dist: mcp>=1.0.0; extra == "mcp"
62
62
  Requires-Dist: pyjwt>=2.13.0; extra == "mcp"
63
63
  Requires-Dist: python-multipart>=0.0.27; extra == "mcp"
64
64
  Requires-Dist: starlette>=1.0.1; extra == "mcp"
65
+ Provides-Extra: parallel
66
+ Requires-Dist: parallel-web>=1.0.1; extra == "parallel"
65
67
  Provides-Extra: llm
66
68
  Requires-Dist: tiktoken>=0.7.0; extra == "llm"
67
69
  Provides-Extra: all
@@ -73,6 +75,7 @@ Requires-Dist: mcp>=1.0.0; extra == "all"
73
75
  Requires-Dist: pyjwt>=2.13.0; extra == "all"
74
76
  Requires-Dist: python-multipart>=0.0.27; extra == "all"
75
77
  Requires-Dist: starlette>=1.0.1; extra == "all"
78
+ Requires-Dist: parallel-web>=1.0.1; extra == "all"
76
79
  Provides-Extra: dev
77
80
  Requires-Dist: pytest>=7.0.0; extra == "dev"
78
81
  Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
@@ -152,7 +155,7 @@ content directly from framework data feeds:
152
155
  | Mintlify | `__NEXT_DATA__` with Mintlify tagging |
153
156
  | OpenAPI | Renders `openapi.json` / `swagger.json` into Markdown |
154
157
  | Docusaurus| Detected and tagged; generic extractor produces Markdown |
155
- | Sphinx | Detected and tagged; generic extractor produces Markdown |
158
+ | Sphinx | Detected from generator metadata / Read the Docs hosts and tagged; generic extractor produces Markdown |
156
159
 
157
160
  JS-only SPAs with no server-rendered content are detected and skipped with a
158
161
  clear reason (or, with `--strict-js-required`, reported as an error so agents
@@ -215,11 +218,146 @@ async def tool_call(url: str) -> str:
215
218
 
216
219
  ```bash
217
220
  docpull https://site.com --profile rag # Default. Dedup, rich metadata.
218
- docpull https://site.com --profile llm # NDJSON + chunks + metadata.
219
- docpull https://site.com --profile mirror # Full archive, polite, cached.
221
+ docpull https://site.com --profile llm # NDJSON + chunks + metadata; JS-only pages skip unless --strict-js-required is passed.
222
+ docpull https://site.com --profile mirror # Full archive, polite, cached, hierarchical paths.
220
223
  docpull https://site.com --profile quick # Sampling: 50 pages, depth 2.
221
224
  ```
222
225
 
226
+ ## Parallel context packs
227
+
228
+ `docpull[parallel]` adds an optional source-discovery and research layer on top
229
+ of Parallel web intelligence APIs. Use the core crawler when you already know
230
+ the docs URL. Use Parallel packs when an agent needs current web sources found,
231
+ extracted, researched, organized, and loaded as durable local context.
232
+
233
+ Parallel handles live Search, Extract, Task, Entity Search, FindAll, TaskGroup,
234
+ and Monitor calls. docpull turns those results into local, inspectable packs
235
+ with Markdown, NDJSON, manifests, hashes, source indexes, workflow metadata, and
236
+ an `AGENT_CONTEXT.md` load plan so agents do not need to reverse-engineer raw API
237
+ responses before using the pack.
238
+
239
+ Live calls use your own Parallel API key. docpull never proxies requests through
240
+ a shared account or writes the key into pack artifacts. For normal PyPI installs,
241
+ `docpull parallel init` stores the key in a local secrets file with restricted
242
+ permissions so it works across shells and agent sessions. Pack artifacts can
243
+ still contain source content, workflow inputs/outputs, selected URLs, and
244
+ metadata, so treat them as research data.
245
+
246
+ ```bash
247
+ pip install 'docpull[parallel]'
248
+
249
+ # Recommended local setup. Prompts securely and writes ~/.config/docpull/secrets.env.
250
+ docpull parallel init
251
+ docpull parallel auth --json
252
+
253
+ # Optional project-local setup for agent worktrees. Writes .env.local and
254
+ # adds it to .gitignore.
255
+ docpull parallel init --project
256
+
257
+ # CI can still use a normal environment secret instead.
258
+ export PARALLEL_API_KEY="<your-parallel-api-key>"
259
+
260
+ docpull parallel context-pack "Compare AI web-search APIs for agents" \
261
+ --query "AI web search API" \
262
+ --query "agent web extraction API" \
263
+ --include-domain parallel.ai \
264
+ --exclude-domain onparallel.com \
265
+ --output-dir ./packs/ai-web-search \
266
+ --mode advanced \
267
+ --extract-limit 8 \
268
+ --max-estimated-cost 0.05 \
269
+ --task-brief
270
+ ```
271
+
272
+ `docpull parallel auth` checks local SDK/key presence without printing the key
273
+ value and reports the key source (`env`, `project_env`, `user_config`, or
274
+ `missing`). It does not make a live Parallel call or prove the key is valid. Use
275
+ `--json` when an agent or CI job needs machine-readable configuration status.
276
+
277
+ The pack contains:
278
+
279
+ - `AGENT_CONTEXT.md` — agent load plan with source order, pack signals, warnings, errors, and artifact map.
280
+ - `documents.ndjson` — chunked records for agents and RAG pipelines.
281
+ - `corpus.manifest.json` — stable docpull record manifest.
282
+ - `parallel.pack.json` — Parallel workflow metadata, selected URLs, errors, IDs, and usage.
283
+ - `sources.md` and `sources/*.md` — human-readable sources and extracted Markdown.
284
+ - `brief.md` when `--task-brief` is enabled.
285
+
286
+ That makes the Parallel layer more than a pass-through wrapper: Parallel finds
287
+ and enriches live web sources, while docpull normalizes them into a repeatable
288
+ context pack that can be scored, diffed, audited, and loaded offline.
289
+
290
+ For planning without spending credits, use `--dry-run`. For repeatable packs,
291
+ use `--include-domain`, `--exclude-domain`, `--after-date`, and `--max-search-results`
292
+ to pin the source policy that appears later in `parallel.pack.json`.
293
+ Use `--fetch-max-age-seconds`, `--fetch-timeout-seconds`,
294
+ `--disable-cache-fallback`, `--excerpt-chars-per-result`, and `--location`
295
+ when you need Parallel's freshness, excerpt-size, and geo controls.
296
+
297
+ Offline fixtures are supported for demos and tests without a Parallel account:
298
+
299
+ ```bash
300
+ docpull parallel demo --output-dir ./packs/demo
301
+ ```
302
+
303
+ When working from the source checkout, the same fixture can also be imported
304
+ directly with `docpull parallel import ./docs/examples/parallel-search-extract.json`.
305
+
306
+ The core workflow is Search + Extract + optional Task. Broader pack workflows
307
+ cover Search-only snapshots, docs discovery plans, Extract-known-URL packs,
308
+ core-fetch-with-Parallel-fallback packs, diff briefs, Task lifecycle snapshots,
309
+ Entity Search, FindAll lifecycle actions, TaskGroup batches, Monitor
310
+ metadata/events, API specs, and local source scoring.
311
+ See [docs/parallel.md](docs/parallel.md) for the Parallel product cross-reference
312
+ and the boundary between docpull, Parallel MCP, and planned workflows.
313
+
314
+ Additional Parallel-backed artifact commands:
315
+
316
+ ```bash
317
+ # Readiness and API-key setup checks.
318
+ docpull parallel auth
319
+ docpull parallel init
320
+ docpull parallel init --project
321
+
322
+ # Search + Extract + optional Task brief.
323
+ docpull parallel context-pack "Compare AI web-search APIs for agents" \
324
+ --query "AI web search API" \
325
+ --include-domain parallel.ai \
326
+ --dry-run
327
+
328
+ # Search-only, docs-discovery, known-URL extract, fallback, and structured Task packs.
329
+ docpull parallel search-pack "Parallel Search API docs" --query "Parallel Search API" --dry-run
330
+ docpull parallel discover-docs "Find Parallel API docs" \
331
+ --query "Parallel Search API docs" \
332
+ --include-domain docs.parallel.ai \
333
+ --dry-run
334
+ docpull parallel extract-pack https://docs.parallel.ai/api-reference/search/search --dry-run
335
+ docpull parallel fallback-pack https://docs.parallel.ai/api-reference/search/search --dry-run
336
+ docpull parallel task-pack "Research Parallel Search API" \
337
+ --source-include-domain docs.parallel.ai \
338
+ --output-schema-json '{"type":"object","properties":{"summary":{"type":"string"}},"required":["summary"]}' \
339
+ --dry-run
340
+
341
+ # Entity dossiers, FindAll lifecycle, batch enrichment, monitors, and API specs.
342
+ docpull parallel entity-pack "AI developer infrastructure companies" --entity-type companies
343
+ docpull parallel findall-pack "AI developer infrastructure companies" --generator preview --dry-run
344
+ docpull parallel findall-ingest-pack "Find companies with public API docs"
345
+ docpull parallel findall-result-pack findall_123 --output-dir ./packs/findall-result
346
+ docpull parallel taskgroup-pack ./companies.json --prompt-template "Research {company}" --dry-run
347
+ docpull parallel taskgroup-pack ./companies.json --prompt-template "Research {company}" --wait
348
+ docpull parallel monitor-pack create "New vendor pricing changes" --dry-run
349
+ docpull parallel monitor-pack create --type snapshot --task-run-id run_123 --dry-run
350
+ docpull parallel monitor-pack list --status active --output-dir ./packs/monitors
351
+ docpull parallel monitor-pack events monitor_123 --cursor next_cursor --output-dir ./packs/events
352
+ docpull parallel api-pack https://docs.parallel.ai/public-openapi.json --output-dir ./packs/parallel-openapi
353
+ docpull parallel run ./docs/examples/parallel-llms-api-pack.yaml --dry-run
354
+ docpull parallel run ./docs/examples/parallel-openapi-api-pack.yaml --dry-run
355
+ docpull pack score ./packs/parallel-openapi
356
+ docpull pack sources ./packs/parallel-openapi --require-domain docs.parallel.ai
357
+ docpull pack diff ./packs/old ./packs/new --markdown ./packs/changes.md
358
+ docpull parallel diff-brief ./packs/old ./packs/new --dry-run
359
+ ```
360
+
223
361
  ## MCP server
224
362
 
225
363
  docpull ships an MCP (Model Context Protocol) server so AI agents can call it
@@ -271,21 +409,27 @@ pip install 'docpull[mcp]'
271
409
 
272
410
  See [plugin/README.md](plugin/README.md) for details.
273
411
 
274
- Tools exposed (8 total — read tools advertise `readOnlyHint` so hosts that auto-approve safe tools won't prompt):
412
+ Tools exposed (12 total — read tools advertise `readOnlyHint` so hosts that auto-approve safe tools won't prompt):
275
413
 
276
414
  Read:
277
415
  - `fetch_url(url, max_tokens?)` — one-shot fetch, no crawl. HTTPS-only, SSRF-validated.
278
- - `list_sources(category?)` — show available aliases (react, nextjs, fastapi, …)
416
+ - `list_sources(category?)` — show available aliases (react, nextjs, parallel, fastapi, …)
279
417
  - `list_indexed()` — what has been fetched locally, with last-fetched age
280
418
  - `grep_docs(pattern, library?, limit?, context?)` — regex search across fetched Markdown (length-capped + wall-clock budgeted to mitigate ReDoS)
281
419
  - `read_doc(library, path, line_start?, line_end?)` — read a specific cached file, optionally line-sliced
420
+ - `pack_score(pack_dir, required_domains?)` — score a local context pack for agent readiness.
421
+ - `pack_diff(old_pack_dir, new_pack_dir)` — compare pack URL sets and content hashes.
282
422
 
283
423
  Write:
284
424
  - `ensure_docs(source, force?, profile?)` — fetch a named library (cached 7 days). Forwards progress to clients that supply a `progressToken`.
425
+ - `parallel_context_pack(...)` — build or dry-run a Parallel Search + Extract pack without shelling out.
426
+ - `parallel_api_pack(source, kind?, output_dir?)` — build a pack from `llms.txt` or OpenAPI.
285
427
  - `add_source(name, url, description?, category?, max_pages?, force?)` — register a user alias (HTTPS-only, atomic write to `sources.yaml`).
286
428
  - `remove_source(name, delete_cache?)` — drop a user alias and (optionally) its cached docs.
287
429
 
288
- All tools that carry data also return `structuredContent` validated against an `outputSchema` for clients that prefer typed output.
430
+ All schema-backed tools return `structuredContent` validated against an
431
+ `outputSchema` for clients that prefer typed output. `fetch_url` intentionally
432
+ returns Markdown text directly.
289
433
 
290
434
  User-defined sources live in `~/.config/docpull-mcp/sources.yaml`:
291
435
 
@@ -298,16 +442,17 @@ sources:
298
442
  maxPages: 200
299
443
  ```
300
444
 
301
- ### About the `mcp/` directory in this repo
445
+ ### Supported MCP path
302
446
 
303
- The `mcp/` directory at the repo root is a separate TypeScript + Bun MCP
304
- server backed by PostgreSQL with pgvector for semantic search. It is not
305
- the Python MCP server shipped in the `docpull` package described above
306
- — that one is the right choice for almost every user and is installed
307
- with `pip install 'docpull[mcp]'`. The `mcp/` tree is mirrored to its
308
- own repo at [`raintree-technology/docpull-mcp`](https://github.com/raintree-technology/docpull-mcp);
309
- unless you specifically need pgvector-backed semantic search, ignore it
310
- and use `docpull mcp`.
447
+ The supported MCP server is the Python stdio server started by `docpull mcp`.
448
+ That is the only MCP path covered by the `docpull` package release contract and
449
+ the one agents, plugin users, Claude Code, Cursor, and Claude Desktop should
450
+ use.
451
+
452
+ This repository also contains an `mcp/` directory with an internal TypeScript +
453
+ Bun lab for PostgreSQL/pgvector semantic search. It is not shipped by the Python
454
+ package, is not documented as a user install path, and should be ignored unless
455
+ you are explicitly developing that lab.
311
456
 
312
457
  ## Output
313
458
 
@@ -327,9 +472,14 @@ source_type: "nextjs"
327
472
  NDJSON (one record per page or chunk):
328
473
 
329
474
  ```json
330
- {"url": "...", "title": "...", "content": "...", "hash": "...", "token_count": 842, "chunk_index": 0}
475
+ {"document_id": "doc_...", "chunk_id": "chunk_...", "url": "...", "title": "...", "content": "...", "hash": "...", "token_count": 842, "chunk_index": 0}
331
476
  ```
332
477
 
478
+ Every output format also writes `corpus.manifest.json` next to the generated
479
+ documents. The manifest records the run identity, output format, stable
480
+ `document_id` / `chunk_id` values, content hashes, relative output paths, and
481
+ chunk counts so regenerated corpora can be diffed and cited by agents.
482
+
333
483
  ## Security
334
484
 
335
485
  - HTTPS-only, mandatory robots.txt compliance
@@ -349,7 +499,7 @@ Run `docpull --help` for the full list. Highlights:
349
499
 
350
500
  ```
351
501
  Core:
352
- --profile {rag,mirror,quick,llm,custom}
502
+ --profile {rag,mirror,quick,llm}
353
503
  --single Fetch one URL (no crawl)
354
504
  --format {markdown,json,ndjson,sqlite}
355
505
  --stream Stream NDJSON to stdout
@@ -368,27 +518,34 @@ Cache:
368
518
  --cache Enable incremental updates
369
519
  --cache-dir DIR
370
520
  --cache-ttl DAYS
521
+
522
+ Crawl:
523
+ --max-concurrent N Global request concurrency
524
+ --per-host-concurrent N Per-host request concurrency
371
525
  ```
372
526
 
373
527
  ## Performance
374
528
 
375
529
  End-to-end numbers from `tests/benchmarks/test_10k_pages.py` against a
376
530
  synthetic 10,000-page localhost site (RAG profile, `max_concurrent=50`,
377
- HTTP keep-alive, 5% injected duplicate content):
531
+ `per_host_concurrent=50`, HTTP keep-alive, 5% injected duplicate content).
532
+ The benchmark emits progress every 1,000 pages plus a final JSON report for
533
+ trend tooling. Wall time varies by machine; the value below is the latest
534
+ local audit run.
378
535
 
379
536
  | Metric | Value |
380
537
  |---|---|
381
- | Total wall time | ~27 s |
382
- | Discovery (sitemap parse) | ~80 ms |
383
- | Fetch + convert + save | ~27 s |
384
- | Per-page latency p50 / p95 / p99 | ~2.6 / 4.6 / 5.3 ms |
385
- | Peak RSS delta from baseline | ~28 MB |
386
- | Cache manifest size on disk | ~3.4 MB |
538
+ | Total wall time | ~309 s |
539
+ | Pages fetched / skipped / failed | 9,501 / 499 / 0 |
540
+ | Time to first saved page | ~119 ms |
541
+ | Per-page latency p50 / p95 / p99 | ~0 / 168 / 271 ms |
542
+ | Peak RSS delta from baseline | ~93 MB |
543
+ | Cache manifest size on disk | ~8.9 MB |
387
544
  | Duplicates detected (5% injected) | 499 / 500 |
388
545
 
389
546
  Reproduce with `make benchmark` (requires `aiohttp`; runs the gated
390
- benchmark in `tests/benchmarks/` and prints a JSON line you can pipe
391
- into trend tooling).
547
+ benchmark in `tests/benchmarks/` and prints progress plus a JSON line you can
548
+ pipe into trend tooling).
392
549
 
393
550
  ## Troubleshooting
394
551