Skip to main content
Glama
overleaf-mcp

overleaf-mcp

by deepghs

Overleaf MCP by deepghs

Collaborative LaTeX editing through MCP: native tracked changes, review comments, compilation, downloads, and file management in one connection.

Based on netique/overleaf-mcp, with its Git history and AGPL license preserved. This version adds ten tools, for 27 tools total. Document edits use Overleaf's Socket.IO OT protocol rather than whole-file uploads or the Git bridge. This is an unofficial integration, not an Overleaf-supported API.

What This Version Adds

  • Log in to several Overleaf servers at once (overleaf.com plus any number of self-hosted instances) and pick one per list_projects / open_project call.

  • Keep the file tree in sync from the server's real-time broadcasts instead of re-joining the project after every file-management call.

  • Download document snapshots, binary assets, project ZIPs, PDFs, and compile logs.

  • Create empty documents and folders, upload new assets, rename and delete files.

  • Create native comments on unique selected text, with version checks and message/anchor readback verification.

  • Preserve the upstream tracked OT pathway for existing document text.

  • Fix self-hosted compilation requests that reject a null root document path.

Source installation only. npx @netique/overleaf-mcp runs upstream, not this enhanced version. This repository is not published to npm; build it locally.

Related MCP server: overleaf-paper-mcp

Requirements

  • Node.js 20.18.1 or newer and npm (Node 22 LTS recommended).

  • A reachable Overleaf deployment and an authorized project account.

  • Server support for tracked changes and comments to use those features; self-hosting alone does not guarantee they are enabled.

  • A supported Chromium-family browser for interactive login.

Installation

git clone https://github.com/deepghs/overleaf-mcp.git
cd overleaf-mcp
npm ci
npm run build
npm test

If the repository is private, authenticate Git with an account that has access. Keep the checkout at a stable path. Rebuild after pulling changes and restart your MCP client to load the new code.

Authentication

SSH / Headless Servers

For self-hosted instances with ordinary email/password login:

export OL_BASE_URL=https://overleaf.example.org
node dist/index.js login --password
# Or prefill the email only:
node dist/index.js login --email user@example.org
node dist/index.js status

The interactive terminal prompts for email and a hidden password. --password is a mode switch, NOT a password argument. Never put passwords on the command line. Only the validated session cookie is saved; passwords are not persisted. Failed login leaves any existing saved session unchanged. Use ssh -t when your SSH invocation does not allocate a terminal.

On Linux without DISPLAY/WAYLAND_DISPLAY, plain login selects password mode automatically. Set OL_HEADLESS=1 to disable automatic browser login explicitly. Missing/expired credentials during MCP calls produce instructions to log in from SSH; the MCP stdio stream is never used for password prompts.

This requires HTTPS and a server that accepts password login without CAPTCHA, SSO or 2FA. It does not bypass those challenges. Session expiry still requires another interactive login; no stored password or automatic password renewal. For interactive browser authentication use login --browser on a desktop.

Desktop Browser Login

For a self-hosted instance, use the same origin for login and MCP configuration:

OL_BASE_URL=https://overleaf.example.org node dist/index.js login
OL_BASE_URL=https://overleaf.example.org node dist/index.js status

For hosted Overleaf, omit OL_BASE_URL. Login opens an isolated browser profile; sign in there. Missing or expired credentials may trigger the same flow on a tool call. CSRF tokens are normally discovered automatically.

Cookies are plaintext in <configDir>/overleaf-mcp/cookie.json, with mode 0600 where supported. On Linux, configDir is $XDG_CONFIG_HOME or ~/.config; on macOS it is ~/Library/Application Support; on Windows it is %APPDATA%. Never commit this store or its dedicated browser profile.

Use a dedicated collaborator account for clear attribution and limited project access. Otherwise edits and comments use the authenticated human's identity.

Multiple Overleaf Servers

Cookies are stored per host, so one MCP process can be logged in to overleaf.com and any number of self-hosted instances at the same time. Every host with a stored cookie is a known server, alongside OL_BASE_URL (the default) and anything pre-declared in OL_SERVERS:

# Log in to each server once; --server takes a host or an origin URL.
node dist/index.js login --server overleaf.example.org
node dist/index.js login --server www.overleaf.com
node dist/index.js login --password --server lab.example.edu   # headless host
node dist/index.js status                                       # every known server
node dist/index.js logout --server overleaf.example.org

