file-indexer-mcp
An HTTP MCP server that indexes large documents into exact-line-numbered sections, enabling AI models to locate, read, summarize, and edit parts of a document without ingesting the whole file.
README
file-indexer
An HTTP MCP server that breaks large documents into an exact-line-numbered section index, so a small local model can locate, read, summarize and edit one part of a document at a time — without ever ingesting the whole file.
Formats: markdown, plain text, HTML, PDF.
Why
A 207k-token document does not fit in a 32k-context model. Uploading it is not the answer; finding the right 1,600 tokens is. The index is built by plain extraction — never by the model — so it is exact and effectively free:
745,247 chars / 207k tokens / 8,079 lines → 129 sections in 234ms, largest 1,666 tokens
find("tslib") → section s112, lines 6971-7034, ~1,610 tokens (zero model tokens spent)
Retrieval is BM25 over the index and costs nothing. The model only ever sees the span it asked for.
How a model uses it
| Question | Tool | Cost |
|---|---|---|
| What's in this file? | outline |
0 tokens |
| Where is X? | find |
0 tokens |
| Read just that part | read_section / read_span |
one section |
| Process the whole file part by part | next_unit |
one unit per call |
| Summarize the file | summarize_file |
map-reduce, reports coverage |
| Add something at section X | edit_section |
backed up, restorable |
Sections carry both own and subtree sizes, so a model can tell a 32-token heading from the
11k-token chapter beneath it before committing to a read. Documents with no headings still get a
navigable index: HTML falls back to <section>/<article>, then batched repeating rows, then
synthetic packing — such sections are marked synthetic and shown as (auto).
summarize_file returns a coverage fraction and an explicit capped/dropped_sections list.
A summary that silently skips most of a document reads exactly like a good one, so under-coverage
is reported rather than hidden.
Tools
| Tool | Description |
|---|---|
index_file |
Build or refresh one document's index; incremental, milliseconds |
index_all |
Index every supported document under the workspace |
index_status |
Whether a document is indexed, and whether it changed on disk since |
list_documents |
Indexable documents with size and indexed state |
outline |
Table of contents: section ids, depth, titles, line spans, sizes |
find |
Locate sections by keyword — zero-token BM25, no model call |
read_section |
Read one section's own text (or one chunk of it) |
read_span |
Read an arbitrary line range verbatim |
next_unit |
Walk a document sequentially, one readable unit per call |
summarize_section |
Summarize one section or its subtree; cached |
summarize_file |
Whole-document map-reduce summary with a coverage figure |
edit_section |
Insert/replace/delete at a section id — 7 explicit modes |
edit_replace |
Unique-match search/replace, with a whitespace-tolerant fallback |
list_backups / restore_backup |
Every write is backed up first |
health |
Model endpoint, configured model, indexed document count |
edit_section modes are explicit about subsections, because picking the wrong boundary silently
misplaces text: append (end of own text, before subsections), append_subtree (after all of
them), prepend, replace_body, replace_subtree, replace_all, delete.
A PDF has no lines. Its text is extracted once to a sidecar under .fileindex/text/, and every
line number reported for a PDF refers to that extracted text, not the PDF. Sections also carry
start_page/end_page:
s2 # Preface (lines 47-121, ~1122 tok, p.4-5)
s7 # List of Figures (lines 490-662, ~2493 tok, p.15-18) [2 chunks]
Structure comes from PDF bookmarks when present, then a font-size heuristic, then page grouping. PDF is read-only — the edit tools refuse it.
Run it
cp .env.example .env # set WORKSPACE_HOST_DIR to the directory you want indexed
./start.sh
start.sh runs the container as the owner of WORKSPACE_HOST_DIR. This matters: the editor writes
atomically (temp file + rename), so a root container would rewrite an edited document as root:root
and lock its owner out of their own file.
The workspace is mounted read-write — edit_section/edit_replace can create, modify or delete
anything under it. Scope it to the documents you actually want editable.
Summarization uses a local model over an OpenAI-compatible endpoint (llama.cpp/llama-swap by
default, Ollama optional). Point LLAMACPP_URL at an already-loaded instance rather than letting
a second process load its own copy — two copies of the same model will exhaust a small GPU.
Adding a format
parsers.py is a registry mapping extension to a parser returning
list[dict] sections shaped {title, depth, start_line, end_line, heading_lines}. A new line-based
format is one module plus one registry entry, and inherits the heading-less fallback for free.
PDF is the exception: it has no lines, so parsers.extract_pdf() does extraction and section
detection together and returns text that docindex.build() writes to a sidecar and indexes instead.
Licence
MIT — see LICENSE.
PDF extraction uses PyMuPDF, which is AGPL-3.0. That only matters if you redistribute this server; it is not a concern for local or private-network use. Everything else is the Python standard library plus the MCP SDK.
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。