doc-index-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.
- doc_index_mcp-0.1.0/.github/workflows/publish.yml +47 -0
- doc_index_mcp-0.1.0/.github/workflows/test.yml +23 -0
- doc_index_mcp-0.1.0/.gitignore +54 -0
- doc_index_mcp-0.1.0/LICENSE +21 -0
- doc_index_mcp-0.1.0/PKG-INFO +264 -0
- doc_index_mcp-0.1.0/README.md +218 -0
- doc_index_mcp-0.1.0/demo/.mcp-local.json +11 -0
- doc_index_mcp-0.1.0/demo/.mcp.json +11 -0
- doc_index_mcp-0.1.0/pyproject.toml +51 -0
- doc_index_mcp-0.1.0/requirements.txt +19 -0
- doc_index_mcp-0.1.0/src/__init__.py +20 -0
- doc_index_mcp-0.1.0/src/install_skill.py +34 -0
- doc_index_mcp-0.1.0/src/server.py +831 -0
- doc_index_mcp-0.1.0/src/services/__init__.py +9 -0
- doc_index_mcp-0.1.0/src/services/boundary_detector.py +482 -0
- doc_index_mcp-0.1.0/src/services/chunker.py +619 -0
- doc_index_mcp-0.1.0/src/services/document_loader.py +572 -0
- doc_index_mcp-0.1.0/src/services/embedder.py +191 -0
- doc_index_mcp-0.1.0/src/services/table_extractor.py +484 -0
- doc_index_mcp-0.1.0/src/services/text_search.py +160 -0
- doc_index_mcp-0.1.0/src/services/vector_store.py +290 -0
- doc_index_mcp-0.1.0/src/skills/doc-search/SKILL.md +46 -0
- doc_index_mcp-0.1.0/src/tests/__init__.py +5 -0
- doc_index_mcp-0.1.0/src/tests/test_boundary_detector.py +480 -0
- doc_index_mcp-0.1.0/src/tests/test_chunker.py +374 -0
- doc_index_mcp-0.1.0/src/tests/test_content_tool.py +178 -0
- doc_index_mcp-0.1.0/src/tests/test_document_loader.py +495 -0
- doc_index_mcp-0.1.0/src/tests/test_search_boundaries.py +461 -0
- doc_index_mcp-0.1.0/src/tests/test_table_extractor.py +360 -0
- doc_index_mcp-0.1.0/src/tests/test_text_search.py +139 -0
- doc_index_mcp-0.1.0/src/tests/test_toc_tool.py +141 -0
- doc_index_mcp-0.1.0/src/tests/test_vector_store.py +318 -0
- doc_index_mcp-0.1.0/src/tools/__init__.py +9 -0
- doc_index_mcp-0.1.0/src/tools/content_tool.py +239 -0
- doc_index_mcp-0.1.0/src/tools/search_tool.py +375 -0
- doc_index_mcp-0.1.0/src/tools/toc_tool.py +129 -0
- doc_index_mcp-0.1.0/src/validation.py +464 -0
- doc_index_mcp-0.1.0/tests/__init__.py +0 -0
- doc_index_mcp-0.1.0/tests/bench_providers.py +151 -0
- doc_index_mcp-0.1.0/tests/conftest.py +91 -0
- doc_index_mcp-0.1.0/tests/download_fixtures.py +30 -0
- doc_index_mcp-0.1.0/tests/test_economic_report.py +170 -0
- doc_index_mcp-0.1.0/tests/test_nist_800_53.py +112 -0
- doc_index_mcp-0.1.0/tests/test_nist_csf.py +116 -0
- doc_index_mcp-0.1.0/tests/test_toc_and_content.py +162 -0
- doc_index_mcp-0.1.0/uv.lock +2064 -0
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
name: Build and Publish
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
id-token: write
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
build:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
|
|
17
|
+
- name: Install uv
|
|
18
|
+
uses: astral-sh/setup-uv@v4
|
|
19
|
+
|
|
20
|
+
- name: Install dependencies
|
|
21
|
+
run: uv sync --extra dev
|
|
22
|
+
|
|
23
|
+
- name: Run tests
|
|
24
|
+
run: uv run pytest src/tests/ -v
|
|
25
|
+
|
|
26
|
+
- name: Build package
|
|
27
|
+
run: uv build
|
|
28
|
+
|
|
29
|
+
- name: Upload artifacts
|
|
30
|
+
uses: actions/upload-artifact@v4
|
|
31
|
+
with:
|
|
32
|
+
name: dist
|
|
33
|
+
path: dist/
|
|
34
|
+
|
|
35
|
+
publish:
|
|
36
|
+
needs: build
|
|
37
|
+
runs-on: ubuntu-latest
|
|
38
|
+
environment: pypi
|
|
39
|
+
steps:
|
|
40
|
+
- name: Download artifacts
|
|
41
|
+
uses: actions/download-artifact@v4
|
|
42
|
+
with:
|
|
43
|
+
name: dist
|
|
44
|
+
path: dist/
|
|
45
|
+
|
|
46
|
+
- name: Publish to PyPI
|
|
47
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
name: Tests
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
|
|
13
|
+
steps:
|
|
14
|
+
- uses: actions/checkout@v4
|
|
15
|
+
|
|
16
|
+
- name: Install uv
|
|
17
|
+
uses: astral-sh/setup-uv@v4
|
|
18
|
+
|
|
19
|
+
- name: Install dependencies
|
|
20
|
+
run: uv sync --extra dev
|
|
21
|
+
|
|
22
|
+
- name: Run tests
|
|
23
|
+
run: uv run pytest src/tests/ -v
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
*.so
|
|
6
|
+
.Python
|
|
7
|
+
build/
|
|
8
|
+
develop-eggs/
|
|
9
|
+
dist/
|
|
10
|
+
downloads/
|
|
11
|
+
eggs/
|
|
12
|
+
.eggs/
|
|
13
|
+
lib/
|
|
14
|
+
lib64/
|
|
15
|
+
parts/
|
|
16
|
+
sdist/
|
|
17
|
+
var/
|
|
18
|
+
wheels/
|
|
19
|
+
*.egg-info/
|
|
20
|
+
.installed.cfg
|
|
21
|
+
*.egg
|
|
22
|
+
|
|
23
|
+
# Virtual environments
|
|
24
|
+
.venv/
|
|
25
|
+
venv/
|
|
26
|
+
ENV/
|
|
27
|
+
env/
|
|
28
|
+
|
|
29
|
+
# IDE
|
|
30
|
+
.idea/
|
|
31
|
+
.vscode/
|
|
32
|
+
*.swp
|
|
33
|
+
*.swo
|
|
34
|
+
*~
|
|
35
|
+
|
|
36
|
+
# Doc index data (user-specific)
|
|
37
|
+
.docindex/
|
|
38
|
+
|
|
39
|
+
# Testing
|
|
40
|
+
.pytest_cache/
|
|
41
|
+
.coverage
|
|
42
|
+
htmlcov/
|
|
43
|
+
|
|
44
|
+
# Test fixture PDFs (large, downloaded at test time)
|
|
45
|
+
tests/fixtures/*.pdf
|
|
46
|
+
|
|
47
|
+
# Demo artifacts
|
|
48
|
+
demo/.venv/
|
|
49
|
+
demo/.docindex/
|
|
50
|
+
demo/.claude/
|
|
51
|
+
|
|
52
|
+
# Misc
|
|
53
|
+
.DS_Store
|
|
54
|
+
*.log
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Mike
|
|
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,264 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: doc-index-mcp
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: MCP server for semantic document search with boundary-aware chunking
|
|
5
|
+
License: MIT License
|
|
6
|
+
|
|
7
|
+
Copyright (c) 2026 Mike
|
|
8
|
+
|
|
9
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
10
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
11
|
+
in the Software without restriction, including without limitation the rights
|
|
12
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
13
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
14
|
+
furnished to do so, subject to the following conditions:
|
|
15
|
+
|
|
16
|
+
The above copyright notice and this permission notice shall be included in all
|
|
17
|
+
copies or substantial portions of the Software.
|
|
18
|
+
|
|
19
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
20
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
21
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
22
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
23
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
24
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
25
|
+
SOFTWARE.
|
|
26
|
+
License-File: LICENSE
|
|
27
|
+
Requires-Python: >=3.11
|
|
28
|
+
Requires-Dist: fastembed>=0.2.0
|
|
29
|
+
Requires-Dist: mcp>=1.0.0
|
|
30
|
+
Requires-Dist: numpy>=1.24.0
|
|
31
|
+
Requires-Dist: openpyxl>=3.1.0
|
|
32
|
+
Requires-Dist: pdfplumber>=0.10.0
|
|
33
|
+
Requires-Dist: pydantic>=2.0.0
|
|
34
|
+
Requires-Dist: python-docx>=0.8.11
|
|
35
|
+
Requires-Dist: python-pptx>=0.6.21
|
|
36
|
+
Requires-Dist: tiktoken>=0.5.0
|
|
37
|
+
Requires-Dist: usearch>=2.0.0
|
|
38
|
+
Provides-Extra: apple
|
|
39
|
+
Requires-Dist: onnxruntime>=1.17.0; extra == 'apple'
|
|
40
|
+
Provides-Extra: cuda
|
|
41
|
+
Requires-Dist: onnxruntime-gpu>=1.17.0; extra == 'cuda'
|
|
42
|
+
Provides-Extra: dev
|
|
43
|
+
Requires-Dist: pytest-asyncio>=0.21.0; extra == 'dev'
|
|
44
|
+
Requires-Dist: pytest>=7.0.0; extra == 'dev'
|
|
45
|
+
Description-Content-Type: text/markdown
|
|
46
|
+
|
|
47
|
+
# Doc Index MCP
|
|
48
|
+
|
|
49
|
+
## What is This For?
|
|
50
|
+
|
|
51
|
+
A local-first semantic search server for your documents. Index PDFs, Word docs, PowerPoints, Excel files, and text/markdown, then search them using natural language via the Model Context Protocol (MCP).
|
|
52
|
+
|
|
53
|
+
- **Semantic search** - Find relevant content using natural language queries
|
|
54
|
+
- **Boundary-aware chunking** - Respects document structure (chapters, sections, headers)
|
|
55
|
+
- **Table extraction** - Extract tables from documents as CSV
|
|
56
|
+
- **Fully local** - No external APIs, no cloud services, no PyTorch
|
|
57
|
+
- **Lightweight** - ONNX-based embeddings (~50MB vs ~2GB for PyTorch)
|
|
58
|
+
|
|
59
|
+
## Supported Formats
|
|
60
|
+
|
|
61
|
+
| Format | Extensions | Notes |
|
|
62
|
+
|--------|------------|-------|
|
|
63
|
+
| Text | `.txt` | Plain text |
|
|
64
|
+
| Markdown | `.md`, `.markdown` | Preserves headers for boundaries |
|
|
65
|
+
| PDF | `.pdf` | Text extraction with page markers |
|
|
66
|
+
| Word | `.docx` | Paragraphs, headings, tables |
|
|
67
|
+
| PowerPoint | `.pptx` | Slides, notes, tables |
|
|
68
|
+
| Excel | `.xlsx`, `.xls` | Sheets as tables |
|
|
69
|
+
|
|
70
|
+
### Why No External Services?
|
|
71
|
+
|
|
72
|
+
| Component | Traditional RAG | This Server |
|
|
73
|
+
|-----------|-----------------|-------------|
|
|
74
|
+
| Embeddings | OpenAI API / hosted model | Local ONNX model (fastembed) |
|
|
75
|
+
| Vector DB | Pinecone / Weaviate / Qdrant | Local file (usearch) |
|
|
76
|
+
| Storage | Cloud / managed DB | Local `.docindex/` directory |
|
|
77
|
+
| Dependencies | PyTorch (~2GB) | ONNX Runtime (~50MB) |
|
|
78
|
+
|
|
79
|
+
## Tools
|
|
80
|
+
|
|
81
|
+
### `doc_index`
|
|
82
|
+
Index a document for semantic search.
|
|
83
|
+
|
|
84
|
+
```json
|
|
85
|
+
{
|
|
86
|
+
"file_path": "docs/manual.pdf",
|
|
87
|
+
"source_name": "manual"
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### `doc_search`
|
|
92
|
+
Search indexed documents using natural language.
|
|
93
|
+
|
|
94
|
+
```json
|
|
95
|
+
{
|
|
96
|
+
"query": "how to configure authentication",
|
|
97
|
+
"top_k": 5,
|
|
98
|
+
"expand_to_boundary": "section",
|
|
99
|
+
"max_return_tokens": 4096
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Parameters:
|
|
104
|
+
- `query` - Search query
|
|
105
|
+
- `sources` - Filter to specific sources (optional)
|
|
106
|
+
- `top_k` - Number of results (default: 5)
|
|
107
|
+
- `expand_to_boundary` - Expand results to full "section" or "chapter"
|
|
108
|
+
- `max_return_tokens` - Token budget for results (default: 4096)
|
|
109
|
+
- `include_siblings` - Include sibling sections when expanding
|
|
110
|
+
|
|
111
|
+
### `doc_list`
|
|
112
|
+
List all indexed sources.
|
|
113
|
+
|
|
114
|
+
### `doc_chunk`
|
|
115
|
+
Retrieve a specific chunk by ID with optional neighbors.
|
|
116
|
+
|
|
117
|
+
```json
|
|
118
|
+
{
|
|
119
|
+
"chunk_id": "manual:42",
|
|
120
|
+
"neighbors": 2
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### `read_document`
|
|
125
|
+
Read a document without indexing. Returns formatted text.
|
|
126
|
+
|
|
127
|
+
```json
|
|
128
|
+
{
|
|
129
|
+
"file_path": "report.pdf",
|
|
130
|
+
"max_chars": 100000
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### `list_tables`
|
|
135
|
+
List all tables in a document.
|
|
136
|
+
|
|
137
|
+
```json
|
|
138
|
+
{
|
|
139
|
+
"file_path": "data.xlsx"
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### `extract_table`
|
|
144
|
+
Extract a specific table as CSV.
|
|
145
|
+
|
|
146
|
+
```json
|
|
147
|
+
{
|
|
148
|
+
"file_path": "data.xlsx",
|
|
149
|
+
"table_index": 0,
|
|
150
|
+
"max_rows": 100
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## Installation
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
pip install -r requirements.txt
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Or with uv:
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
uv pip install -r requirements.txt
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## Configuration
|
|
167
|
+
|
|
168
|
+
Add to your Claude Desktop or MCP client config:
|
|
169
|
+
|
|
170
|
+
```json
|
|
171
|
+
{
|
|
172
|
+
"mcpServers": {
|
|
173
|
+
"doc-index": {
|
|
174
|
+
"command": "python",
|
|
175
|
+
"args": ["/path/to/doc-index-mcp/src/server.py"],
|
|
176
|
+
"env": {
|
|
177
|
+
"MCP_WORKING_DIR": "/path/to/your/project",
|
|
178
|
+
"DOC_INDEX_DIR": "/path/to/store/indices"
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### Environment Variables
|
|
186
|
+
|
|
187
|
+
| Variable | Description | Default |
|
|
188
|
+
|----------|-------------|---------|
|
|
189
|
+
| `MCP_WORKING_DIR` | Base directory for resolving file paths | Current working directory |
|
|
190
|
+
| `DOC_INDEX_DIR` | Directory for storing vector indices | `.docindex` in working dir |
|
|
191
|
+
|
|
192
|
+
## Architecture
|
|
193
|
+
|
|
194
|
+
Everything runs locally - no external APIs, databases, or embedding servers required.
|
|
195
|
+
|
|
196
|
+
```mermaid
|
|
197
|
+
flowchart TB
|
|
198
|
+
subgraph Client["MCP Client (Claude Desktop, etc.)"]
|
|
199
|
+
LLM[LLM]
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
subgraph MCP["Doc Index MCP Server"]
|
|
203
|
+
Server[server.py]
|
|
204
|
+
|
|
205
|
+
subgraph Services["Local Services"]
|
|
206
|
+
Loader[Document Loader<br/>PDF, DOCX, PPTX, XLSX]
|
|
207
|
+
Chunker[Boundary-Aware<br/>Chunker]
|
|
208
|
+
Embedder[Embedder<br/>ONNX Runtime]
|
|
209
|
+
VectorStore[Vector Store<br/>usearch]
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
subgraph Storage["Local Filesystem"]
|
|
214
|
+
Docs[(Source<br/>Documents)]
|
|
215
|
+
Index[(".docindex/<br/>├── manifest.json<br/>└── vectors/<br/> ├── index.usearch<br/> ├── chunks.jsonl<br/> └── boundaries.json")]
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
subgraph Models["Embedded Model (downloaded once)"]
|
|
219
|
+
ONNX[BAAI/bge-small-en-v1.5<br/>ONNX format ~50MB]
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
LLM <-->|MCP Protocol| Server
|
|
223
|
+
Server --> Loader
|
|
224
|
+
Server --> Chunker
|
|
225
|
+
Server --> Embedder
|
|
226
|
+
Server --> VectorStore
|
|
227
|
+
|
|
228
|
+
Loader -->|read| Docs
|
|
229
|
+
VectorStore <-->|read/write| Index
|
|
230
|
+
Embedder -->|load once| ONNX
|
|
231
|
+
|
|
232
|
+
style Client fill:#e1f5fe
|
|
233
|
+
style Storage fill:#fff3e0
|
|
234
|
+
style Models fill:#f3e5f5
|
|
235
|
+
style MCP fill:#e8f5e9
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
### Data Flow
|
|
239
|
+
|
|
240
|
+
```mermaid
|
|
241
|
+
flowchart LR
|
|
242
|
+
subgraph Index["Indexing"]
|
|
243
|
+
direction TB
|
|
244
|
+
A[Document] --> B[Load & Extract Text]
|
|
245
|
+
B --> C[Detect Boundaries]
|
|
246
|
+
C --> D[Chunk ~256 tokens]
|
|
247
|
+
D --> E[Generate Embeddings]
|
|
248
|
+
E --> F[Save to Disk]
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
subgraph Search["Searching"]
|
|
252
|
+
direction TB
|
|
253
|
+
G[Query] --> H[Embed Query]
|
|
254
|
+
H --> I[Vector Similarity Search]
|
|
255
|
+
I --> J[Expand to Boundaries]
|
|
256
|
+
J --> K[Return Results]
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
Index -.->|stored in .docindex/| Search
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
## License
|
|
263
|
+
|
|
264
|
+
MIT
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
# Doc Index MCP
|
|
2
|
+
|
|
3
|
+
## What is This For?
|
|
4
|
+
|
|
5
|
+
A local-first semantic search server for your documents. Index PDFs, Word docs, PowerPoints, Excel files, and text/markdown, then search them using natural language via the Model Context Protocol (MCP).
|
|
6
|
+
|
|
7
|
+
- **Semantic search** - Find relevant content using natural language queries
|
|
8
|
+
- **Boundary-aware chunking** - Respects document structure (chapters, sections, headers)
|
|
9
|
+
- **Table extraction** - Extract tables from documents as CSV
|
|
10
|
+
- **Fully local** - No external APIs, no cloud services, no PyTorch
|
|
11
|
+
- **Lightweight** - ONNX-based embeddings (~50MB vs ~2GB for PyTorch)
|
|
12
|
+
|
|
13
|
+
## Supported Formats
|
|
14
|
+
|
|
15
|
+
| Format | Extensions | Notes |
|
|
16
|
+
|--------|------------|-------|
|
|
17
|
+
| Text | `.txt` | Plain text |
|
|
18
|
+
| Markdown | `.md`, `.markdown` | Preserves headers for boundaries |
|
|
19
|
+
| PDF | `.pdf` | Text extraction with page markers |
|
|
20
|
+
| Word | `.docx` | Paragraphs, headings, tables |
|
|
21
|
+
| PowerPoint | `.pptx` | Slides, notes, tables |
|
|
22
|
+
| Excel | `.xlsx`, `.xls` | Sheets as tables |
|
|
23
|
+
|
|
24
|
+
### Why No External Services?
|
|
25
|
+
|
|
26
|
+
| Component | Traditional RAG | This Server |
|
|
27
|
+
|-----------|-----------------|-------------|
|
|
28
|
+
| Embeddings | OpenAI API / hosted model | Local ONNX model (fastembed) |
|
|
29
|
+
| Vector DB | Pinecone / Weaviate / Qdrant | Local file (usearch) |
|
|
30
|
+
| Storage | Cloud / managed DB | Local `.docindex/` directory |
|
|
31
|
+
| Dependencies | PyTorch (~2GB) | ONNX Runtime (~50MB) |
|
|
32
|
+
|
|
33
|
+
## Tools
|
|
34
|
+
|
|
35
|
+
### `doc_index`
|
|
36
|
+
Index a document for semantic search.
|
|
37
|
+
|
|
38
|
+
```json
|
|
39
|
+
{
|
|
40
|
+
"file_path": "docs/manual.pdf",
|
|
41
|
+
"source_name": "manual"
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### `doc_search`
|
|
46
|
+
Search indexed documents using natural language.
|
|
47
|
+
|
|
48
|
+
```json
|
|
49
|
+
{
|
|
50
|
+
"query": "how to configure authentication",
|
|
51
|
+
"top_k": 5,
|
|
52
|
+
"expand_to_boundary": "section",
|
|
53
|
+
"max_return_tokens": 4096
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Parameters:
|
|
58
|
+
- `query` - Search query
|
|
59
|
+
- `sources` - Filter to specific sources (optional)
|
|
60
|
+
- `top_k` - Number of results (default: 5)
|
|
61
|
+
- `expand_to_boundary` - Expand results to full "section" or "chapter"
|
|
62
|
+
- `max_return_tokens` - Token budget for results (default: 4096)
|
|
63
|
+
- `include_siblings` - Include sibling sections when expanding
|
|
64
|
+
|
|
65
|
+
### `doc_list`
|
|
66
|
+
List all indexed sources.
|
|
67
|
+
|
|
68
|
+
### `doc_chunk`
|
|
69
|
+
Retrieve a specific chunk by ID with optional neighbors.
|
|
70
|
+
|
|
71
|
+
```json
|
|
72
|
+
{
|
|
73
|
+
"chunk_id": "manual:42",
|
|
74
|
+
"neighbors": 2
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### `read_document`
|
|
79
|
+
Read a document without indexing. Returns formatted text.
|
|
80
|
+
|
|
81
|
+
```json
|
|
82
|
+
{
|
|
83
|
+
"file_path": "report.pdf",
|
|
84
|
+
"max_chars": 100000
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### `list_tables`
|
|
89
|
+
List all tables in a document.
|
|
90
|
+
|
|
91
|
+
```json
|
|
92
|
+
{
|
|
93
|
+
"file_path": "data.xlsx"
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### `extract_table`
|
|
98
|
+
Extract a specific table as CSV.
|
|
99
|
+
|
|
100
|
+
```json
|
|
101
|
+
{
|
|
102
|
+
"file_path": "data.xlsx",
|
|
103
|
+
"table_index": 0,
|
|
104
|
+
"max_rows": 100
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Installation
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
pip install -r requirements.txt
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Or with uv:
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
uv pip install -r requirements.txt
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Configuration
|
|
121
|
+
|
|
122
|
+
Add to your Claude Desktop or MCP client config:
|
|
123
|
+
|
|
124
|
+
```json
|
|
125
|
+
{
|
|
126
|
+
"mcpServers": {
|
|
127
|
+
"doc-index": {
|
|
128
|
+
"command": "python",
|
|
129
|
+
"args": ["/path/to/doc-index-mcp/src/server.py"],
|
|
130
|
+
"env": {
|
|
131
|
+
"MCP_WORKING_DIR": "/path/to/your/project",
|
|
132
|
+
"DOC_INDEX_DIR": "/path/to/store/indices"
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### Environment Variables
|
|
140
|
+
|
|
141
|
+
| Variable | Description | Default |
|
|
142
|
+
|----------|-------------|---------|
|
|
143
|
+
| `MCP_WORKING_DIR` | Base directory for resolving file paths | Current working directory |
|
|
144
|
+
| `DOC_INDEX_DIR` | Directory for storing vector indices | `.docindex` in working dir |
|
|
145
|
+
|
|
146
|
+
## Architecture
|
|
147
|
+
|
|
148
|
+
Everything runs locally - no external APIs, databases, or embedding servers required.
|
|
149
|
+
|
|
150
|
+
```mermaid
|
|
151
|
+
flowchart TB
|
|
152
|
+
subgraph Client["MCP Client (Claude Desktop, etc.)"]
|
|
153
|
+
LLM[LLM]
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
subgraph MCP["Doc Index MCP Server"]
|
|
157
|
+
Server[server.py]
|
|
158
|
+
|
|
159
|
+
subgraph Services["Local Services"]
|
|
160
|
+
Loader[Document Loader<br/>PDF, DOCX, PPTX, XLSX]
|
|
161
|
+
Chunker[Boundary-Aware<br/>Chunker]
|
|
162
|
+
Embedder[Embedder<br/>ONNX Runtime]
|
|
163
|
+
VectorStore[Vector Store<br/>usearch]
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
subgraph Storage["Local Filesystem"]
|
|
168
|
+
Docs[(Source<br/>Documents)]
|
|
169
|
+
Index[(".docindex/<br/>├── manifest.json<br/>└── vectors/<br/> ├── index.usearch<br/> ├── chunks.jsonl<br/> └── boundaries.json")]
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
subgraph Models["Embedded Model (downloaded once)"]
|
|
173
|
+
ONNX[BAAI/bge-small-en-v1.5<br/>ONNX format ~50MB]
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
LLM <-->|MCP Protocol| Server
|
|
177
|
+
Server --> Loader
|
|
178
|
+
Server --> Chunker
|
|
179
|
+
Server --> Embedder
|
|
180
|
+
Server --> VectorStore
|
|
181
|
+
|
|
182
|
+
Loader -->|read| Docs
|
|
183
|
+
VectorStore <-->|read/write| Index
|
|
184
|
+
Embedder -->|load once| ONNX
|
|
185
|
+
|
|
186
|
+
style Client fill:#e1f5fe
|
|
187
|
+
style Storage fill:#fff3e0
|
|
188
|
+
style Models fill:#f3e5f5
|
|
189
|
+
style MCP fill:#e8f5e9
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### Data Flow
|
|
193
|
+
|
|
194
|
+
```mermaid
|
|
195
|
+
flowchart LR
|
|
196
|
+
subgraph Index["Indexing"]
|
|
197
|
+
direction TB
|
|
198
|
+
A[Document] --> B[Load & Extract Text]
|
|
199
|
+
B --> C[Detect Boundaries]
|
|
200
|
+
C --> D[Chunk ~256 tokens]
|
|
201
|
+
D --> E[Generate Embeddings]
|
|
202
|
+
E --> F[Save to Disk]
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
subgraph Search["Searching"]
|
|
206
|
+
direction TB
|
|
207
|
+
G[Query] --> H[Embed Query]
|
|
208
|
+
H --> I[Vector Similarity Search]
|
|
209
|
+
I --> J[Expand to Boundaries]
|
|
210
|
+
J --> K[Return Results]
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
Index -.->|stored in .docindex/| Search
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
## License
|
|
217
|
+
|
|
218
|
+
MIT
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "doc-index-mcp"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "MCP server for semantic document search with boundary-aware chunking"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.11"
|
|
11
|
+
license = { file = "LICENSE" }
|
|
12
|
+
dependencies = [
|
|
13
|
+
"mcp>=1.0.0",
|
|
14
|
+
"pydantic>=2.0.0",
|
|
15
|
+
"numpy>=1.24.0",
|
|
16
|
+
# Embedding - ONNX-based, lightweight (~50x smaller than PyTorch)
|
|
17
|
+
"fastembed>=0.2.0",
|
|
18
|
+
# Vector search - pre-built wheels, no C++ compilation
|
|
19
|
+
"usearch>=2.0.0",
|
|
20
|
+
# Token counting
|
|
21
|
+
"tiktoken>=0.5.0",
|
|
22
|
+
# Document loading
|
|
23
|
+
"pdfplumber>=0.10.0",
|
|
24
|
+
"python-docx>=0.8.11",
|
|
25
|
+
"python-pptx>=0.6.21",
|
|
26
|
+
"openpyxl>=3.1.0",
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
[project.optional-dependencies]
|
|
30
|
+
cuda = [
|
|
31
|
+
"onnxruntime-gpu>=1.17.0",
|
|
32
|
+
]
|
|
33
|
+
apple = [
|
|
34
|
+
"onnxruntime>=1.17.0",
|
|
35
|
+
]
|
|
36
|
+
dev = [
|
|
37
|
+
"pytest>=7.0.0",
|
|
38
|
+
"pytest-asyncio>=0.21.0",
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
[project.scripts]
|
|
42
|
+
doc-index-mcp = "src.server:main"
|
|
43
|
+
doc-index-install-skill = "src.install_skill:main"
|
|
44
|
+
|
|
45
|
+
[tool.hatch.build.targets.wheel]
|
|
46
|
+
packages = ["src"]
|
|
47
|
+
artifacts = ["src/skills/**/*.md"]
|
|
48
|
+
|
|
49
|
+
[tool.pytest.ini_options]
|
|
50
|
+
asyncio_mode = "auto"
|
|
51
|
+
testpaths = ["src/tests", "tests"]
|