At run time, list_servers shows the known servers and which one the open project is on. list_projects without server queries every logged-in server and tags each project with its server; open_project accepts the same server argument and may omit it when the project was just listed or only one server is logged in. One project is open at a time, and every project-scoped tool acts on that project's server. Naming a server that has no stored cookie starts the login flow for it (a browser window on desktops; on headless hosts an error telling you which login --server command to run). A wrong server never logs you out: the real-time service answers with connectionRejected, which is reported as an API error, not treated as an expired cookie.

MCP Configuration

Codex

Replace the example origin and absolute path:

codex mcp add overleaf \
  --env OL_BASE_URL=https://overleaf.example.org \
  -- node /absolute/path/to/overleaf-mcp/dist/index.js

Equivalent TOML:

[mcp_servers.overleaf]
command = "node"
args = ["/absolute/path/to/overleaf-mcp/dist/index.js"]

[mcp_servers.overleaf.env]
OL_BASE_URL = "https://overleaf.example.org"

JSON-Based Clients

{
  "mcpServers": {
    "overleaf": {
      "command": "node",
      "args": ["/absolute/path/to/overleaf-mcp/dist/index.js"],
      "env": { "OL_BASE_URL": "https://overleaf.example.org" }
    }
  }
}

Use an absolute Node executable path if the client cannot resolve node. Restart the client after replacing a server configuration.

Tool Reference

Project-scoped tools use the project selected by open_project.

Group

Tools

Purpose

Discovery

ping, list_servers, list_projects, open_project, list_files

Inspect servers, projects and the live tree

Editing

read_file, edit_file, find_and_replace

Read text/version and submit minimal tracked OT edits

Review

list_tracked_changes, accept_changes, reject_changes

Inspect and review pending suggestions

Comments

list_comments, read_comment_thread, add_comment, reply_comment, resolve_comment, reopen_comment

Native review-panel threads

Compilation

compile, read_log

Remote build and diagnostics

Downloads

download_file, download_project, download_output

Local snapshots, ZIP, or compile artifacts

Files

create_file, create_folder, upload_file, rename_entity, delete_entity

Tree management without uploading over existing text

Added Tool Arguments

Tool

Required arguments

Constraints / optional arguments

list_servers

—

No network; default, OL_SERVERS and every cookie host

list_projects

—

Optional server (host or URL); omitted = every logged-in server

open_project

project_id

Optional server; needed only when several servers are logged in and the project was not just listed

download_file

path, output_path

Existing text or binary file

download_project

output_path

ZIP only, no extraction

download_output

output_path

Optional artifact, default output.pdf; compile first

create_file

path

Empty document; parent must exist

create_folder

path

Single folder; parent must exist

upload_file

path, local_path

New PNG/JPEG/GIF/WebP/PDF/EPS/ZIP assets only

rename_entity

path, new_name

Basename in the same parent; target must not exist

delete_entity

path, confirm

Requires confirm: true; refuses nonempty folders

add_comment

path, selected_text, content, expected_version

Unique text and the version from read_file

Remote paths are project-relative. local_path and output_path must be absolute. Downloads require an existing parent and never overwrite a destination, including a symlink at the destination path.

  1. Open the project and read the target document and relevant comments.

  2. Edit with track: "on", expected_version from the read, and strict_version: true when other writers are active.

  3. On stale-version rejection, read again and recompute the edit. Upstream refreshes its cache on rejection; blindly retrying old new_content against that cache can remove another writer's new text.

  4. Read back the result, compile, inspect diagnostics, and download the PDF.

  5. Reply to the relevant thread. Leave suggestions and threads pending until the author explicitly requests acceptance or resolution.

Example requests:

Improve the introduction as tracked changes. Preserve citations and factual claims. Re-read and recompute if the document version changes.

Add a comment on this unique sentence explaining the missing experimental detail. Do not change the document text.

Compile the open project and download output.pdf and output.log to these absolute paths without overwriting existing files.

