flint-slating
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@flint-slatingread the attached PDF and return its structured Markdown content"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
flint-slating
MCP server that reads PDFs and exposes them to LLM consumers as structured Markdown, plus the usual ancillaries: metadata, outline, images, tables.
Designed to pair with a separate "wiki" MCP server that handles the
writing side — an agent calls flint-slating to read PDFs and another
MCP to persist notes about them into a frontmattered-markdown knowledge
base.
What it does
Built on a permissive-license PDF stack:
Library | License | Role |
MIT | PDF → Markdown with heading hierarchy, multi-column reading order, and Markdown tables | |
BSD-3 | metadata, TOC, page count, encryption checks, image enumeration | |
MIT | per-page table extraction |
There is no PyMuPDF, no MuPDF, no AGPL or GPL anywhere in the
dependency tree. The licenses job of license-check.yml rejects PRs that pull in
copyleft transitive deps.
Related MCP server: pdf-mcp
Transports
Two transports off the same MCP server, selected via --transport:
Transport | Run via | Use case |
Streamable-HTTP (default) |
| Long-lived local daemon, container, or shared service. |
stdio |
| The standard MCP integration shape — drop into |
Run
As an HTTP daemon (default)
uvx flint-slating # listens on PORT (default 35833)
curl http://127.0.0.1:35833/healthOr pin it:
uv tool install flint-slating
flint-slatingAs a stdio MCP server
uvx flint-slating --transport stdioWire into Claude Code's MCP config:
{
"mcpServers": {
"flint-slating": {
"command": "uvx",
"args": ["flint-slating", "--transport", "stdio"]
}
}
}Docker
docker run --rm \
-p 35833:35833 \
-v $(pwd)/pdfs:/pdfs:ro \
-v flint-slating-data:/data \
ghcr.io/parkviewlab/flint-slating:latestOr use docker-compose.yml for a persistent stack.
MCP tools
All PDF tools take a source argument with one of:
{"path": "/abs/path/to/file.pdf"}— local file{"url": "https://..."}— streamed to a content-addressed cache{"bytes_b64": "...", "filename": "x.pdf"}— base64 upload (size-capped)
Tool | What it does |
|
|
| flat outline |
| plain text by page range (fast — pypdf, no ML) |
| high-quality Markdown via Docling (hybrid sync/async — see below) |
| per-page Markdown chunks with tables/images/toc_items (hybrid sync/async) |
| enumerate images: |
| base64 bytes of one image |
| per-page Markdown tables via pdfplumber |
| poll a background job |
| fetch a finished job's artifact |
| cancel a running job |
Hybrid sync/async
pdf_read_markdown and pdf_read_chunks run inline when
the number of pages requested is at most SYNC_PAGE_THRESHOLD (default 20;
all pages when pages is omitted, so the count is the PDF's page_count
then). For larger requests they
queue a background job and return a job_id — poll get_job_status
until state=="done", then call get_job_result (or, in HTTP mode,
fetch output_url directly).
stdio mode transparently waits for the job inline — there's no HTTP server to download from, so the originating tool call blocks until the result is ready and returns it directly.
HTTP endpoints (HTTP mode only)
GET /health—{ok, version, uptime_seconds}GET /admin/version— package and dependency versions, and Docling status (docling_model_loadedreports whether the converter has been built, not whether a model is loaded)GET /admin/jobs— recent job listGET /outputs/{job_id}/result.md— finished MarkdownGET /outputs/{job_id}/result.json— finished chunked outputGET /outputs/{job_id}/log.jsonl— append-only job logPOST /sse— MCP Streamable-HTTP transport
Configuration
Env var | Default (daemon) | Default (container) | Purpose |
|
|
| HTTP bind port |
|
|
| HTTP bind address |
|
|
| Per-job output dirs |
|
|
| Materialized URL / base64 PDFs |
|
|
| Hourly sweep (HTTP mode only) of job directories older than N days by directory mtime; |
|
|
| Cap on base64 upload size |
|
|
| Cap on URL download size |
|
|
| Inline-vs-job cutoff for Markdown conversion |
|
|
| Caps the job history kept and the |
|
|
| Cap on the size of one image returned by |
| unset | unset | Directory of pre-downloaded Docling models; see Resource notes |
|
|
| Used to build |
Resource notes
Docling downloads its layout models (~200–500 MB) on the first conversion unless
DOCLING_ARTIFACTS_PATHis set. The container image does not pre-fetch them (pre-fetching dominated multi-arch build time under QEMU), so the first Markdown conversion pays the download. IfDOCLING_ARTIFACTS_PATHis set, it must point at a directory that already holds every model (docling-tools models download -o <dir>, for instance into a mounted volume); Docling never downloads into it. Default: unset.OCR follows Docling's default options, which run OCR; there is no setting for it.
pypdf, pdfplumber, and the URL / base64 paths are fast and have no ML overhead — use
pdf_info,pdf_toc,pdf_read_text, andpdf_find_tableswhenever Markdown isn't strictly needed.
Releasing
Tag-driven CI publishes to both PyPI (flint-slating) and GHCR (ghcr.io/parkviewlab/flint-slating):
The release runs from the CLI in the flint-slating-main worktree, as the
ParkviewLab handbook's releases.md gives it under "Cutting a release" and
"The release's last step":
git pull --ff-only # sync main
git -C ../flint-slating-develop pull --ff-only # sync develop
git merge --no-ff develop # promote develop to main
git bump <patch|minor|major|release|X.Y.Z>
git release # annotated tag, derived from pyproject.toml
git push --follow-tags # the tag push fires the release workflow
git back-merge # the release's last stepThe release workflow refuses a tag that does not match pyproject.toml's version, that still carries a dev marker (.devN), that is not on origin/main, or that is not greater than the previous release tag.
Commit message convention
After the publish jobs, a changelog job generates the new CHANGELOG.md section — an LLM-written "Highlights" paragraph plus a categorized list written by dev-tools' generate-changelog — commits it back to main, and creates the GitHub Release with the same content as its body. Categorization uses Conventional Commits prefixes (the full list is in the ParkviewLab handbook's commits-and-changelogs.md):
Title | Group in the notes | Notes |
any type with | Breaking changes | listed there once, whatever its type |
| Features | user-visible |
| Bug fixes | user-visible |
| Performance | user-visible |
| Refactor | |
| Docs | |
| Tests | |
| Reverts | GitHub's Revert button titles a PR |
| Maintenance | |
any other title | Other changes | the whole title |
a commit with no pull request | Direct commits | its subject and short hash |
A title without a recognised type is not dropped: it is listed whole under Other changes. So prefix your PR titles, and correct a title before the merge, since retitling afterwards does not change the commit. The groups appear in the order above, and an empty group is left out.
License
Licensed under either of
Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0), or
MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option. In SPDX terms: MIT OR Apache-2.0.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this work by you shall be dual-licensed as above, without any additional terms or conditions. See LICENSING.md.
flint-slating only depends on permissive-licensed libraries; the licenses job
of license-check.yml enforces this on every PR. torch and torchvision are
pinned to the CPU-only PyTorch wheel index
for the GHCR image and for uv sync from a checkout (uv.lock resolves torch
to the CPU wheels), so those do not bundle NVIDIA's proprietary CUDA libraries.
A PyPI install (uvx flint-slating, uv tool install flint-slating) does not
read that pin and gets PyPI's torch, which on Linux pulls NVIDIA's CUDA
libraries unless torch is installed from the CPU index. Inference runs on CPU
on Linux/Windows and on MPS (Metal) on Apple Silicon. See
THIRD_PARTY_LICENSES.md for the per-dependency
license breakdown.
© 2026 Gary Frattarola · Licensed under MIT OR Apache-2.0 · part of ParkviewLab
This server cannot be deployed
Maintenance
Related MCP Connectors
Hosted MCP server: convert PDFs to clean, LLM-ready Markdown with tables, formulas and OCR.
Document-to-Markdown MCP server — convert PDF, Office and HTML into LLM-ready Markdown.
Convert PDF documents to Markdown for AI agents through an authenticated remote MCP server.
Document conversion MCP server: PDF to Markdown, image OCR, spreadsheet parsing.
Related MCP Servers
- AlicenseAqualityFmaintenanceAn MCP server that exports PDF documents to markdown format optimized for LLM processing.11BSD 3-Clause
- AlicenseAqualityCmaintenanceAn MCP server for reading, rendering, and searching PDF files, specifically optimized for LLMs to extract text, tables, and technical diagrams. It enables metadata retrieval, multi-format text extraction, and page-to-image rendering using PyMuPDF.579MIT

pymupdf4llm-mcpofficial
AlicenseNot gradedqualityCmaintenanceMCP server for exporting PDF to markdown, optimized for LLM consumption.503 PyPI71AGPL 3.0- FlicenseNot gradedqualityDmaintenanceA local MCP server that extracts text-layer content from PDF files, enabling AI agents to inspect, extract text, outlines, and page content.-