latex-build-mcp
An MCP server that compiles LaTeX and tells an agent, in structured form, what is wrong with the result: errors with file and line, overfull boxes, Beamer frames that run past the slide, undefined or unused labels and citations, the numbering recorded in the .aux, and a LaTeX → Word conversion that keeps cross-reference numbers.
It needs a TeX distribution on the machine (TeX Live or MacTeX). Optional tools add features: latexmk, biber, pandoc, texcount, chktex, pdftotext. toolchain_status shows what is there and what each missing tool disables.
Install
{
"mcpServers": {
"latex-build": {
"command": "node",
"args": ["/path/to/latex-build-mcp/server.mjs"],
"env": { "LATEX_MCP_ROOT": "/path/to/your/papers" }
}
}
}
| Variable | Meaning | Default |
|---|
LATEX_MCP_ROOT | The only directory the server reads and writes | working directory |
LATEX_MCP_TIMEOUT_S | Time limit for each child process, in seconds | 120 |
Tools
| Tool | What it returns |
|---|
toolchain_status | Installed tools and versions, engines available, capabilities |
compile | Success, page count, PDF path, parsed diagnostics. engine: pdflatex, xelatex, lualatex. clean: true builds from scratch |
parse_log | Errors (file, line, context, explanation), warnings, overfull / underfull boxes, undefined references and citations, duplicate labels, missing files, fonts and characters, rerun hints |
check_overflow | Overfull hboxes above hbox_threshold_pt with the source text; overfull vboxes; for Beamer, the overflowing frames by number, title and line |
beamer_report | Every frame (title, line, pages, overlay slides), overflowing frames, frames without a title |
audit_references | Static scan: undefined references, duplicate labels with locations, unused labels, unreferenced floats, undefined citations, unused and duplicate .bib keys |
bib_audit | Backend expected by the document against the one that ran, entries not found, warnings, errors, incomplete entries |
word_count | texcount figures (text, headings, captions), or a documented approximation |
lint | chktex, curated subset (all: true for everything) |
extract_numbering | Number, page and kind of every label; captions; citation labels |
to_docx | A .docx with cross-references, caption numbers and equation numbers resolved from the .aux, and the list of what does not survive |
pdf_text | Text of a page range of the PDF |
clean_build | Removes the build directory created by this server (confirm: true) |
explain_error | Cause and fix for a known error message |
The tools that read a build (parse_log, check_overflow, beamer_report, bib_audit, extract_numbering, to_docx, pdf_text) use the last compile of the same file and say so when there is none.
Safety
- One root. Every path is resolved, symlinks included, and refused when it leaves
LATEX_MCP_ROOT. \input files outside the root are skipped by the scans.
- No shell. Programs are started with
execFile and a timeout; nothing is passed through a shell.
- No shell escape. Engines always run with
-no-shell-escape: \write18 and piped \input do nothing, and compile lists the commands the document tried to run. Documents that need shell escape (minted, externalised TikZ, gnuplot) will not build here; pre-generate their output.
- No project rc file. latexmk runs with
-norc, because a latexmkrc is Perl code. Settings from such a file (custom engines, extra rules) are therefore ignored.
- A marked build directory. Output goes to
.latex-build/ next to the main file, marked with a file. The server refuses to build into, and clean_build refuses to remove, a directory that is not marked. Source files are never deleted or modified.
- Files are data. Text in
.tex, .bib and log files is returned as data; the server gives it no authority.
A TeX document is still a program: it can read files the TeX configuration allows (by default, not parent directories or dotfiles) and loop until the timeout. Compile untrusted documents in a sandbox.
Limits
- Log parsing is exact for logs produced by this server (long lines,
file:line:error). For other logs, wrapped at 79 columns, file attribution can be missing.
- TeX reports an overfull paragraph when the paragraph ends: a paragraph that starts in an
\input file and ends in its parent is attributed to the parent.
- Beamer overflow is what Beamer reports as an overfull
\vbox. Content placed with absolute positioning (textpos, TikZ overlays) that leaves the slide raises no warning. Frame titles come from the source in order; frames produced by macros, \againframe or conditionals make the mapping approximate, and the report says so.
audit_references is a static scan: labels and keys built by macros are not seen.
to_docx: cross-references become plain text; section numbers come from pandoc, not the .aux; citations are re-rendered by citeproc (author-date unless a CSL file is given); TikZ, custom layout and PDF/EPS images do not carry over.
word_count without texcount is an approximation and is labelled as such.
Tests
npm test
npm run test:offline
The integration tests compile five small fixtures through the MCP protocol and assert exact diagnostics: file and line of each defect, the overflowing frame of the deck, the numbers in the Word file, that \write18 and a hostile latexmkrc do nothing, and that paths outside the root are refused. Tested on macOS with TeX Live 2026, pandoc 3.9 and Node 26. Not tested on Windows or with MiKTeX.
License
MIT