Safety and Compatibility

  • No upload fallback for text. Uploads refuse existing targets and text files. Create empty documents, then insert their content through tracked OT.

  • The file tree follows the server's real-time broadcasts (reciveNewDoc, removeEntity, reciveEntityRename, ...), so collaborators' changes and your own file-management calls show up without re-joining the project, and cached document text survives them (a deleted doc is dropped from the cache). Each file-management call waits for its own broadcast and reports tree_sync: "event"; if none arrives within 5 s it re-joins the project once and reports tree_sync: "reconnect", which also clears the document cache. File-management calls are serialized with each other within one process, not with every upstream tool. Do not overlap them with project switching or text edits.

  • Remote filename checks are not atomic with server writes. Do not create the same filename simultaneously from different clients; cross-client races are not covered by the collision guard.

  • A comment message and its anchor are separate writes. Partial failures return a thread ID to inspect before retrying; creation is not transactional.

  • Verification covers the upstream ShareJS path, not history-OT compatibility. This fork does not add live cursors or complete browser presence behavior.

  • OT preserves independent operations, not semantic agreement about a sentence. Reconnection and every possible concurrency failure mode are not certified.

  • A PDF may be generated despite LaTeX errors. Inspect error_count and the log. A missing/unreadable log is not proof of a clean build, even if upstream reports built_cleanly.

  • Cookies grant account access. Use trusted MCP clients, authorized accounts, and automation consistent with the deployment's applicable terms.

Environment Variables

Variable

Purpose

OL_BASE_URL

Default server origin; https://www.overleaf.com if unset

OL_SERVERS

Extra servers to pre-declare (comma/space separated hosts or origins); hosts with a stored cookie are known without this

OL_HEADLESS

1 disables browser login; a missing cookie then fails with SSH instructions

OL_BROWSER

Explicit Chromium-family browser executable

OL_CSRF

Optional CSRF override

OL_MCP_LOG_LEVEL

debug, info, warn, error; logs go to stderr

OL_INSECURE

Browser-login certificate exception; avoid normally and do not assume it fixes Node TLS

Tests and Development

npm run typecheck
npm run build
npm test
git diff --check

On September 10, 2026, this version passed 25 unit tests and live MCP stdio tests against a self-hosted deployment. Coverage included tracked insertion, anchored comment readback, stale-version rejection, binary byte roundtrips, file-management protections, PDF/log/ZIP downloads, and two-client OT merging at different positions. This was not browser visual QA or exhaustive testing of every upstream tool or every Overleaf version.

On September 22, 2026, the multi-server and tree-sync changes passed 48 unit tests plus a live two-client run against a self-hosted deployment: eight file-management calls (create, rename, edit, delete across two clients) all confirmed through broadcasts with tree_sync: "event", the second client saw the first client's changes without re-opening, and the run used exactly one socket connection per client. Multi-server logic was exercised with one logged-in server and one without a cookie (aggregate listing, automatic server pick, fast failure on the cookie-less server without disturbing the open project). Two servers logged in simultaneously was covered by unit tests only.

The opt-in integration test modifies its supplied disposable project and leaves review evidence there. It imports an existing olcli credential into an isolated cookie store after checking its origin:

export OL_BASE_URL=https://overleaf.example.org
export OL_TEST_COOKIE_CONFIG=/absolute/path/to/olcli-nodejs/config.json
export XDG_CONFIG_HOME="$(mktemp -d)"
node tests/manual/extensions.mjs DISPOSABLE_PROJECT_ID

Protect that isolated directory: it contains authentication state and downloaded artifacts. Never run this test against a production manuscript.

Run npm audit before deployment. The inherited lockfile had dependency advisories during verification; this feature branch did not resolve them. No npm publication or production security certification is implied.

Troubleshooting

  • Missing tools: build this checkout and point the client to its entry point, not upstream npm or the olcli executable.

  • Login required: use the same OL_BASE_URL for login and MCP configuration.

  • Missing parent / existing destination: create parents first and choose a new filename; edit existing text through OT.

  • Output unavailable: compile the currently open project, then choose a filename listed in output_files.

  • No tracked suggestions: check project/user settings and server feature support; do not silently fall back to untracked uploads.

Attribution and License

AGPL-3.0-or-later, see LICENSE. Preserve notices and comply with the license when distributing or operating modifications.

The additions use existing server abstractions; no olcli implementation was copied. olcli credentials are only an optional input to the integration test.

Available Tools

26 tools
accept_changesAccept tracked changes by idA

Permanently accepts the specified tracked-change suggestions. The text stays in the doc; the pending-change metadata is cleared. Pass the change_id values from list_tracked_changes. Changes that span multiple docs are grouped automatically. Irreversible.

