Internals¶
How the server talks to Overleaf, and the guarantees that follow from it. Read this if you are debugging, contributing, or deciding whether to trust a particular behaviour.
Connection model¶
Two channels are used, both through the same saved web session:
- Private REST endpoints, the ones the Overleaf editor and dashboard call, for the project list, project creation, cloning, zip import, renaming, trash and archive state, project settings, the file tree operations, uploads, downloads, compiles, comment threads, and history. Every route is listed in the private API catalogue.
- Socket.IO 0.9 with operational transformation (OT) for reading and writing document text. The server implements the Socket.IO 0.9 wire format used by the targeted Overleaf client family and rejects unsupported protocol versions at project bootstrap.
No Overleaf Git integration is involved.
Layers¶
Every operation is defined once and reached through one engine, whichever interface calls it.
- Contracts (
src/contracts/): for each operation, its input and output schemas, its description, its MCP annotations, and its effects, plus the error codes and theOperationContextan interface passes in (progress, cancellation, diagnostics). Tests check that the annotations agree with the effects and that every destructive operation takes a confirm value or an expected state. - Service (
src/service/operations.ts):createOverleafService(runtime)returns one function per operation. Each one shapes its arguments for the domain engine and nothing else; the MCP handlers insrc/mcp/tools.tsmake one service call each. - Domain engine (
src/overleaf/,src/protocol/,src/http/): every public method checks the access policy for the project and each effect it performs before any request, so a direct call through the exported runtime is held to the same rules as a tool call. The per-project queue, revision checks, write verification, and timeout recovery live here.
The engine is published without MCP as overleaf-web-mcp/core, which exports OverleafRuntime,
readConfig, createOverleafService, McpError, ERROR_CODES, and the contract types, and
never loads the MCP SDK. It enforces the same defaults as the MCP server; v0.7.0 documents its
stable surface.
import { createOverleafService, OverleafRuntime, readConfig } from 'overleaf-web-mcp/core'
const runtime = await OverleafRuntime.create(readConfig())
try {
const service = createOverleafService(runtime)
const { content, revision } = await service.read_file({ projectId, filePath: 'main.tex' })
} finally {
await runtime.close()
}
Protocol and reliability notes¶
- ShareJS text OT and history-OT are normalized behind one document interface. Tracked ShareJS writes carry the authenticated author in update metadata; tracked history-OT writes carry author and timestamp metadata on inserted and retained-deletion components.
- Visible history-OT offsets account for tracked deletions retained in the raw snapshot.
- At most two project sockets are cached by default. Active sockets are never evicted, and idle sockets disconnect after 90 seconds.
- All document sessions and tree mutations in a project share one FIFO queue, because Overleaf's join/leave epoch is socket-wide. A document is joined for one queued operation and then left.
- A write succeeds only after acknowledgement, a matching
otUpdateApplied, a leave and rejoin, and content-hash verification of the live document. - If a write times out, the intended hash proves success, the unchanged original revision proves the write was not applied, and any third observable state is reported as a conflict. The write is never submitted again automatically.
- A comment is created as a REST thread and then attached through OT. Timed-out attachment recovery checks the new thread id and exact range; orphan cleanup happens only after the unchanged document proves attachment did not apply.
- A timed-out reply is accepted only when current author, exact normalized content, and the request-time window identify the refreshed message.
- ShareJS comment status uses the dedicated REST action. History-OT comment status is part of the document operation and snapshot.
- Folder sync is a composition, not a second implementation:
sync_directorycalls the same document write, upload, folder creation, and delete that the single-file tools use, one at a time through the project queue, and each upload or delete re-opens the project so the next step sees a fresh tree. A plan token is a small base64url JSON object holding three truncated SHA-256 digests: of the arguments that decide the scope, of every project entity in scope (path, type, id, and binary hash or document version and content hash), and of every local file's blob hash and folder. Nothing is stored on the server between calls, so a token survives a restart; after a sync, the digest covers what it changed as re-read from the tree and everything else as planned. batch_uploadis the same kind of composition: one tree read, the folder creation and upload thatsync_directoryuses, and one read-back that confirms each upload and classifies a timed-out one without sending it again. It stops at the firstRATE_LIMITED, since Overleaf counts uploads per project and would refuse the rest.download_project_zipstreams the archive to a temporary file beside the target and moves it into place, never holding the whole archive in memory. It keeps the last 64 KB as it goes, the most a zip end record and its comment can span, and checks for that record there, because Overleaf builds the archive while sending it and a transfer cut short still ends with HTTP 200.- History monitoring reads one 25-group update window, strips email fields, and keeps no cursor or background state on the server.
Protocol fixtures under test/fixtures/protocol are sanitized: cookies, user data, project and
document ids, and document content are removed.
How comment locations are resolved¶
Thread messages, authors, and resolution state come from /project/:id/threads. When the
deployment exposes /project/:id/ranges, that project-wide index identifies which documents
contain the filtered threads, and only those documents are joined to compute line and column
positions and quoted context.
If a usable project-wide range index is unavailable, a project-wide list_comments call returns
threads with positionsUnavailable: true; it never scans every document silently. Supplying
filePath joins only that document and resolves its ShareJS ranges or history-OT comment state.
Threads without a document range are returned as unlocated.
The discussion record and the source range are separate Overleaf objects. The thread endpoint provides messages; live document state provides attachment and status.
Related projects¶
Representative rather than exhaustive; capabilities change over time. The README's comparison table checks each capability against the source of the most widely used servers.
| Implementation | Connection model | Focus |
|---|---|---|
| This project | Browser-assisted saved session plus private REST and Socket.IO/OT | Project and file management, tracked writing, compilation, review, recent history |
@netique/overleaf-mcp |
Browser session plus private REST and Socket.IO/OT | A close web/OT peer with review comments and tracked-change workflows |
overleaf-mcp-rt |
Session authentication plus native OT | Real-time file, compile, and comment tooling for www.overleaf.com and Community Edition |
olcli |
Session cookie plus private REST | A command-line client with an MCP mode: whole-file uploads, compilation, comments, and local-folder diffs |
OverleafMCP, overleaf-mcp-server, vibeTeX |
Overleaf Git bridge | Git-backed synchronization, editing, and history |
The Git-bridge servers need Overleaf's Git integration, a paid feature on www.overleaf.com, and
work on the repository rather than the live document, so their edits bypass tracked changes and
revision checks entirely. Web-session peers such as @netique/overleaf-mcp share this project's
connection model and also offer tracked changes; what sets this project apart is the project
lifecycle tools, the verified-write guarantees described above, and folder sync that, given the
planToken from plan_sync, stops when either side drifted from the plan. The
roadmap plans a command line and a TypeScript client
over the same engine overleaf-web-mcp/core already exposes, tracked-change review, and
multi-file documents.
Review-range investigation was informed by Overleaf Comment Exporter. Real-time protocol behaviour was informed by Overleaf Workshop.