docusense-mcp 0.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.

Potentially problematic release.


This version of docusense-mcp might be problematic. Click here for more details.

Files changed (49) hide show
  1. docusense_mcp-0.1.0/LICENSE +21 -0
  2. docusense_mcp-0.1.0/PKG-INFO +143 -0
  3. docusense_mcp-0.1.0/README.md +103 -0
  4. docusense_mcp-0.1.0/pyproject.toml +82 -0
  5. docusense_mcp-0.1.0/pyproject.toml.orig +68 -0
  6. docusense_mcp-0.1.0/src/docusense/__init__.py +1 -0
  7. docusense_mcp-0.1.0/src/docusense/api.py +107 -0
  8. docusense_mcp-0.1.0/src/docusense/cli.py +430 -0
  9. docusense_mcp-0.1.0/src/docusense/config.py +80 -0
  10. docusense_mcp-0.1.0/src/docusense/context/__init__.py +0 -0
  11. docusense_mcp-0.1.0/src/docusense/context/assemble.py +105 -0
  12. docusense_mcp-0.1.0/src/docusense/db/__init__.py +0 -0
  13. docusense_mcp-0.1.0/src/docusense/db/conn.py +276 -0
  14. docusense_mcp-0.1.0/src/docusense/db/schema.sql +160 -0
  15. docusense_mcp-0.1.0/src/docusense/db/schema_sqlite.sql +145 -0
  16. docusense_mcp-0.1.0/src/docusense/db/sql.py +9 -0
  17. docusense_mcp-0.1.0/src/docusense/extract/__init__.py +0 -0
  18. docusense_mcp-0.1.0/src/docusense/extract/claims.py +223 -0
  19. docusense_mcp-0.1.0/src/docusense/extract/definitions.py +70 -0
  20. docusense_mcp-0.1.0/src/docusense/extract/llm_fallback.py +64 -0
  21. docusense_mcp-0.1.0/src/docusense/extract/references.py +126 -0
  22. docusense_mcp-0.1.0/src/docusense/extract/run.py +222 -0
  23. docusense_mcp-0.1.0/src/docusense/extract/sentences.py +53 -0
  24. docusense_mcp-0.1.0/src/docusense/index/__init__.py +0 -0
  25. docusense_mcp-0.1.0/src/docusense/index/bm25.py +56 -0
  26. docusense_mcp-0.1.0/src/docusense/index/embed.py +163 -0
  27. docusense_mcp-0.1.0/src/docusense/ingest/__init__.py +0 -0
  28. docusense_mcp-0.1.0/src/docusense/ingest/add.py +100 -0
  29. docusense_mcp-0.1.0/src/docusense/ingest/convert.py +309 -0
  30. docusense_mcp-0.1.0/src/docusense/ingest/md_parser.py +56 -0
  31. docusense_mcp-0.1.0/src/docusense/ingest/model.py +65 -0
  32. docusense_mcp-0.1.0/src/docusense/ingest/normalize.py +18 -0
  33. docusense_mcp-0.1.0/src/docusense/ingest/pdf_parser.py +344 -0
  34. docusense_mcp-0.1.0/src/docusense/ingest/pipeline.py +138 -0
  35. docusense_mcp-0.1.0/src/docusense/ingest/render.py +42 -0
  36. docusense_mcp-0.1.0/src/docusense/ingest/sections.py +98 -0
  37. docusense_mcp-0.1.0/src/docusense/jobs.py +115 -0
  38. docusense_mcp-0.1.0/src/docusense/llm.py +134 -0
  39. docusense_mcp-0.1.0/src/docusense/mcp_server.py +358 -0
  40. docusense_mcp-0.1.0/src/docusense/provenance.py +111 -0
  41. docusense_mcp-0.1.0/src/docusense/py.typed +0 -0
  42. docusense_mcp-0.1.0/src/docusense/savings.py +87 -0
  43. docusense_mcp-0.1.0/src/docusense/services.py +575 -0
  44. docusense_mcp-0.1.0/src/docusense/store/__init__.py +0 -0
  45. docusense_mcp-0.1.0/src/docusense/store/blobs.py +36 -0
  46. docusense_mcp-0.1.0/src/docusense/versions/__init__.py +0 -0
  47. docusense_mcp-0.1.0/src/docusense/versions/blame.py +68 -0
  48. docusense_mcp-0.1.0/src/docusense/versions/conflicts.py +165 -0
  49. docusense_mcp-0.1.0/src/docusense/versions/diff.py +194 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Harsh Kumar Sharma
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,143 @@
1
+ Metadata-Version: 2.4
2
+ Name: docusense-mcp
3
+ Version: 0.1.0
4
+ Summary: MCP server that lets AI agents read your PDFs and docs in sections, not wholesale: ~20x fewer tokens, hash-verified citations, version diffs
5
+ Keywords: mcp,model-context-protocol,claude,rag,pdf,documents,citations,llm,tokens,agents
6
+ Author: hksharma2004
7
+ Author-email: hksharma2004 <hksharma2004@users.noreply.github.com>
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Programming Language :: Python :: 3.13
15
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
16
+ Classifier: Topic :: Text Processing :: Indexing
17
+ Requires-Dist: pymupdf>=1.24
18
+ Requires-Dist: markdown-it-py>=3.0
19
+ Requires-Dist: pydantic-settings>=2.4
20
+ Requires-Dist: pyyaml>=6.0
21
+ Requires-Dist: typer>=0.12
22
+ Requires-Dist: httpx>=0.27
23
+ Requires-Dist: rich>=13.7
24
+ Requires-Dist: mcp>=1.10
25
+ Requires-Dist: bm25s>=0.2
26
+ Requires-Dist: pystemmer>=2.2
27
+ Requires-Dist: fastembed>=0.4
28
+ Requires-Dist: numpy>=1.26
29
+ Requires-Dist: fastapi>=0.115
30
+ Requires-Dist: uvicorn>=0.30
31
+ Requires-Dist: anthropic>=1.0 ; extra == 'anthropic'
32
+ Requires-Dist: psycopg[binary,pool]>=3.2 ; extra == 'postgres'
33
+ Requires-Dist: pgvector>=0.3 ; extra == 'postgres'
34
+ Requires-Python: >=3.12
35
+ Project-URL: Homepage, https://github.com/hksharma2004/docu-sense
36
+ Project-URL: Issues, https://github.com/hksharma2004/docu-sense/issues
37
+ Provides-Extra: anthropic
38
+ Provides-Extra: postgres
39
+ Description-Content-Type: text/markdown
40
+
41
+ # DocuSense
42
+
43
+ **Stop pasting whole documents into your agent.** DocuSense is a local MCP server that lets Claude, Cursor
44
+ and other agents read your PDFs, Word files and docs *by section and by claim* — so they load the 300
45
+ tokens that answer the question, not the 50,000-token file. Every passage comes back with a citation
46
+ to its exact bytes and a SHA-256 you can verify.
47
+
48
+ ```
49
+ pasting the docs ████████████████████████████████████████ 160,179 tokens
50
+ vector RAG (top-5) ████ 15,765 tokens
51
+ DocuSense ██ 6,869 tokens → 23x less than pasting, 2.3x less than RAG
52
+ ```
53
+ <sub>Three real tasks over RFC specs and a policy file; measured by [`scripts/token_demo.py`](scripts/token_demo.py) on the actual tool outputs.</sub>
54
+
55
+ ## Why not just RAG?
56
+
57
+ Chunk-and-embed retrieval answers "find text like this". Most document questions are not that:
58
+
59
+ | Question | Paste / file read | Vector RAG | DocuSense |
60
+ |---|---|---|---|
61
+ | *"What's the max PKCE verifier length? Cite it."* | 9,090 tok | 5,362 tok of nearby chunks | **478 tok**: the one paragraph + verified citation |
62
+ | *"What changed between v1 and v2 of the spec?"* | 97,895 tok (both versions) | 5,292 tok, can't actually diff | **2,776 tok**: claim-level diff (`diff_versions`) |
63
+ | *"Does our policy conflict with the standard?"* | 53,194 tok | 5,111 tok, hopes the right chunks surface | **3,615 tok**: `find_contradictions`, both sides cited |
64
+
65
+ What makes the difference:
66
+
67
+ - **Structure-aware.** Documents are parsed into a section tree, paragraphs, tables, definitions,
68
+ cross-references and normative claims (*MUST / MUST NOT / numeric limits*). Agents navigate like a
69
+ person with a table of contents instead of re-reading everything.
70
+ - **Budgeted responses.** Every tool takes `max_tokens`; outputs are compact JSON with no padding.
71
+ - **Versions are first-class.** Re-adding a changed file makes `v2`. Ask what changed, who changed it
72
+ (`blame`), or which other documents are affected — without loading either version.
73
+ - **Verifiable.** Each passage is `ds://doc@version#§section:b<start>-<end>` + SHA-256. `get_evidence`
74
+ re-hashes against the stored bytes, so a hallucinated quote can't pass as a citation.
75
+ - **Local and free.** Embedded SQLite + local embeddings. No API key, no server, nothing leaves your machine.
76
+ - **Proves its own value.** `docusense savings` shows how many tokens your agents actually saved.
77
+
78
+ ## Get started (2 minutes)
79
+
80
+ ```bash
81
+ uv tool install docusense-mcp # or: pipx install docusense-mcp
82
+ docusense setup # registers with Claude Code (also: claude-desktop | cursor | print)
83
+ docusense add ~/work/specs handbook.pdf # PDF, .docx, HTML, Markdown, .txt — folders scanned recursively
84
+ ```
85
+
86
+ Then ask your agent about your documents. Later, see what it saved (example output):
87
+
88
+ ```console
89
+ $ docusense savings
90
+ 12 agent sessions · 87 tool calls
91
+ read via DocuSense: 41,302 tokens
92
+ pasting the docs: 912,440 tokens
93
+ saved: 871,138 tokens (22.1x less context)
94
+ ```
95
+
96
+ The first indexing run downloads the local embedding model (`bge-small-en-v1.5`, ~130 MB) once.
97
+ Claude Desktop users can also install the one-click `docusense.mcpb` bundle from the
98
+ [releases page](https://github.com/hksharma2004/docu-sense/releases).
99
+
100
+ <!-- mcp-name: io.github.hksharma2004/docusense -->
101
+
102
+
103
+ Agents can add files themselves with the `add_document` tool (restricted to `DOCUSENSE_ADD_ROOTS`,
104
+ default: your home folder).
105
+
106
+ ## Good fits
107
+
108
+ - **Engineers** working against long specs, RFCs, API references, design docs and ADRs in Claude Code / Cursor.
109
+ - **Compliance and policy work**: checking internal policies against standards, tracking what changed between revisions.
110
+ - **Contracts, handbooks, research papers**: precise quotes with page numbers instead of paraphrase.
111
+ - **Any long-running agent** whose context fills up from repeatedly reading the same large files.
112
+
113
+ Less useful for: a handful of short files that fit comfortably in context anyway. The tool list costs
114
+ ~3.2k tokens per session (~2.3k with `DOCUSENSE_TOOLS=core`), which one avoided document read repays.
115
+
116
+ ## Tools
117
+
118
+ | Purpose | Tools |
119
+ |---|---|
120
+ | Orient | `list_documents`, `get_section_map`, `read_section` |
121
+ | Find | `search` (hybrid BM25 + embeddings), `search_claims`, `lookup_definition`, `resolve_reference` |
122
+ | Verify | `get_evidence`, `verify_span`, `assemble_context` (packs evidence into a token budget) |
123
+ | Change over time | `list_versions`, `diff_versions`, `blame`, `find_affected_documents`, `find_contradictions` |
124
+ | Ingest | `add_document`, `ingest_status` |
125
+
126
+ Plus resources (`docusense://documents`, canonical text per version) and prompts (`answer_with_evidence`,
127
+ `what_changed`, `audit_contradictions`). `DOCUSENSE_TOOLS=core` exposes 11 tools for tight budgets.
128
+
129
+ ## Accuracy
130
+
131
+ On a 14-question agent benchmark over the RFC corpus (multi-hop, structural and temporal questions),
132
+ an agent using DocuSense answered 100% correctly with 93% citation hit-rate
133
+ ([`bench/`](bench/), `nvidia/nemotron-3-ultra` via OpenRouter). Retrieval-only eval on non-RFC PDFs
134
+ (IRS Pub 463, the Transformer paper): hybrid hit@5 0.85, MRR 0.65 ([`scripts/eval_retrieval.py`](scripts/eval_retrieval.py)).
135
+
136
+ ## More
137
+
138
+ - Other ways to run it: `docusense serve-mcp --http` (Streamable HTTP, several agents sharing one store;
139
+ non-loopback requires `DOCUSENSE_HTTP_TOKEN`), Postgres + pgvector backend, CLI search.
140
+ - Configuration, development, provenance model and releasing: [docs/DEVELOPMENT.md](docs/DEVELOPMENT.md).
141
+ - Inspect the tools: `npx @modelcontextprotocol/inspector docusense serve-mcp`.
142
+
143
+ MIT licensed. Originally built as a B.Tech IT project (USICT, GGSIPU); see [PLAN.md](PLAN.md).
@@ -0,0 +1,103 @@
1
+ # DocuSense
2
+
3
+ **Stop pasting whole documents into your agent.** DocuSense is a local MCP server that lets Claude, Cursor
4
+ and other agents read your PDFs, Word files and docs *by section and by claim* — so they load the 300
5
+ tokens that answer the question, not the 50,000-token file. Every passage comes back with a citation
6
+ to its exact bytes and a SHA-256 you can verify.
7
+
8
+ ```
9
+ pasting the docs ████████████████████████████████████████ 160,179 tokens
10
+ vector RAG (top-5) ████ 15,765 tokens
11
+ DocuSense ██ 6,869 tokens → 23x less than pasting, 2.3x less than RAG
12
+ ```
13
+ <sub>Three real tasks over RFC specs and a policy file; measured by [`scripts/token_demo.py`](scripts/token_demo.py) on the actual tool outputs.</sub>
14
+
15
+ ## Why not just RAG?
16
+
17
+ Chunk-and-embed retrieval answers "find text like this". Most document questions are not that:
18
+
19
+ | Question | Paste / file read | Vector RAG | DocuSense |
20
+ |---|---|---|---|
21
+ | *"What's the max PKCE verifier length? Cite it."* | 9,090 tok | 5,362 tok of nearby chunks | **478 tok**: the one paragraph + verified citation |
22
+ | *"What changed between v1 and v2 of the spec?"* | 97,895 tok (both versions) | 5,292 tok, can't actually diff | **2,776 tok**: claim-level diff (`diff_versions`) |
23
+ | *"Does our policy conflict with the standard?"* | 53,194 tok | 5,111 tok, hopes the right chunks surface | **3,615 tok**: `find_contradictions`, both sides cited |
24
+
25
+ What makes the difference:
26
+
27
+ - **Structure-aware.** Documents are parsed into a section tree, paragraphs, tables, definitions,
28
+ cross-references and normative claims (*MUST / MUST NOT / numeric limits*). Agents navigate like a
29
+ person with a table of contents instead of re-reading everything.
30
+ - **Budgeted responses.** Every tool takes `max_tokens`; outputs are compact JSON with no padding.
31
+ - **Versions are first-class.** Re-adding a changed file makes `v2`. Ask what changed, who changed it
32
+ (`blame`), or which other documents are affected — without loading either version.
33
+ - **Verifiable.** Each passage is `ds://doc@version#§section:b<start>-<end>` + SHA-256. `get_evidence`
34
+ re-hashes against the stored bytes, so a hallucinated quote can't pass as a citation.
35
+ - **Local and free.** Embedded SQLite + local embeddings. No API key, no server, nothing leaves your machine.
36
+ - **Proves its own value.** `docusense savings` shows how many tokens your agents actually saved.
37
+
38
+ ## Get started (2 minutes)
39
+
40
+ ```bash
41
+ uv tool install docusense-mcp # or: pipx install docusense-mcp
42
+ docusense setup # registers with Claude Code (also: claude-desktop | cursor | print)
43
+ docusense add ~/work/specs handbook.pdf # PDF, .docx, HTML, Markdown, .txt — folders scanned recursively
44
+ ```
45
+
46
+ Then ask your agent about your documents. Later, see what it saved (example output):
47
+
48
+ ```console
49
+ $ docusense savings
50
+ 12 agent sessions · 87 tool calls
51
+ read via DocuSense: 41,302 tokens
52
+ pasting the docs: 912,440 tokens
53
+ saved: 871,138 tokens (22.1x less context)
54
+ ```
55
+
56
+ The first indexing run downloads the local embedding model (`bge-small-en-v1.5`, ~130 MB) once.
57
+ Claude Desktop users can also install the one-click `docusense.mcpb` bundle from the
58
+ [releases page](https://github.com/hksharma2004/docu-sense/releases).
59
+
60
+ <!-- mcp-name: io.github.hksharma2004/docusense -->
61
+
62
+
63
+ Agents can add files themselves with the `add_document` tool (restricted to `DOCUSENSE_ADD_ROOTS`,
64
+ default: your home folder).
65
+
66
+ ## Good fits
67
+
68
+ - **Engineers** working against long specs, RFCs, API references, design docs and ADRs in Claude Code / Cursor.
69
+ - **Compliance and policy work**: checking internal policies against standards, tracking what changed between revisions.
70
+ - **Contracts, handbooks, research papers**: precise quotes with page numbers instead of paraphrase.
71
+ - **Any long-running agent** whose context fills up from repeatedly reading the same large files.
72
+
73
+ Less useful for: a handful of short files that fit comfortably in context anyway. The tool list costs
74
+ ~3.2k tokens per session (~2.3k with `DOCUSENSE_TOOLS=core`), which one avoided document read repays.
75
+
76
+ ## Tools
77
+
78
+ | Purpose | Tools |
79
+ |---|---|
80
+ | Orient | `list_documents`, `get_section_map`, `read_section` |
81
+ | Find | `search` (hybrid BM25 + embeddings), `search_claims`, `lookup_definition`, `resolve_reference` |
82
+ | Verify | `get_evidence`, `verify_span`, `assemble_context` (packs evidence into a token budget) |
83
+ | Change over time | `list_versions`, `diff_versions`, `blame`, `find_affected_documents`, `find_contradictions` |
84
+ | Ingest | `add_document`, `ingest_status` |
85
+
86
+ Plus resources (`docusense://documents`, canonical text per version) and prompts (`answer_with_evidence`,
87
+ `what_changed`, `audit_contradictions`). `DOCUSENSE_TOOLS=core` exposes 11 tools for tight budgets.
88
+
89
+ ## Accuracy
90
+
91
+ On a 14-question agent benchmark over the RFC corpus (multi-hop, structural and temporal questions),
92
+ an agent using DocuSense answered 100% correctly with 93% citation hit-rate
93
+ ([`bench/`](bench/), `nvidia/nemotron-3-ultra` via OpenRouter). Retrieval-only eval on non-RFC PDFs
94
+ (IRS Pub 463, the Transformer paper): hybrid hit@5 0.85, MRR 0.65 ([`scripts/eval_retrieval.py`](scripts/eval_retrieval.py)).
95
+
96
+ ## More
97
+
98
+ - Other ways to run it: `docusense serve-mcp --http` (Streamable HTTP, several agents sharing one store;
99
+ non-loopback requires `DOCUSENSE_HTTP_TOKEN`), Postgres + pgvector backend, CLI search.
100
+ - Configuration, development, provenance model and releasing: [docs/DEVELOPMENT.md](docs/DEVELOPMENT.md).
101
+ - Inspect the tools: `npx @modelcontextprotocol/inspector docusense serve-mcp`.
102
+
103
+ MIT licensed. Originally built as a B.Tech IT project (USICT, GGSIPU); see [PLAN.md](PLAN.md).
@@ -0,0 +1,82 @@
1
+ [project]
2
+ name = "docusense-mcp"
3
+ version = "0.1.0"
4
+ description = "MCP server that lets AI agents read your PDFs and docs in sections, not wholesale: ~20x fewer tokens, hash-verified citations, version diffs"
5
+ readme = "README.md"
6
+ license = "MIT"
7
+ license-files = ["LICENSE"]
8
+ requires-python = ">=3.12"
9
+ keywords = [
10
+ "mcp",
11
+ "model-context-protocol",
12
+ "claude",
13
+ "rag",
14
+ "pdf",
15
+ "documents",
16
+ "citations",
17
+ "llm",
18
+ "tokens",
19
+ "agents",
20
+ ]
21
+ classifiers = [
22
+ "Development Status :: 4 - Beta",
23
+ "Intended Audience :: Developers",
24
+ "License :: OSI Approved :: MIT License",
25
+ "Programming Language :: Python :: 3.12",
26
+ "Programming Language :: Python :: 3.13",
27
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
28
+ "Topic :: Text Processing :: Indexing",
29
+ ]
30
+ dependencies = [
31
+ "pymupdf>=1.24",
32
+ "markdown-it-py>=3.0",
33
+ "pydantic-settings>=2.4",
34
+ "pyyaml>=6.0",
35
+ "typer>=0.12",
36
+ "httpx>=0.27",
37
+ "rich>=13.7",
38
+ "mcp>=1.10",
39
+ "bm25s>=0.2",
40
+ "pystemmer>=2.2",
41
+ "fastembed>=0.4",
42
+ "numpy>=1.26",
43
+ "fastapi>=0.115",
44
+ "uvicorn>=0.30",
45
+ ]
46
+
47
+ [[project.authors]]
48
+ name = "hksharma2004"
49
+ email = "hksharma2004@users.noreply.github.com"
50
+
51
+ [project.urls]
52
+ Homepage = "https://github.com/hksharma2004/docu-sense"
53
+ Issues = "https://github.com/hksharma2004/docu-sense/issues"
54
+
55
+ [project.optional-dependencies]
56
+ postgres = [
57
+ "psycopg[binary,pool]>=3.2",
58
+ "pgvector>=0.3",
59
+ ]
60
+ anthropic = ["anthropic>=1.0"]
61
+
62
+ [project.scripts]
63
+ docusense = "docusense.cli:main"
64
+ docusense-mcp = "docusense.cli:main"
65
+
66
+ [dependency-groups]
67
+ dev = [
68
+ "pytest>=8.0",
69
+ "psycopg[binary,pool]>=3.2",
70
+ "pgvector>=0.3",
71
+ "anthropic>=1.0",
72
+ ]
73
+
74
+ [tool.uv.build-backend]
75
+ module-name = "docusense"
76
+
77
+ [tool.pytest.ini_options]
78
+ testpaths = ["tests"]
79
+
80
+ [build-system]
81
+ requires = ["uv_build>=0.11.8,<0.12.0"]
82
+ build-backend = "uv_build"
@@ -0,0 +1,68 @@
1
+ [project]
2
+ name = "docusense-mcp"
3
+ version = "0.1.0"
4
+ description = "MCP server that lets AI agents read your PDFs and docs in sections, not wholesale: ~20x fewer tokens, hash-verified citations, version diffs"
5
+ authors = [
6
+ { name = "hksharma2004", email = "hksharma2004@users.noreply.github.com" }
7
+ ]
8
+ readme = "README.md"
9
+ license = "MIT"
10
+ license-files = ["LICENSE"]
11
+ requires-python = ">=3.12"
12
+ keywords = ["mcp", "model-context-protocol", "claude", "rag", "pdf", "documents", "citations", "llm", "tokens", "agents"]
13
+ classifiers = [
14
+ "Development Status :: 4 - Beta",
15
+ "Intended Audience :: Developers",
16
+ "License :: OSI Approved :: MIT License",
17
+ "Programming Language :: Python :: 3.12",
18
+ "Programming Language :: Python :: 3.13",
19
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
20
+ "Topic :: Text Processing :: Indexing",
21
+ ]
22
+ dependencies = [
23
+ "pymupdf>=1.24",
24
+ "markdown-it-py>=3.0",
25
+ "pydantic-settings>=2.4",
26
+ "pyyaml>=6.0",
27
+ "typer>=0.12",
28
+ "httpx>=0.27",
29
+ "rich>=13.7",
30
+ "mcp>=1.10",
31
+ "bm25s>=0.2",
32
+ "pystemmer>=2.2",
33
+ "fastembed>=0.4",
34
+ "numpy>=1.26",
35
+ "fastapi>=0.115",
36
+ "uvicorn>=0.30",
37
+ ]
38
+
39
+ [project.urls]
40
+ Homepage = "https://github.com/hksharma2004/docu-sense"
41
+ Issues = "https://github.com/hksharma2004/docu-sense/issues"
42
+
43
+ [project.optional-dependencies]
44
+ # Postgres + pgvector backend (team / hosted). The default backend is embedded SQLite.
45
+ postgres = [
46
+ "psycopg[binary,pool]>=3.2",
47
+ "pgvector>=0.3",
48
+ ]
49
+
50
+ # Anthropic (Claude) as the optional LLM provider for extraction fallback / contradiction judging.
51
+ anthropic = ["anthropic>=1.0"]
52
+
53
+ [project.scripts]
54
+ docusense = "docusense.cli:main"
55
+ docusense-mcp = "docusense.cli:main"
56
+
57
+ [dependency-groups]
58
+ dev = ["pytest>=8.0", "psycopg[binary,pool]>=3.2", "pgvector>=0.3", "anthropic>=1.0"]
59
+
60
+ [tool.uv.build-backend]
61
+ module-name = "docusense"
62
+
63
+ [build-system]
64
+ requires = ["uv_build>=0.11.8,<0.12.0"]
65
+ build-backend = "uv_build"
66
+
67
+ [tool.pytest.ini_options]
68
+ testpaths = ["tests"]
@@ -0,0 +1 @@
1
+ """DocuSense: a verifiable and version-native document layer for AI agents."""
@@ -0,0 +1,107 @@
1
+ """REST API for the viewer. Thin wrapper over services.DocuSense.
2
+
3
+ Run: uv run docusense serve-api (http://localhost:8000, docs at /docs)
4
+ """
5
+
6
+ from contextlib import asynccontextmanager
7
+
8
+ from fastapi import FastAPI, HTTPException, Query, Request
9
+ from fastapi.middleware.cors import CORSMiddleware
10
+ from fastapi.responses import JSONResponse
11
+
12
+ from docusense.db.conn import ConnectionPool
13
+ from docusense.services import DocuSense, NotFound
14
+
15
+ pool: ConnectionPool | None = None
16
+
17
+
18
+ @asynccontextmanager
19
+ async def lifespan(app: FastAPI):
20
+ global pool
21
+ pool = ConnectionPool()
22
+ yield
23
+ pool.close()
24
+
25
+
26
+ app = FastAPI(title="DocuSense API", version="0.1.0", lifespan=lifespan)
27
+ app.add_middleware(CORSMiddleware, allow_origins=["http://localhost:3000"], allow_methods=["GET"], allow_headers=["*"])
28
+
29
+
30
+ @app.exception_handler(NotFound)
31
+ async def not_found(_: Request, exc: NotFound):
32
+ return JSONResponse(status_code=404, content={"detail": str(exc)})
33
+
34
+
35
+ @app.exception_handler(ValueError)
36
+ async def bad_request(_: Request, exc: ValueError):
37
+ return JSONResponse(status_code=400, content={"detail": str(exc)})
38
+
39
+
40
+ def run(fn, *args, **kwargs):
41
+ with pool.connection() as conn:
42
+ return fn(DocuSense(conn), *args, **kwargs)
43
+
44
+
45
+ # Sync endpoints run in FastAPI's thread pool, so blocking DB calls don't stall the event loop.
46
+
47
+ @app.get("/api/health")
48
+ def health():
49
+ return run(lambda ds: {"ok": True, "versions": ds.conn.execute("SELECT count(*) AS n FROM versions").fetchone()["n"]})
50
+
51
+
52
+ @app.get("/api/documents")
53
+ def documents():
54
+ return run(lambda ds: ds.list_documents())
55
+
56
+
57
+ @app.get("/api/documents/{doc}/versions")
58
+ def versions(doc: str):
59
+ return run(lambda ds: ds.list_versions(doc))
60
+
61
+
62
+ @app.get("/api/documents/{doc}/affected")
63
+ def affected(doc: str):
64
+ return run(lambda ds: ds.find_affected_documents(doc, max_tokens=20000))
65
+
66
+
67
+ @app.get("/api/versions/{version_id}")
68
+ def version_document(version_id: str):
69
+ return run(lambda ds: ds.version_document(version_id))
70
+
71
+
72
+ @app.get("/api/spans/{span_id}")
73
+ def span(span_id: int):
74
+ return run(lambda ds: ds.get_evidence(span_id))
75
+
76
+
77
+ @app.get("/api/spans/{span_id}/verify")
78
+ def verify(span_id: int, deep: bool = False):
79
+ return run(lambda ds: ds.verify_span(span_id, deep))
80
+
81
+
82
+ @app.get("/api/spans/{span_id}/blame")
83
+ def blame(span_id: int):
84
+ return run(lambda ds: ds.blame(span_id))
85
+
86
+
87
+ @app.get("/api/spans/{span_id}/references")
88
+ def references(span_id: int):
89
+ return run(lambda ds: ds.resolve_reference(span_id=span_id, max_tokens=20000))
90
+
91
+
92
+ @app.get("/api/diff")
93
+ def diff(doc: str, from_version: str | None = Query(None, alias="from"),
94
+ to_version: str | None = Query(None, alias="to"), level: str = "section"):
95
+ return run(lambda ds: ds.diff_versions(doc, from_version, to_version, level, max_tokens=200000))
96
+
97
+
98
+ @app.get("/api/search")
99
+ def search(q: str, mode: str = "hybrid", doc: str | None = None, k: int = 10):
100
+ if not q.strip():
101
+ raise HTTPException(400, "query is empty")
102
+ return run(lambda ds: ds.search(q, mode, doc, k, max_tokens=20000))
103
+
104
+
105
+ @app.get("/api/contradictions")
106
+ def contradictions(doc: str | None = None, history: bool = False, rejected: bool = False, k: int = 50):
107
+ return run(lambda ds: ds.find_contradictions(doc, history, rejected, k, max_tokens=200000))