{
  "markdown": "# doc-extract-mcp\n\nAn MCP (Model Context Protocol) server that gives an LLM deterministic document\ntooling for structured-data extraction workflows. The LLM does the reading and\nextraction reasoning; this server provides the parts that should never be left\nto a language model: reliable file access, parsing, chunking, JSON Schema\nvalidation, and guarded file output.\n\nBuilt by Koray Nar as a portfolio project for the AI document-automation\nworkflows he is building — the target use case is turning messy PDFs (purchase\norders, invoices, reports) into schema-validated JSON. Published as part of a\npublic portfolio. Pairs with Claude Code and Claude Desktop, and with any\nother MCP client.\n\n## Why\n\nAn extraction agent fails in predictable places: it hallucinates file contents,\nloses track of long documents, silently produces JSON that almost matches the\ntarget schema, and writes output wherever it likes. This server removes those\nfailure modes:\n\n- File access is confined to one allowed root (`DOC_EXTRACT_ROOT`).\n- PDF text arrives with explicit `--- page N ---` markers, so citations of\n  \"page 3\" mean page 3.\n- Long documents are chunked deterministically with overlap and page hints.\n- Extracted JSON is checked against a JSON Schema (Draft 2020-12) and **every**\n  error is reported with a JSON Pointer path — not just the first — so the\n  model can fix all mistakes in one pass.\n- Output is written by the server (JSON or CSV), inside the same root, with a\n  verifiable row/byte count.\n\n## Tools\n\n| Tool | Arguments | What it does |\n|---|---|---|\n| `list_documents` | `directory`, `glob_pattern='*'` | List files under a directory inside the allowed root, with size and modified time. Supports recursive globs like `**/*.pdf`. Patterns must be relative and free of `..`; matches resolving outside the root are dropped. |\n| `read_document` | `path`, `pages=''` | Return a document's text. `.pdf` via pypdf with `--- page N ---` markers and optional 1-indexed page selection (`'3'`, `'1-5'`, `'1-3,7'`); `.txt`/`.md`/`.json` read directly; `.csv` rendered as an aligned text table. Clear error for unsupported types. |\n| `document_info` | `path` | Metadata without full content: type, size, modified time; page count and PDF metadata for PDFs; line count for text files. |\n| `chunk_document` | `path`, `max_chars=4000`, `overlap=200` | Split a document into ordered overlapping chunks, each with an index, start offset, and (for PDFs) a page hint. |\n| `validate_json` | `data`, `json_schema` | Validate a JSON string against a JSON Schema (Draft 2020-12). Returns every validation error with a JSON Pointer path via `Draft202012Validator.iter_errors`. |\n| `save_structured` | `path`, `data`, `format='json'\\|'csv'` | Write extracted data inside the allowed root. CSV expects a JSON array of flat objects. Returns written path, row count, and byte count. |\n\nAll path arguments are resolved and refused if they escape the allowed root\n(path traversal guard). The `glob_pattern` argument is confined the same way:\nabsolute patterns and patterns containing `..` are rejected, and any match\nthat resolves outside the root (for example through a symlink) is silently\ndropped from the listing. Guard failures are raised as MCP tool errors, so\nthe calling model sees the actual reason, not a masked generic error.\n\n## Quickstart\n\nRequires Python 3.11+ and [uv](https://docs.astral.sh/uv/).\n\n```bash\ngit clone https://github.com/koraynar/doc-extract-mcp.git\ncd doc-extract-mcp\nuv venv\nuv pip install -e .\n```\n\nRun standalone (stdio transport):\n\n```bash\nDOC_EXTRACT_ROOT=/path/to/your/documents uv run doc-extract-mcp\n```\n\n### Claude Code\n\n```bash\nclaude mcp add doc-extract --env DOC_EXTRACT_ROOT=/path/to/your/documents \\\n  -- uv run --directory /absolute/path/to/doc-extract-mcp doc-extract-mcp\n```\n\n### Claude Desktop\n\nAdd to `claude_desktop_config.json`:\n\n```json\n{\n  \"mcpServers\": {\n    \"doc-extract\": {\n      \"command\": \"uv\",\n      \"args\": [\n        \"run\",\n        \"--directory\",\n        \"/absolute/path/to/doc-extract-mcp\",\n        \"doc-extract-mcp\"\n      ],\n      \"env\": {\n        \"DOC_EXTRACT_ROOT\": \"/path/to/your/documents\"\n      }\n    }\n  }\n}\n```\n\n`DOC_EXTRACT_ROOT` defaults to the server's working directory if unset. Set it\nto the folder your documents live in; nothing outside it can be read or\nwritten.\n\n### Typical workflow\n\n1. `list_documents(\".\", \"*.pdf\")` — find the invoices.\n2. `document_info(\"invoice.pdf\")` — check the page count.\n3. `read_document(\"invoice.pdf\", \"1-3\")` or `chunk_document(...)` — get text.\n4. The LLM extracts fields into JSON.\n5. `validate_json(data, json_schema)` — fix every reported error, revalidate.\n6. `save_structured(\"out/invoice.json\", data, \"json\")` — write the result.\n\n## Limitations (honest ones)\n\n- **Text-based PDFs only.** Extraction uses pypdf; scanned/image-only PDFs\n  yield empty text. There is no OCR.\n- **Extraction quality varies** with how the PDF was produced. Complex layouts\n  (multi-column, heavy tables) may come out with imperfect reading order —\n  that is a pypdf characteristic this server inherits.\n- **No .docx / .xlsx support.** Supported types are `.pdf`, `.txt`, `.md`,\n  `.csv`, `.json`.\n- **The server does no extraction reasoning.** It will not find your invoice\n  total; it makes sure the model that does is working from real text and that\n  the result matches your schema.\n- This is a working tool, built for the AI-automation work I'm building up and\n  published as part of my portfolio — it is new and has no production mileage\n  yet. It has tests and a path-confinement guard, but it has not been hardened\n  beyond that — review before pointing it at sensitive directories.\n\n## Development\n\n```bash\nuv venv\nuv pip install -e '.[dev]'\nuv run pytest\n```\n\nThe test suite builds a small two-page PDF fixture in-memory (a minimal\nhand-constructed PDF, no extra dependencies) and covers all six tools, the\npath-traversal guard, glob-pattern confinement (including symlink escapes),\npage-range errors, multi-error schema validation, a CSV round-trip, and tool\nregistration plus error propagation through the MCP server object.\n\n## License\n\nMIT © 2026 Koray Nar\n",
  "bytes": 6146,
  "sha": "73f9c3173067adfb92e5609f534cfde3a86ce6278d33f7f931899dcebe907132",
  "repo_slug": "koraynar/doc-extract-mcp",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_koraynar_doc_extract_mcp_dc431489/readme"
}