Inputs: change_ids (required).

add_commentA

Create a native anchored comment on unique selected text. expected_version must come from read_file. Refuses stale versions. Does not change document text.

Inputs: path (required), content (required), selected_text (required), expected_version (required).

compileCompile the open Overleaf projectA

Triggers a LaTeX compile on Overleaf's CLSI, then fetches output.log and returns a unified summary: status, whether a PDF was produced, error_count (parsed ! -prefixed log lines), warning_count, first error lines, output file list, timings. Note: Overleaf returns status:"success" even when LaTeX has errors (PDF is still generated under nonstopmode). The truthful 'did it build cleanly?' check is error_count === 0. Use read_log for the full log when more context is needed.

Inputs: draft, root_doc, stop_on_first_error.

create_fileA

Create an empty text document. Use edit_file for tracked content insertion.

Inputs: path (required).

create_folderA

Create one folder in the open project. Parent must exist.

Inputs: path (required).

delete_entityA
Destructive

Delete a file or folder. Requires explicit confirmation; recursive folder deletion is refused.

Inputs: path (required), confirm (required).

download_fileC

Download a document snapshot or binary file from the open project.

Inputs: path (required), output_path (required).

download_outputA

Download an artifact from the last compile. Run compile first. Defaults to output.pdf.

Inputs: output_path (required), artifact.

download_projectB

Download the open project as a ZIP archive (no extraction).

Inputs: output_path (required).

edit_fileEdit a .tex doc in the open Overleaf projectA

Replaces the contents of a doc by computing a minimal diff and submitting it as an OT operation over the live Socket.IO connection. The change lands in the web editor in real time. By default the edit appears as a pending suggestion in the Review panel (track:'on'); pass track:'off' to write directly. If path is omitted, defaults to the project's root doc. Only .tex / .bib / .md / similar text docs are editable — binary files are not.

Inputs: new_content (required), path, track, strict_version, +1 more.

find_and_replaceSurgical find-and-replace in an Overleaf docA

Replace one occurrence — or all, with replace_all: true — of old_string with new_string in a doc, without re-emitting the rest of the file. By default old_string must be unique; ambiguous matches are returned with line:column locations so you can extend the match. Submits the minimal OT operation through the same pathway as edit_file, so by default it lands as a pending suggestion in Overleaf's Review panel (track:'on'). If path is omitted, defaults to the project's root doc. USE WHEN: a SINGLE targeted edit (one typo, one label rename, one heading change) in a large doc — saves tokens vs. re-emitting the body and avoids accidental whitespace drift. AVOID FOR BATCH WORK: for multiple substitutions (e.g. converting many words, applying a style guide across a chapter) prefer ONE edit_file call with all changes computed client-side. Each find_and_replace is its own round-trip with its own race window, its own tracked-change entry, and its own cache-sync cycle — calling it N times for N small changes amplifies the failure modes that one batched edit_file would avoid.

Inputs: new_string (required), old_string (required), path, track, +3 more.

list_commentsList review-panel comment threadsA
Read-only

Returns all review-panel comment threads in the open project, sorted by most recently updated. Each entry includes the thread id, the doc path it's anchored on, the quoted text it refers to, the latest message preview, and whether it's resolved. Use read_comment_thread for full content.

Inputs: full, path_contains, include_resolved.

list_filesList files in the open Overleaf projectA
Read-onlyIdempotent

Returns the file tree of the currently open project as a flat list of project-relative paths. Cheap — uses cached data from open_project, no network. Each entity has a path (e.g. 'chapters/intro.tex'), an id, and a kind ('doc' | 'file' | 'folder').

Inputs: kind, path_contains.

list_projectsList Overleaf projectsA
Read-onlyIdempotent

Lists projects on the configured Overleaf account, sorted by most recently updated. Returns each project's id, name, last update time, and owner. Use the returned id with open_project to start working on a project.

Inputs: limit, name_contains, include_trashed, include_archived.

list_tracked_changesList pending tracked changes across the open projectA
Read-only

Enumerates every pending tracked-change suggestion in the open project (across all docs), with author name + email, doc path, op kind ('insert' | 'delete'), position, inserted/deleted text, and a stable change_id. Use this to plan an accept_changes or reject_changes call: filter the result by author / doc / kind / text, collect the matching change_id values, then pass them. Tracked changes only — review-panel comments are listed by list_comments.

