Give any MCP-compatible AI assistant direct access to your Overleaf projects.
Overleaf has no public write API. The only programmatic access is through Git. OverleafMCP wraps that Git interface with 19 purpose-built tools, giving your AI assistant the ability to edit LaTeX documents the way a developer would: targeted replacements, not full-file rewrites.
OverleafMCP implements the Model Context Protocol and is compatible with any MCP client. It has been primarily tested with Claude.
- Read files, document structure, preamble, postamble, and individual sections from any configured Overleaf project
- Write entire files or replace individual sections with automatic commit and push
- Surgical edits via
str_replace,insert_before, andinsert_after: no full-file rewrites needed - BibTeX entry management: get, add, replace, and remove individual entries by cite key
- Git history and diff inspection
- Per-project read-only mode to protect published or archived projects from accidental writes
- Fine-grained tool permissions via
disallowedToolsat global and per-project level - In-process per-project locking: safe concurrent tool calls with no external dependencies
- Dirty-state recovery: if the server crashes mid-write, staged changes are committed on next startup
- Path traversal protection on all file operations
- No Redis, no Docker required
You can, but you lose context window space, lose the ability to make targeted edits without rewriting whole files, and lose git history. OverleafMCP keeps your document in Overleaf, edits it in place, and commits every change with a message.
Most existing implementations are read-only or only support full-file writes. OverleafMCP adds surgical edit tools (str_replace, insert_before, insert_after), BibTeX entry management, fine-grained per-project permissions, and dirty-state crash recovery.
- Node.js >= 18
- Git installed and available on
PATH - An Overleaf account with Git integration enabled (Overleaf premium feature)
git clone https://github.com/SemPlaatsman/OverleafMCP.git
cd OverleafMCP
npm install
cp projects.example.json projects.jsonEdit projects.json with your Overleaf credentials:
{
"projects": {
"my-paper": {
"name": "My Paper",
"projectId": "YOUR_OVERLEAF_PROJECT_ID",
"gitToken": "YOUR_OVERLEAF_GIT_TOKEN",
"readOnly": false
}
}
}Project ID: Open your project in Overleaf. The ID is in the URL:
https://www.overleaf.com/project/[PROJECT_ID]
Git token: Go to Overleaf Account Settings, then Git Integration, then create a token.
All options have sensible defaults and can be overridden with environment variables:
| Variable | Default | Description |
|---|---|---|
PROJECTS_FILE |
./projects.json |
Path to the projects config file |
OVERLEAF_TEMP_DIR |
./temp |
Directory for local git clones |
OVERLEAF_GIT_AUTHOR_NAME |
(git global config) | Git author name for commits |
OVERLEAF_GIT_AUTHOR_EMAIL |
(git global config) | Git author email for commits |
Setting OVERLEAF_GIT_AUTHOR_NAME and OVERLEAF_GIT_AUTHOR_EMAIL is recommended on environments where git is not globally configured, otherwise commits will fail.
OverleafMCP works with any MCP-compatible client. Add it to your client's configuration using the stdio transport:
{
"mcpServers": {
"overleaf": {
"command": "node",
"args": ["/absolute/path/to/OverleafMCP/overleaf-mcp-server.js"],
"env": {
"OVERLEAF_GIT_AUTHOR_NAME": "Your Name",
"OVERLEAF_GIT_AUTHOR_EMAIL": "you@example.com"
}
}
}
}Configuration file locations for common clients:
| Client | Configuration file |
|---|---|
| Claude Desktop (macOS) | ~/Library/Application Support/Claude/claude_desktop_config.json |
| Claude Desktop (Windows) | %APPDATA%\Claude\claude_desktop_config.json |
| Claude Desktop (Linux) | ~/.config/claude/claude_desktop_config.json |
| Cursor | .cursor/mcp.json in your project or ~/.cursor/mcp.json globally |
| Windsurf | ~/.codeium/windsurf/mcp_config.json |
Restart your client after saving the configuration.
Tools are grouped below by purpose. All write tools support an optional commitMessage, a push boolean (default true), and a dryRun boolean (default false) that validates inputs and reports sizes without writing anything.
list_projects
List all configured projects, including their readOnly status and disallowedTools.
list_files
List files in a project. Defaults to .tex files; pass extension to filter by a different extension (e.g. ".bib").
status_summary
High-level overview of a project: total file count, main file, and section structure.
read_file
Read the full contents of any file.
get_sections
Get all sectioning commands from a .tex file as a hierarchical tree. Each node includes its type (section, subsection, etc.), title, character offset, the text immediately following that heading (not including children), a 100-character preview, and a children array of nested sections. Supports all seven LaTeX levels including starred variants (\section*{}).
get_section_content
Get the full content of a specific named section. Supply the optional parentTitle parameter to disambiguate when the same section title appears under multiple parent sections.
get_preamble
Get everything before the first sectioning command: document class declaration, package imports, and custom command definitions. Returns the full file content if no sections exist. Only applicable to .tex files.
get_postamble
Get everything from \end{document} (inclusive) to the end of the file. Returns an empty string if \end{document} is absent (e.g. \input'd files). Note: bibliography commands (\bibliography{}, \printbibliography) appear before \end{document} and fall within the last section's content range. Use str_replace to edit them. Only applicable to .tex files.
list_history
Show recent git commits. Supports limit (default 20, max 200), filePath to filter by file, and since/until time filters (e.g. "2.weeks" or "2025-01-01").
get_diff
Get a unified diff. Defaults to all changes since the last commit (working tree vs HEAD). Supply fromRef and/or toRef to diff between specific commits or branches. Supports filePaths array, contextLines (default 3, max 10), and maxOutputChars (default 120000).
write_file
Overwrite an entire file. Use for new file creation or full-file replacements only. Prefer str_replace, insert_before, insert_after, or write_section for targeted edits.
write_section
Replace a single named section in a .tex file. Only the named section is replaced; everything else is untouched. The boundary is level-aware: the section ends where the next command of equal or higher level begins, or at \end{document} if there is none. Only applicable to .tex files.
str_replace
Replace the single unique occurrence of oldStr with newStr in any file. oldStr must match exactly once. If it matches zero or multiple locations, an error is returned with the occurrence count so you can add more surrounding context to make it unambiguous. Setting newStr to an empty string deletes oldStr. This is the preferred tool for targeted edits anywhere in a file, including the preamble and bibliography commands.
insert_before
Insert content immediately before the single unique occurrence of anchorStr. Same uniqueness rules as str_replace.
insert_after
Insert content immediately after the single unique occurrence of anchorStr. Same uniqueness rules as str_replace. Useful for appending a new \usepackage line after the last existing one.
All BibTeX tools operate on .bib files. The entry and newEntry parameters accept a complete raw BibTeX string of any entry type (@article, @book, @inproceedings, @misc, etc.).
get_bib_entry
Get the raw BibTeX block for a single cite key.
add_bib_entry
Append a new BibTeX entry. The cite key is extracted server-side. Returns an error if the cite key already exists, pointing you to replace_bib_entry instead.
replace_bib_entry
Replace the entry with the given cite key with a new raw BibTeX block. The replacement may use a different cite key if desired.
remove_bib_entry
Remove the entry with the given cite key. Surrounding whitespace is normalised to keep the file tidy.
Add multiple entries to projects.json and reference them by key in tool calls:
{
"defaults": {
"disallowedTools": []
},
"projects": {
"active-paper": {
"name": "Current Paper",
"projectId": "...",
"gitToken": "...",
"readOnly": false
},
"published-paper": {
"name": "Published Paper",
"projectId": "...",
"gitToken": "...",
"readOnly": true
}
}
}Then pass projectName: "active-paper" in any tool call to target a specific project.
- Single project configured:
projectNamecan be omitted; the server resolves the project automatically. - Multiple projects configured:
projectNamemust be supplied. If omitted, the server returns an error listing the available project keys. This is intentional: silently resolving to the wrong project on a write operation would be worse than an explicit error.
Setting "readOnly": true on a project allows all read operations but rejects any write operation with a clear error message. The default is false, so omitting the field has no effect. This is useful for protecting published or archived papers from accidental edits.
For more selective control, use disallowedTools to block specific tools rather than all writes. This works at two levels.
A defaults block at the top of projects.json sets the baseline for all projects:
{
"defaults": {
"disallowedTools": ["write_file", "remove_bib_entry"]
},
"projects": {
"my-paper": {
"name": "My Paper",
"projectId": "...",
"gitToken": "..."
}
}
}A per-project disallowedTools array overrides the global defaults entirely for that project:
{
"defaults": {
"disallowedTools": ["write_file"]
},
"projects": {
"my-paper": {
"name": "My Paper",
"projectId": "...",
"gitToken": "...",
"disallowedTools": []
}
}
}In this example the global default blocks write_file, but my-paper overrides it with an empty list, making all tools available.
Resolution order: readOnly: true takes precedence over everything and blocks all write tools. Then per-project disallowedTools is checked. If absent, it falls through to defaults.disallowedTools. If that is also absent, all tools are allowed. The valid tool names are: write_file, write_section, str_replace, insert_before, insert_after, add_bib_entry, replace_bib_entry, remove_bib_entry.
OverleafMCP has a two-layer test suite built on Node.js's built-in node:test module — no extra test dependencies required.
Unit tests cover pure logic: section and BibTeX parsing, path traversal protection, and anchor uniqueness. They run in under a second with no credentials or network access:
npm testIntegration tests run the full tool stack against a live Overleaf project via git, verifying every tool end-to-end including push verification via listHistory. They require a projects.json with writable and read-only test projects configured:
npm run test:integrationCI runs unit tests on every PR to dev and main. Integration tests run on PRs to main only.
See ATTRIBUTION.md for credits to the open-source projects that informed this work.
MIT. See LICENSE.