Repository navigation
Releases: DanielHou315/overleaf-mcp-rt
Release list
v2.2.0
Projects that Overleaf has moved to its newer document format (history-OT) no longer fail to open: they can be read, and edited if you opt in. Nothing changes for classic projects — which today is every project most people have.
Added
- Projects on Overleaf's newer document format (history-OT) can be read, and — opt-in — edited. Overleaf is migrating projects (
otMigrationStage> 0) from the ShareJS text type to the operation format of its history system; until now such a project's documents could not be opened at all ("client does not support history-ot"). The engine now detects the format per document, declares support when joining, converts edits to and from text operations at the socket, and predicts the server's handling of an in-flight edit with a port of Overleaf's own algorithm for that format — so live co-editing,<external-changes>, write guards and everything else behave as on classic projects. Writes to such documents are off by default (HISTORY_OT_WRITES_DISABLED, nothing sent; enable withOVERLEAF_HISTORY_OT_WRITES=1): a write the server rejects disconnects everyone in the document, and the format could only be verified against Community Edition, not overleaf.com. The first write to each such document is checked against a fresh server snapshot; a difference raisesHISTORY_OT_MISMATCHand stops further writes for the session.overleaf_add_commentrefuses on such documents (COMMENTS_UNSUPPORTED, before creating a thread): anchoring uses a different operation that could not be verified. Verified live against Community Edition 6.0.1 and 6.3.0 with a project switched to the format; no migrated project on overleaf.com was available to test against.
Fixed
- Nothing user-visible, but worth recording: the randomized co-editing test that guards the core "never desync a human" fix used a one-line LCG whose low bits are stuck at zero in JavaScript, so it generated almost no collaborator edits. It now uses a proper PRNG and asserts that its scenario really contains races; the engine passes unchanged. The same check caught that the classic and history-OT transforms disagree on some overlaps, which is why history-OT got its own.
Testing
- The live suite runs every scenario against both protocols on releases that have history-OT (the matrix switches a project in the throw-away instance's Mongo), and gained a scenario where two clients fire overlapping replacements at the same version and must converge.
v2.1.0
Installable in Codex, verified against real servers, and five bugs fewer. The new live test suite runs the built server against throw-away Overleaf Community Edition instances (4.2, 5.5, 6.0, 6.3) and against overleaf.com; everything under Fixed below marked as found by it was invisible to the unit tests.
Added
- Codex plugin.
.codex-plugin/plugin.jsonmakes the repository installable withcodex plugin marketplace add DanielHou315/overleaf-mcp-rt+codex plugin add overleaf-mcp-rt@overleaf-mcp-rt: the MCP server, the four skills, and the two commands (which Codex turns into skills). Codex could already install from the Claude Code manifest, but it does not expand the plugin-root variable in MCP arguments, so the server silently never started.
Changed
- Supported Community Edition range is now 4.x – 6.x (was declared as 3.x – 6.x). The new version matrix showed that 3.x never worked: its real-time service predates the join-on-connect handshake this client uses, so the connection never completes (tested with 3.5.13). 4.2, 5.5, 6.0 and 6.3 pass the full live suite.
Fixed
Found by the new live test suite running against real Overleaf servers:
- A tool call could hang for a minute when the real-time connection never completes (a reverse proxy that doesn't forward
/socket.io, or an unsupported Overleaf 3.x). The project handshake now times out after 20 s with aNETWORK_ERRORthat says what to check; reconnect attempts are bounded the same way. - Writing an emoji could later get everyone disconnected from the doc. Overleaf cannot store characters outside the Basic Multilingual Plane: its document-updater rewrites every UTF-16 surrogate in inserted text to U+FFFD, and only acknowledges the sender without telling it. The engine kept the original characters in its snapshot, so its next delete across that text did not match the server's and was rejected — and Overleaf answers a rejected op by disconnecting every client on the doc. The engine now applies the same rewrite before sending, so both sides always agree, and
overleaf_edit_doc/overleaf_write_docadd anotesentry telling the agent which characters could not be stored. overleaf_read_compile_log/overleaf_download_pdfright afteroverleaf_compilefailed with "No log produced". Overleaf allows one compile per project per second and answers a second one withtoo-recently-compiledand no output files. Compiles now wait out that window once and retry; they are also no longer flagged as editor auto-compiles (auto_compile=true), which the server throttles per user and server-wide; and the error names the compile status when there really is no log.overleaf_read_compile_logandoverleaf_download_pdfreturned 404 on overleaf.com. A build's output only exists on the compile server that produced it; downloads now carry theclsiserveridandcompileGroupfrom the compile response, as the editor does. Output served from a separate download domain is fetched without the session cookie or proxy headers, which belong to the Overleaf origin only.
Also:
diagnoseno longer prints⚠ reverse-proxy — CF detected but no extra headers configuredfor an instance that merely sits behind a CDN and works. A proxy is now only mentioned when it is actually in the way: a redirect to a sign-in page on another host, or a 401 / 403 / challenge on/project, is reported asPROXY_AUTH_FAILED(previously a generic error or a misleadingOVERLEAF_AUTH_FAILED) with a hint aboutlogin --header/OVERLEAF_EXTRA_HEADERS; and an OT handshake that fails after REST succeeded points at WebSocket forwarding.
Documentation
- README leads with support for both self-hosted Overleaf (Community Edition / Server Pro) and overleaf.com, with a new Supported Overleaf servers table and a short recording of an agent and a person editing the same file.
Testing
- Live test suite and Community Edition version matrix (
test/live/).run-matrix.shstarts a throw-away Overleaf CE for each supported release line (4.2, 5.5, 6.0, 6.3), runs the built server against it over stdio — tools, string edits with non-ASCII text, an agent editing while a second client types in the same line, write guards, uploads, compile, comments — checks the server's own logs for OT errors, and removes every container, volume, network and pulled image. The instances publish no ports and live on an internal Docker network. The same suite runs against a configured host (LIVE_HOST=overleaf.com npm run test:live). Replaces the oldtest/integrationscaffold, which published a port and needed a hand-made account.
v2.0.0
First release since 1.1.1. (1.2.0 was prepared but never published; its changes are included here.) A major version because agent-visible behaviour changed in ways that can break existing prompts and integrations — see the next section.
The theme: the server is now safe to use while a human edits the same document in the browser, it works against overleaf.com as well as self-hosted instances, and editing works the way coding agents edit files.
Breaking changes — migrating from 1.x
overleaf_write_doccan now refuse. It returnsDOC_NOT_READfor a non-empty doc the agent hasn't read this session andDOC_CHANGED_EXTERNALLYif a collaborator edited it since. Migrate: read first, or switch tooverleaf_edit_doc; passoverwrite: trueto get the 1.x behaviour.- Multi-edit calls are sequential. Edits in one
overleaf_edit_doccall now apply in order, each to the result of the previous; in 1.x they all resolved against the original text. Migrate: anold_string/findmust match the text as it stands after the earlier edits in the same call. overleaf_edit_docadvertises a new schema —{old_string, new_string, replace_all?}. The 1.xmode-based edits are still accepted at runtime, but clients that validate arguments against the published schema must move to the new shape.replace_linesandraw_opsare refused withDOC_CHANGED_EXTERNALLYif the doc changed since the agent last saw it.- Tool results may carry a second text block,
<external-changes>…</external-changes>, after the JSON. Migrate: parse the first content block as JSON rather than concatenating all blocks. OT_VERSION_DRIFTis never emitted, and new error codes exist (EDIT_NO_MATCH,EDIT_AMBIGUOUS,DOC_CHANGED_EXTERNALLY,DOC_NOT_READ,COMMENTS_UNSUPPORTED). "Anchor not found / ambiguous" used to beOVERLEAF_GENERIC.- The server no longer exits on a bad or expired session. It starts and reports
OVERLEAF_AUTH_FAILEDper tool call. Supervisors that relied on the exit code to detect auth failure should call a tool (ordiagnose) instead. - Credentials file v2.
~/.config/overleaf-mcp-rt/credentials.jsonis rewritten as{default, hosts}by the nextlogin. 2.x reads the 1.x file; 1.x cannot read the 2.x file, so don't mix versions against one config directory (OVERLEAF_CREDENTIALS_FILEcan separate them). - No project-level
.mcp.jsonin the repository — the repo root is now a plugin. If you ran the server from a checkout via that file, install the plugin from the checkout instead (README → Developing).
Fixed
- Agent edits knocking browser sessions "out of sync". The OT engine ignored collaborators'
otUpdateAppliedbroadcasts, so once a human typed, the agent's next op was computed against stale text and submitted at a stale version. document-updater rejected it (Delete component … does not match), and Overleaf's real-time service answers a rejected op by sendingotUpdateErrorto — and disconnecting — every client in the doc, discarding the human's unsaved keystrokes. The engine now applies every remote op to a live snapshot, transforms its own in-flight op past ops that beat it to the server (a port of the ShareJStexttype document-updater itself runs, so both sides compute the same text), and evaluates edits against the live text in the same tick the op is emitted. - Write confirmation. The
applyOtUpdateack only means the op was queued. The engine now waits for the real confirmation (otUpdateApplied {doc, v}) and surfacesotUpdateErrorrejections, which were previously invisible. - Parallel reads. real-time fails a
joinDocthat another join overtakes; joins are now serialized, time out instead of wedging the queue, and updates that arrive before a join's response are replayed onto the snapshot. - The server starts even when the session has expired. Auth is checked on the first tool call and reported as
OVERLEAF_AUTH_FAILEDwith a hint, instead of the process exiting before the MCP handshake (which hosts show as an unexplained "connection closed"). Logging in again fixes a running server without a restart. - The version reported in the MCP handshake and
--helpwas hard-coded (1.0.0); it now comes frompackage.json.
Added
overleaf_edit_docby exact string replacement —{old_string, new_string, replace_all?}:old_stringmust match exactly one place, edits apply sequentially and atomically, a whitespace/indentation/re-wrap-tolerant fallback is accepted when unambiguous, the op sent is the minimal character diff (collaborators' cursors elsewhere are undisturbed), and the result includes a unified diff. Failures report the closest matching region.- External-change awareness — every tool result (success or error) carries an
<external-changes>block — diff, author, age, plus file-tree events — when collaborators changed something the agent has already seen. Reported once; the agent's own edits never are. New tooloverleaf_check_changesfor polling. - Multiple hosts — one server can be logged in to several Overleaf instances. Every tool takes an optional
host; new tooloverleaf_list_hosts; newhostsCLI command;--hostforlsanddiagnose. Credentials file v2 ({default, hosts}); the v1 file is still read and is upgraded by the nextlogin. - overleaf.com support for projects on the classic OT pipeline, verified against production. The server fetches the load-balancer stickiness cookie (
GCLB) so the Socket.IO handshake and websocket reach the same backend. - Browser login —
login --browserlaunches an installed Chromium-family browser with a throwaway profile, lets you sign in normally (CAPTCHA, SSO, 2FA), and captures the session over the DevTools protocol. No prompts when--urland--browserare both given.OVERLEAF_BROWSERpicks the binary. - Friendlier cookie login — accepts a bare value or
name=valueand detectsoverleaf_session2/overleaf.sid/sharelatex.sid. - Review-panel comments (overleaf.com and Server Pro) —
overleaf_list_comments,overleaf_add_comment,overleaf_reply_comment,overleaf_resolve_comment. Comments are posted through the logged-in account, so every agent comment ends withCo-authored by <agent name>; the server enforces this (agentNameis required), withomitSignaturefor when the user explicitly opts out. Stock Community Edition has no review panel: these tools fail up front withCOMMENTS_UNSUPPORTEDand change nothing. - The repository is an installable agent plugin for Claude Code, Cursor and Codex: dual manifests (
.claude-plugin/,.cursor-plugin/), sharedskills/andcommands/, and the MCP server wired in. Four concise, model-neutral skills (overleaf-setup,overleaf-editing,overleaf-latex-workflow,overleaf-comments) and two commands (/overleaf-login,/overleaf-status).overleaf-mcp-rt skills install [--target <dir>]copies the skills for any other harness. - MCP server
instructions, giving every connecting agent the collaboration and signature rules. overleaf_read_docreturns the docversion.--versionflag. CI and tag-driven release workflows;scripts/release-prep.mjssets the version everywhere in one step.OVERLEAF_CREDENTIALS_FILErelocates the credentials file.- New error codes:
EDIT_NO_MATCH,EDIT_AMBIGUOUS,DOC_CHANGED_EXTERNALLY,DOC_NOT_READ,COMMENTS_UNSUPPORTED. - Overleaf CE 6.x is supported and is now the primary target (3.x – 6.x declared).
- Live-testing helpers:
scripts/agent-session.mjs,scripts/latency-probe.mjs,scripts/smoke-stdio.mjs.
Changed
overleaf_write_docsends only the differing characters instead of replacing the whole doc.unified_diffedits no longer send "delete everything, insert everything".- (See Breaking changes above for the behavioural changes to
overleaf_write_docandoverleaf_edit_doc.)
Known limitations
- overleaf.com projects migrated to Overleaf's history-OT format (
otMigrationStage> 0) cannot be read or edited yet;joinDocis refused cleanly by the server. REST-backed tools still work. - Comments are unavailable on stock Community Edition (the thread API ships only in Server Pro).
v1.0.0 — First public release
overleaf-mcp-rt is a Model Context Protocol server that connects AI coding agents (Claude Code, Claude Desktop, Codex, Cursor, …) to a self-hosted Overleaf Community Edition instance.
The -rt is for real-time: edits flow through Overleaf's native operational-transform pipeline as live operations from a connected collaborator account — no "file changed externally" toast, and no dependency on git-bridge or Server Pro.
Install
// MCP client config
{
\"mcpServers\": {
\"overleaf-rt\": {
\"command\": \"npx\",
\"args\": [\"-y\", \"overleaf-mcp-rt@latest\"],
\"env\": {
\"OVERLEAF_URL\": \"https://overleaf.example.com\",
\"OVERLEAF_SESSION_COOKIE\": \"overleaf_session2=s%3A...\"
}
}
}
}Or globally:
```bash
npm install -g overleaf-mcp-rt
overleaf-mcp-rt diagnose
```
Requires Node.js ≥ 20.
Capabilities (15 tools)
Read / navigate
- `list_projects`, `get_project_tree`, `read_doc`, `read_file` (returns native MCP image / PDF / text content)
Write (live OT, no toast)
- `write_doc` — replace a text doc; flows as OT ops
- `apply_patch` — emit raw `[{p,i?,d?}]` OT ops for surgical edits
Compile
- `compile`, `read_compile_log`, `download_pdf` (native PDF resource)
Tree CRUD (REST)
- `create_doc`, `create_folder`, `upload_file` (mimeType inferred from path), `rename`, `move`, `delete_entity`
Diagnostics
`overleaf-mcp-rt diagnose` runs a stepped probe:
```
✓ config — URL https://overleaf.example.com
✓ REST handshake — cookie valid, CSRF scraped
✓ reverse-proxy — CF detected, extraHeaders configured
✓ project listing — 3 project(s) accessible
✓ OT handshake — publicId P.abc...
```
A ✗ on any step prints the underlying error code (`OVERLEAF_AUTH_FAILED`, `PROXY_AUTH_FAILED`, `PROJECT_ACCESS_DENIED`, …) so you know which layer to fix.
Reverse-proxy auth
`OVERLEAF_EXTRA_HEADERS` is merged into both REST requests and the Socket.IO handshake. README has worked examples for Cloudflare Access, HTTP Basic, Authelia / oauth2-proxy / forward-auth, and Tailscale / VPN.
Compatibility
- Stock Overleaf Community Edition 3.x – 5.x
- No fork of `sharelatex/sharelatex` required
- No Server Pro features used
How v1.0 came together
Built incrementally over five focused milestones, each gated by TDD and two-stage subagent review (spec compliance + code quality):
| Phase | Theme |
|---|---|
| v0.1 | Read-only via REST + project-zip cache |
| v0.2 | OT-live reads & writes via ported Overleaf-Workshop Socket.IO client; `socket.io-client` patched at install for reverse-proxy header pass-through |
| v0.3 | REST tree mutations: `create_doc`, `create_folder`, `upload_file`, `rename`, `move`, `delete_entity` |
| v0.4 | Polish: WHATWG URL normalization, `ProjectAccessDeniedError` mapping, per-doc write serialization, reconnect jitter + give-up signaling, `pdfDownloadDomain` consumption, `upload_file` MIME inference, stepped `diagnose` subcommand |
| v1.0 | npm publish prep: `files` allowlist, `prepublishOnly` build+test guard, registry metadata, package rename to `overleaf-mcp-rt` |
Test suite grew from 0 → 158 passing unit tests over the arc.
Acknowledgements
Significant portions of the auth and OT code are ported from Overleaf-Workshop by iamhyc and contributors, used under AGPL-3.0.
License
AGPL-3.0-or-later.