Typleaf MCP Server
Allows for working with LaTeX projects on the same Typleaf Pro server, using the same file editing, section analysis, compilation, PDF download, and PDF-layout/targeting tools for .tex projects.
Allows for working with Typst projects on a Typleaf Pro server, including reading and editing .typ files, updating document sections, compiling the project to PDF, extracting page counts and section-to-page mappings, and locating text in the compiled PDF.
Click on "Install 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., "@Typleaf MCP ServerCompile my Typst project and tell me how many pages it has."
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.
# 🌿 Typleaf MCP Server
A Model Context Protocol (MCP) server for Typst projects on Typleaf Pro — a self-hostable Overleaf fork with Typst support.
28 tools covering full CRUD, document structure analysis, git history & diff, compilation, PDF download, PDF-layout perception, and citation verification.
Typleaf compiles both Typst and LaTeX, so this server handles both. The right parser is chosen from the file extension — you never have to say which.
You: "What kind of project is 6a6dfc57bbb3aac01ed9a71d?"
AI: [project_info] Typst, root main.typ, 36 .typ files, bibliography refs.yml
You: "Read sections/03-method.typ"
AI: [read_file] Here's the content: …
You: "Tighten the Background section"
AI: [update_section] ✓ Edited and pushed
You: "Compile it and tell me how many pages"
AI: [get_page_count] 61 pages (format=typst, source=pdf)
You: "Which page does each heading start on?"
AI: [section_page_map] p.1 = Introduction … p.8 == Parameters …This is a fork of
overleaf-mcp-plusby rangehow, retargeted at self-hosted Typleaf. See What changed from the upstream.
🚀 Setup
1. Install
pip install "typleaf-mcp[compile]"Or run without installing:
uvx --from "typleaf-mcp[compile]" typleaf-mcp2. Get your credentials
Variable | Required for | Where to get it |
| everything | Your instance's URL, e.g. |
| everything else — read, write, compile, PDF | DevTools → Application → Cookies → your instance → |
| optional — commit history only |
|
You do not need a git token. Typleaf's git bridge is an optional module, and many deployments don't run it — on those, no token exists to set. This server reads and writes with the session cookie alone (see Backends). Only
list_history,get_diffandsync_projectneed git, and they explain the gap rather than failing cryptically.
Cookie name: Overleaf ships two. Community Edition — which Typleaf is built on — sets
overleaf.sid; the hosted service setsoverleaf_session2. Guessing wrong looks exactly like an expired session, so this server sends both names with your value unless you pin one viaTYPLEAF_SESSION_COOKIE. Just copy whichever your instance shows.
TYPLEAF_BASE_URLhas no default, on purpose. This server sends your session cookie and git token to whatever host it is pointed at. Defaulting towww.overleaf.com— as the upstream does, correctly, for a single-host service — would mean an unconfigured install leaks a private instance's credentials to a third party. So it errors instead. TheOVERLEAF_*spellings of all three are accepted as fallbacks, so an existingoverleaf-mcpconfig only needs its base URL changed.
The session cookie is HttpOnly: you must copy it from the DevTools Cookies panel, not the JS console.
Git tools additionally need the git-bridge module enabled on your deployment. Without it, clones 404 even though every project is visible in the web UI — the error message says so.
3. Register the server
{
"mcpServers": {
"typleaf": {
"command": "typleaf-mcp",
"env": {
"TYPLEAF_BASE_URL": "https://typleaf.example.com",
"TYPLEAF_SESSION": "s%3A...",
"TYPLEAF_GIT_TOKEN": "olp_..."
}
}
}
}Related MCP server: claudeleaf
🔌 Backends
The server picks its backend from what your deployment actually offers:
web (cookie only) | git (bridge enabled) | |
Selected when | no | token is set |
Read / write files | ✅ | ✅ |
Commit messages | ❌ — edits land as ordinary project changes | ✅ |
| ❌ | ✅ |
status_summary reports which one is live.
How the web backend writes. There is no HTTP endpoint that sets a document's content — setDocument exists but sits behind service-to-service auth, and the editor itself writes over WebSocket using operational transform. But POST /Project/<id>/upload is cookie-authenticated and upserts: uploading a name that already exists replaces that entity in place, keeping its id, and a text file comes back as an editable doc rather than a binary attachment.
The catch is that upload needs a folder_id, and no HTTP endpoint exposes folder ids — /entities gives paths only, /metadata gives doc ids only, and creating a folder that exists returns 400 file already exists. They arrive only in the real-time service's joinProject payload, over socket.io 0.9 — a protocol no maintained Python client speaks. So realtime.py implements the four frame types needed to read the tree, and writes go over plain HTTP afterwards. That sidesteps implementing operational transform, which is the genuinely hard part of talking to Overleaf's editor.
Editing an existing document goes through operational transform, not upload. An edit is sent as the operation — {"p": 12, "d": "Original"}, {"p": 12, "i": "EDITED"} — down the same WebSocket the editor uses, so a collaborator with the file open sees it appear in place. Verified with a second client watching: it receives the op live. A one-word change in a 2,430-character file transmits 7 characters, so cursors, selections and the track-changes attribution of untouched text all survive.
Upload is still used where there is no document to edit yet — new files and binary assets.
One wrinkle worth documenting, since it looks alarming in logs: this server's socket.io 0.9 stack sends its ack in a frame that sets the MASK bit, which RFC 6455 forbids a server to do, and strict clients close the connection rather than read it — after the edit has already applied. So the write is confirmed by reading the document back, not by the ack. That's a stronger check anyway: it tests the result rather than the transport.
Multi-file changes are all-or-nothing. Use write_files rather than looping over rewrite_file. Every edit is validated and resolved before anything is written, so the common failures — missing file, search string matching twice or not at all, duplicate path, bad name — touch nothing at all. If a write still fails, the files already written are restored.
It's a compensating transaction, not a real one (Typleaf has no multi-document transaction and no cookie-reachable version restore), so two limits are reported rather than hidden: rollback can itself fail, in which case the result names exactly which files are in which state; and intermediate writes were real, so a collaborator watching may have seen a state that was later undone.
🎯 Typst specifics
The compiler setting is what makes a project Typst
Typleaf's CLSI generates a Typst sync map only when the project's compiler is typst and the root document ends in .typ. A project full of .typ files that is still on the default pdflatex fails to compile with TeX errors that never mention Typst, and every PDF-position tool silently returns nothing.
You: "Make a new Typst paper"
AI: [create_project name="Paper" compiler="typst"] ✓or on an existing project: set_compiler(project_id, "typst").
Headings
get_sections, update_section and section_page_map recognise =-markup headings at column 0 — exactly the rule Typleaf's own editor outline uses, so the sections this server reports are the ones you see in the IDE's outline pane. Raw blocks, line comments and (nested) block comments are skipped, so a = inside a code sample is not mistaken for a section.
#heading(level: n)[…] calls are found by project_info but are deliberately not editable via update_section — rewriting the body of a programmatically generated heading is not a text-span operation.
Compile logs
Typst writes no log file; it reports everything on stderr, which CLSI captures into output.log. download_log parses those diagnostics into a compact list with file:line:column:
Typst compile log — 1 error(s), 1 warning(s)
✗ expected comma (sections/05-parameters.typ:13:74)
⚠ unknown font family: calibriPass raw=true for the unparsed text.
PDF-position tools, and their honest limits
locate_in_pdf and section_page_map work for Typst, backed by Typleaf's TypstSyncManager rather than SyncTeX. Four differences are real and are reported rather than hidden:
Coarser. Typleaf injects zero-width
#metadatamarkers into the sources after compiling and askstypst querywhere they landed. Anchors are per source line, but only where a marker was safe to place — a lookup resolves to the enclosing block. Content in#let/#showtemplate bodies gets no anchor at all, and a#forbody gets one anchor for the whole loop.Slower.
output.typst-sync.jsonis listed among the compile outputs but the web tier will not proxy it (verified: it 404s whileoutput.pdffrom the same build serves fine), so there is nothing to parse offline. A LaTeX project resolves every heading from oneoutput.synctex.gzparse; a Typst project costs one request per heading.section_page_mapis therefore capped at 150 headings by default (TYPLEAF_MAX_SECTIONS) and says so when the cap bites.Headings are queried via their body prose, not their own line. A document with
#outline()renders every heading twice, and Typleaf's sync map keeps whichever copy fits the file's document-order trajectory — which fails for the first heading in each file, since there is no prior trajectory. Measured on a real 104-page document,= Functionsresolved to page 2 (the contents) against a true page of 12. Prose is never duplicated into an outline, so that is what gets queried. Any residual backwards jump is flagged!in the output rather than presented as fact.No text-area figures.
text_area_fill_pct/text_area_remaining_ptcome from the LaTeXgeometrypackage's log dump. Typst reports no page geometry, so those fields are absent for Typst rather than guessed. Physical-page fullness (from the PDF MediaBox) is still reported.
section_page_map follows #include through the whole document by default — a Typst root is usually a thin index whose own heading count is zero. Pass file to map a single file instead.
Page counts for Typst are measured by parsing the PDF, since typst prints no "Output written on … (N pages)" line. And a Typst compile that produces no diagnostics writes no log at all, which download_log reports as success rather than as a missing file.
Bibliographies
Typst reads BibTeX and its native Hayagriva YAML. verify_citations handles both: .yml entries are translated to the minimal BibTeX the verifier reads (title, DOI, arXiv id, author, year, journal — the fields a verdict actually depends on).
Discovery differs from the LaTeX side for a reason: .yml is an ambiguous extension, and a bare scan would feed a GitHub Actions workflow to the verifier. So for Typst the tool reads the #bibliography(…) call out of the source — walking the #include graph, since a #bibliography in a back-matter.typ is a common layout — and falls back to a .bib scan only if none is declared. The report says which route it took.
For Typst projects it also lists cited-but-undefined keys, which render as broken ? references. LaTeX shouts about these at compile time; Typst's warning is easy to miss. Label cross-references (@fig-plot pointing at <fig-plot>) are excluded project-wide, so a well-labelled document does not report its own labels as missing citations.
🛠 Tools (29)
Orientation
Tool | Description |
| Format (Typst/LaTeX), compile root, file counts, declared bibliography. Cheap — no compile. Call this first. |
| All projects on the instance |
| Format, file counts, heading structure of the root |
Read
Tool | Description |
| List files, optionally filtered by extension |
| Read a file's contents |
| Regex search across every text file — one request regardless of project size |
| Heading structure with hierarchy and previews |
| One section's full text by title |
| Verify DOIs/arXiv ids against CrossRef & arXiv; BibTeX + Hayagriva |
Write
Tool | Description |
| New project, optionally with |
| New file; auto-creates parent folders |
| Surgical exact search-and-replace (like |
| Replace a file's entire contents |
| Replace a section's body, preserving its heading |
| Apply a coordinated change across several files, all-or-nothing |
| Upload a local binary (images, PDFs) |
| Delete a file |
| Switch the project's compiler ( |
History
Tool | Description |
| Commit log, filterable by file and date |
| Diff between refs or the working tree |
| Pull the latest changes |
Compile & output
Tool | Description |
| Trigger a compile; returns status + output files |
| Save the compiled PDF locally |
| Compile log — parsed Typst diagnostics, or the raw TeX log |
| Save the project source as a |
| Extract the project source into a directory |
Layout
Tool | Description |
| Total pages in the compiled PDF |
| Where a source line lands: page + rectangles |
| Every heading → its page, plus last-page fullness |
All writes commit and push immediately. Every tool is annotated with MCP safety hints (readOnlyHint / destructiveHint / idempotentHint), and a startup check refuses to run if any tool is left unclassified.
🔄 What changed from the upstream
overleaf-mcp-plus targets hosted overleaf.com and LaTeX. The retarget touched five areas:
New — Typst support
typst.py— heading, include/import, bibliography and citation parsing, matching Typleaf's own editor rulesdocument.py— extension-based dispatch so every tool serves both formats from one code pathtypst_log.py— parser fortypst compilediagnosticshayagriva.py—.ymlbibliography → BibTeX for the verifierlayout.py— a second backend using Typleaf's Typst sync map, with the SyncTeX path untouched for LaTeXNew tools:
project_info,set_compiler;create_projectgained acompilerargument
Self-hosting
TYPLEAF_BASE_URLis required, with no default (see above)The git bridge is on the instance's own origin at
/git/<id>, not a separategit.overleaf.comhostClone failures name the git-bridge module as a possible cause, with the token redacted from the URL
The local-copy sidecar records which instance a checkout came from, not just the project id
Writes that an open editor survives
Upstream replaces a file over HTTP. That works, but any collaborator with the file open gets "this file has gone out of sync" and loses their place. This fork edits through the real-time channel instead — a minimal ShareJS diff, so the change streams into open editors the way a human's typing does.
ot.py— minimal insert/delete components, emitted in reverse document order so every offset stays valid, and replayed locally before transmissionrealtime.py/polling.py— socket.io 0.9 clients (WebSocket and xhr-polling). Typleaf's git bridge is an optional module, so on an instance without it the real-time service is the only place folder and document ids existtransaction.py— multi-file edits validated up front and rolled back on partial failure, so a broken compile is not the way you learn a write failedsearch_files— regex search server-side, instead of listing and reading candidates one at a time
The awkward one is an encoding bug in Typleaf's real-time service: it serves
document lines decoded as Latin-1 while its own ShareJS offsets are over the
correctly-decoded string. Diff against the text as delivered and every offset
past the first non-ASCII character is wrong — inserts validate nothing and land
silently in the wrong place, deletes cannot match and the server drops the
connection. Pure-ASCII files are byte-identical either way and work perfectly,
which makes it look like a size or permissions fault. ot.decode_doc_text
undoes the rendering; it is idempotent, so it stays correct if the instance is
ever fixed to serve UTF-8.
Bug fixes carried into the fork
download_logwas unreachable upstream: a bad merge left its return statements orphaned insidesection_page_map, so the tool fell through toUnknown tool: download_logThe credential guard now reports a missing base URL rather than only the missing cookie
Kept as-is — git client, LaTeX parser, SyncTeX engine, thread-safe per-project locking, MCP SDK v2 handler registration, tool safety annotations.
🧪 Development
git clone <this repo> && cd typleaf-mcp
python -m venv .venv && .venv/bin/pip install -e ".[compile]" pytest
.venv/bin/python -m pytest tests -q391 tests, no external network. tests/conftest.py points the package at a .invalid host, so any test that escapes its stubs fails with a DNS error instead of reaching a live server.
tests/test_integration_fake_instance.py runs a stand-in Typleaf on a real socket and asserts on the requests it receives — that the session cookie is sent, that writes carry the CSRF token, that per-build output URLs carry ?clsiserverid, that sync/code gets a plain project path, and that no .synctex.gz is ever requested for a Typst project. Those are the wire details a mocked httpx cannot cover, and where this package's bugs historically live. It is the slow part of the suite (~18s) because each call opens a fresh connection.
tests/test_verify_citations.py skips unless tofu-search is installed; the discovery and reporting logic this fork adds is covered separately in tests/test_verify_discovery.py, which has no such dependency.
📄 License
MIT. Not affiliated with Overleaf, Inc., Digital Science, or the Typst project. Typleaf Pro is an independent community project.
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceEnables access to Overleaf LaTeX projects through Git integration, allowing users to read files, analyze document structure, extract sections, and manage multiple projects through natural language commands.177MIT
- AlicenseNot gradedqualityCmaintenanceEnables Claude and AI agents to read and edit Overleaf documents in real time, with support for project listing, document manipulation, LaTeX compilation, and live collaboration.1038MIT
- FlicenseBqualityCmaintenanceEnables AI agents to interact with Overleaf projects directly, including creating projects, managing files, and editing documents in real-time using Overleaf's native Operational Transformation protocol.10
- AlicenseNot gradedqualityBmaintenanceConnects Claude/ChatGPT to Overleaf projects via the Git integration, enabling read, edit, write, and file management through natural language commands.2AGPL 3.0
Related MCP Connectors
Edit your Overleaf LaTeX projects from Claude and ChatGPT; every change is a real Git commit.
Connect AI assistants to GitHub - manage repos, issues, PRs, and workflows through natural language.
Connect AI assistants to your GitHub-hosted Obsidian vault to seamlessly access, search, and analy…
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/superzeldalink/typleaf-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server