Inputs: kind, limit, full_text, author_email, +3 more.

open_projectOpen Overleaf projectA

Joins the project's real-time Socket.IO session and caches its file tree. Must be called before list_files / read_file / edit_file. Switching projects automatically closes the previous session.

Inputs: project_id (required).

pingPingA
Read-onlyIdempotent

Health check. Returns 'pong' and the server version. Does not contact Overleaf.

Inputs: no named parameters.

read_comment_threadRead a single comment thread's messagesA
Read-only

Fetches the full message history of one thread (all replies with author + timestamp). Use this when list_comments shows a thread that looks relevant.

Inputs: thread_id (required).

read_fileRead a file from the open Overleaf projectA
Read-only

Reads the contents of a file by project-relative path. For text docs (.tex, .bib, .md, etc.) the full text appears in both the content array and structuredContent.text; metadata (version, line/byte counts, ranges, tracked-change/comment counts) is in structuredContent. For binary files (images, PDFs) base64 is in content and structuredContent.base64, with MIME type alongside.

Inputs: path.

read_logRead the last compile's output.logA
Read-only

Returns the full LaTeX log from the most recent compile call. compile already includes the error count + first few errors in its response — use this only when you need more context (full log, line numbers, package warnings, etc.). The content array shows a summary of !-prefixed error lines at the top, then the log truncated to the last 8000 chars for display. structuredContent.log contains the FULL untruncated log so programmatic callers can grep / parse it.

Inputs: no named parameters.

reject_changesReject tracked changes by idA

Rejects the specified tracked-change suggestions. For tracked inserts the inserted text is removed; for tracked deletes the strikethrough is cleared and the text stays. Implemented as an OT update with u:true flags on the inverse ops — same pathway Overleaf's web client uses. Pass the change_id values from list_tracked_changes. Irreversible.

Inputs: change_ids (required).

rename_entityA
Destructive

Rename a file or folder within its current parent; refuses existing targets.

Inputs: path (required), new_name (required).

reopen_commentReopen a resolved comment threadB

Reopens a previously-resolved thread.

Inputs: thread_id (required).

reply_commentPost a reply to a comment threadA

Adds a new message to an existing comment thread. Threads come from list_comments. The message appears immediately in Overleaf's review panel for collaborators.

Inputs: content (required), thread_id (required).

resolve_commentResolve a comment threadA

Marks a thread as resolved. Use after addressing the comment (e.g. by editing the doc and replying).

Inputs: thread_id (required).

upload_fileA

Upload a NEW binary asset only. Existing targets and text files are refused; use create_file and OT edits for text. Do not run concurrently with other tree changes.

Inputs: path (required), local_path (required).

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A4/5.0

    Scored across 26 tools

    Disambiguation5/5

    Each tool has a clearly distinct purpose. Comment operations (list, read, reply, resolve, reopen, add), tracked changes (list, accept, reject), file operations (create, read, edit, delete, rename, upload), downloads (file, project, output), and compile/log functions are all non-overlapping. Even similar-sounding tools like edit_file and find_and_replace are clearly differentiated.

    Naming Consistency5/5

    Tool names follow a consistent verb_noun pattern throughout (read_comment_thread, list_projects, open_project, list_files, read_file, edit_file, compile, read_log, list_comments, reply_comment, resolve_comment, reopen_comment, list_tracked_changes, accept_changes, reject_changes, download_file, download_project, download_output, create_folder, create_file, upload_file, rename_entity, delete_entity, add_comment). The slight deviation of find_and_replace and ping does not break the overall pattern.

    Tool Count4/5

    With 26 tools, this server is above the typical 3-15 range but is justified by the breadth of Overleaf's feature set. The tools cover project management, editing, comments, tracked changes, compilation, and downloads, each earning its place. It could be trimmed slightly (e.g., consolidating download_* tools) but is reasonably scoped.

    Completeness5/5

    The tool surface covers the full lifecycle of working with an Overleaf project: listing/opening projects, navigating and editing files, creating and managing entities, compiling and reading logs, handling comments and tracked changes, and downloading artifacts. No obvious gaps exist for typical agent workflows; the only missing operations would be administrative tasks like sharing or project deletion, which are out of scope for an MCP server.

    Maintenance

    ActivitySlowing
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers