overleaf-mcp
# overleaf-mcp
An MCP server for [Overleaf](https://www.overleaf.com) that lets AI assistants
(Claude, Cursor, etc.) read, edit, and compile your LaTeX projects directly —
no more copy-pasting between the editor and your terminal.
**No paid plan required.** Overleaf's official Git integration is locked
behind a paid subscription. This project instead authenticates the same way
your browser does — with a session cookie — and drives the same HTTP and
realtime endpoints the Overleaf web app itself uses. It works on the free
plan.
## ⚠️ Before you use this
- This talks to **undocumented, unofficial endpoints**, including Overleaf's
legacy Socket.IO realtime channel (used to resolve folder ids for
uploads/deletes). It can break without notice if Overleaf changes its
backend.
- Your session cookie is as powerful as your password for the duration of
the session. Treat it like a secret: it's read from an environment
variable and never leaves your machine, but don't paste it anywhere else.
- This is not affiliated with or endorsed by Overleaf/Writefull. Use it at
your own risk and in line with Overleaf's Terms of Service.
## Install
```bash
git clone https://github.com/NiccoloSalvini/overleaf-mcp
cd overleaf-mcp
uv sync
```
## Get your session cookie
1. Log into [overleaf.com](https://www.overleaf.com) in your browser.
2. Open devtools → **Application** (Chrome) or **Storage** (Firefox) →
Cookies → `https://www.overleaf.com`.
3. Copy the value of `overleaf_session2` (and `GCLB` if present).
4. Build a `Cookie:` header string:
```
overleaf_session2=<value>; GCLB=<value>
```
Easiest way in Chrome: open the **Network** tab, reload the dashboard,
click any request to overleaf.com, and copy the full `Cookie` request
header from Headers → Request Headers.
The cookie expires (Overleaf sessions typically last a few weeks). When
tools start failing with an authentication error, repeat these steps and
update the environment variable.
## Configure
Set the cookie as an environment variable:
```bash
export OVERLEAF_COOKIE="overleaf_session2=...; GCLB=..."
```
### Claude Code
```bash
claude mcp add overleaf --env OVERLEAF_COOKIE="overleaf_session2=...; GCLB=..." -- uv --directory /path/to/overleaf-mcp run overleaf-mcp
```
### Claude Desktop (`claude_desktop_config.json`)
```json
{
"mcpServers": {
"overleaf": {
"command": "uv",
"args": ["--directory", "/path/to/overleaf-mcp", "run", "overleaf-mcp"],
"env": {
"OVERLEAF_COOKIE": "overleaf_session2=...; GCLB=..."
}
}
}
}
```
## Tools
| Tool | Description | Status |
|---|---|---|
| `overleaf_whoami` | Check whether `OVERLEAF_COOKIE` is set and still valid | ✅ verified live |
| `overleaf_list_projects` | List your projects (id, name, owner, last updated) | ✅ verified live |
| `overleaf_pull` | Download a project and extract it into a local directory | ✅ verified live |
| `overleaf_compile` | Trigger a compile, return status and error log | ✅ verified live |
| `overleaf_download_pdf` | Compile and download the resulting PDF | ✅ verified live |
| `overleaf_push_file` | Upload/overwrite a single file, creating remote folders as needed | ✅ verified live |
| `overleaf_push_dir` | Diff a local directory against the project and push changed files | ✅ verified live (calls push_file) |
| `overleaf_delete_file` | Delete a file from a project | ✅ verified live |
Every tool that takes a `project` argument accepts either the 24-character
project id or the exact project name.
All eight tools were live-tested 2026-07-19 against a real account, including
create/upload/verify/delete round trips on disposable test projects.
`overleaf_list_projects`/`overleaf_pull` needed a fix (Overleaf renamed a
meta tag). `overleaf_push_file` needed a bigger one: black-box guessing at
the upload request shape (field names, `folder_id` vs `parent_folder_id`,
with/without `qqtotalfilesize`) consistently got `422 invalid_filename`, even
with a correct folder id read out of Overleaf's own React state. The actual
cause, found by reading Overleaf's own source
(`services/web/app/src/Features/Uploads/ProjectUploadController.mjs` in
[overleaf/overleaf](https://github.com/overleaf/overleaf), AGPL): the
filename comes from a multipart **form field** `name`, not from a
`qqfilename` **query string** param the way the legacy Fine Uploader-era
endpoint mapping (and every third-party client that copied it, including the
original version of this one) assumed. `folder_id` and `_csrf` genuinely are
query params — only the filename field was wrong. Fixed in `client.py`.
`overleaf_push_file`, `overleaf_push_dir`, and `overleaf_delete_file` first
join Overleaf's realtime channel to resolve folder ids (the only way to get
them without the paid Git API) — expect these to take a few seconds longer,
and to be the first thing that breaks if Overleaf changes its backend.
## How it works
- **List / pull**: the Overleaf dashboard (`GET /project`) embeds the
project list as JSON in a `<meta name="ol-prefetchedProjectsBlob">` tag,
and a CSRF token in `<meta name="ol-csrfToken">`. Projects download as a
zip from `GET /project/{id}/download/zip`. Both are plain HTTP + HTML
parsing.
- **Push / delete**: uploading or deleting a file requires the internal
folder id, which Overleaf only exposes over its realtime channel (a
`joinProject` Socket.IO event, using Overleaf's legacy pre-1.0 Socket.IO
protocol). This project joins that channel, reads the returned folder
tree, and then calls the same REST upload/delete endpoints the editor
uses.
- **Compile**: `POST /project/{id}/compile` with a CSRF token, same as the
in-browser editor's build button.
Endpoint mapping originally traced from
[moritzgloeckl/overleaf-sync](https://github.com/moritzgloeckl/overleaf-sync)
(MIT licensed), reimplemented here with `httpx` and typed models.
## Development
```bash
uv sync --group dev
uv run pytest
uv run ruff check .
```
## License
MIT — see [LICENSE](LICENSE).
TDQS
Scored across 8 tools
Most tools serve distinct purposes, but overleaf_compile and overleaf_download_pdf both involve compilation, and overleaf_push_file / overleaf_push_dir are closely related. Descriptions clarify the differences, so ambiguity is low.
All tools share the overleaf_ prefix and use lowercase snake_case, but the pattern is not perfectly uniform: some are verb_object (delete_file, list_projects) while others are just verbs (compile, pull). This is mostly consistent and predictable.
With 8 tools, the server is well-scoped for its purpose, covering essential Overleaf operations without unnecessary redundancy. The count is appropriate for a focused integration.
Core workflows like pulling, pushing, compiling, and downloading PDFs are covered, but there is no way to create or delete projects, and no direct file listing (only via pull). These gaps may require workarounds.