Skip to main content
Glama
README.md
# ShareLaTeX MCP Server

An MCP (Model Context Protocol) server that gives Claude and other MCP clients access to **ShareLaTeX** projects over Git β€” read LaTeX files, analyze document structure, extract sections, and write changes back.

It targets **[ShareLaTeX @ TUM](https://sharelatex.tum.de)** (TUM's self-hosted Overleaf Server Pro instance) by default, and works with any other Server Pro deployment that has Git integration enabled β€” just set `SHARELATEX_HOST`.

> Forked from [mjyoo2/OverleafMCP](https://github.com/mjyoo2/OverleafMCP), which targets overleaf.com. This fork speaks the Server Pro URL shape (`https://<host>/git/<project_id>`) instead.

## Features

- πŸ“„ **Files**: list, read, write, append, and delete β€” with parent directories created on demand and binary files refused rather than mangled
- πŸ—ΊοΈ **Outline**: one document-order tree across the whole `\input`/`\include` graph, not just per file
- ✍️ **Targeted edits**: replace a section, or insert a new one before/after an anchor, leaving the rest of the file untouched
- βœ… **Validation without compiling**: unbalanced braces, unclosed environments, unknown citation keys, undefined labels, missing includes β€” no LaTeX installation needed
- πŸ“š **Bibliography merging**: import BibTeX by citation key, idempotently, so nothing already cited is ever lost
- πŸ•“ **History**: log, diff, and revert a file to any commit
- πŸ—οΈ **Multi-project**: several projects, even across instances

## Quick Start (recommended)

No clone, no `npm install`. Add this block to your Claude Desktop config and restart Claude Desktop.

**Config file location**

| OS      | Path |
|---------|------|
| Windows | `%APPDATA%\Claude\claude_desktop_config.json` |
| macOS   | `~/Library/Application Support/Claude/claude_desktop_config.json` |
| Linux   | `~/.config/claude/claude_desktop_config.json` |

**macOS / Linux**

```json
{
  "mcpServers": {
    "sharelatex": {
      "command": "npx",
      "args": ["-y", "github:vinconig/ShareLatexMCP"],
      "env": {
        "SHARELATEX_PROJECT_ID": "YOUR_PROJECT_ID",
        "SHARELATEX_GIT_TOKEN": "YOUR_GIT_TOKEN"
      }
    }
  }
}
```

**Windows** β€” Claude Desktop on Windows needs `cmd /c` to find `npx`:

```json
{
  "mcpServers": {
    "sharelatex": {
      "command": "cmd",
      "args": ["/c", "npx", "-y", "github:vinconig/ShareLatexMCP"],
      "env": {
        "SHARELATEX_PROJECT_ID": "YOUR_PROJECT_ID",
        "SHARELATEX_GIT_TOKEN": "YOUR_GIT_TOKEN"
      }
    }
  }
}
```

Restart Claude Desktop. The `sharelatex` tools should appear in the πŸ”§ menu.

The host defaults to `sharelatex.tum.de`, so TUM users need nothing beyond the project ID and token.

> **Updating**: nothing to do β€” `npx` re-resolves the branch against GitHub on every launch and reinstalls if the commit changed. Push to `master` and the next Claude Desktop restart runs it.
>
> The flip side is that each launch needs network access to GitHub. If you want the server to start offline (or faster), use the local clone from [Local Development](#local-development) instead and `git pull` when you want updates.

## Getting your credentials

1. **Project ID** β€” open the project in ShareLaTeX; the ID is the last segment of the URL: `https://sharelatex.tum.de/project/[PROJECT_ID]`
2. **Git Token** β€” ShareLaTeX β†’ Account Settings β†’ Git Integration β†’ "Create Token". The token is shown **once**; copy it immediately.

Sanity-check the pair from a terminal before wiring up Claude Desktop β€” this should prompt for a password (paste the token) and clone:

```bash
git clone https://git@sharelatex.tum.de/git/YOUR_PROJECT_ID
```

## Using another Server Pro instance

Set `SHARELATEX_HOST` alongside the other env vars:

```json
"env": {
  "SHARELATEX_PROJECT_ID": "...",
  "SHARELATEX_GIT_TOKEN": "...",
  "SHARELATEX_HOST": "sharelatex.example.edu"
}
```

A full URL is accepted too (`https://sharelatex.example.edu/`) β€” it is reduced to the bare hostname. The Git URL is always built as `https://<host>/git/<project_id>`.

In a multi-project `projects.json`, set `host` per project instead; it overrides `SHARELATEX_HOST` for that entry.

## Multi-Project Setup

The env-var Quick Start only handles a single project. For multiple projects, drop a `projects.json` file into the user config directory and skip the `env` block in your Claude Desktop config.

**File location**

| OS              | Path |
|-----------------|------|
| Windows         | `%APPDATA%\sharelatex-mcp\projects.json` |
| macOS / Linux   | `~/.config/sharelatex-mcp/projects.json` (or `$XDG_CONFIG_HOME/sharelatex-mcp/projects.json` if set) |

**File contents**

```json
{
  "projects": {
    "default": {
      "name": "Main Paper",
      "projectId": "...",
      "gitToken": "..."
    },
    "thesis": {
      "name": "My Thesis",
      "projectId": "...",
      "gitToken": "..."
    },
    "external": {
      "name": "Paper on another instance",
      "projectId": "...",
      "gitToken": "...",
      "host": "sharelatex.example.edu"
    }
  }
}
```

**Claude Desktop config** β€” same as Quick Start but no `env` block:

```json
{
  "mcpServers": {
    "sharelatex": {
      "command": "npx",
      "args": ["-y", "github:vinconig/ShareLatexMCP"]
    }
  }
}
```

(Add `cmd /c` on Windows, as in the Quick Start.)

Reference a specific project in tool calls with `projectName`:

```
Use read_file with filePath: "main.tex", projectName: "thesis"
```

If `projectName` is omitted, the `default` entry is used. To put `projects.json` somewhere other than the standard location, point `SHARELATEX_PROJECTS_CONFIG=/absolute/path/projects.json` at it from the `env` block.

## Configuration Reference

The server picks the **first** matching configuration source:

1. **Env vars (single project)** β€” `SHARELATEX_PROJECT_ID` + `SHARELATEX_GIT_TOKEN`. Optional: `SHARELATEX_PROJECT_NAME` for the display name.
2. **Token from a file** β€” set `SHARELATEX_PROJECT_ID` together with `SHARELATEX_GIT_TOKEN_FILE=/path/to/token.txt` (instead of `SHARELATEX_GIT_TOKEN`). Useful when you don't want the token in the Claude Desktop JSON. The file is read once at startup and any trailing whitespace/newline is trimmed.
3. **Multi-project file** β€” `SHARELATEX_PROJECTS_CONFIG=/absolute/path/projects.json`.
4. **User config dir** β€” `projects.json` in:
   - Windows: `%APPDATA%\sharelatex-mcp\projects.json`
   - macOS / Linux: `$XDG_CONFIG_HOME/sharelatex-mcp/projects.json` (defaults to `~/.config/sharelatex-mcp/projects.json`)
5. **Working directory** β€” `./projects.json`
6. **Package directory** β€” `projects.json` next to the server script (legacy, for clone-based installs).

`SHARELATEX_HOST` is independent of the above β€” it applies to every project that doesn't set its own `host`, and defaults to `sharelatex.tum.de`.

When env vars are set and a file is also present, env vars win and a notice is logged to stderr so the shadowing is visible.

### Environment variables

| Variable | Required | Purpose |
|---|---|---|
| `SHARELATEX_PROJECT_ID` | yes (single-project mode) | Project ID from the ShareLaTeX URL |
| `SHARELATEX_GIT_TOKEN` | yes, unless `..._FILE` is used | Git token from Account Settings |
| `SHARELATEX_GIT_TOKEN_FILE` | β€” | Path to a file containing the token |
| `SHARELATEX_HOST` | β€” | Instance hostname (default `sharelatex.tum.de`) |
| `SHARELATEX_PROJECT_NAME` | β€” | Display name for the single project |
| `SHARELATEX_PROJECTS_CONFIG` | β€” | Absolute path to a `projects.json` |
| `SHARELATEX_CONFIRM` | β€” | `all` (default), `destructive`, or `off` β€” see [Confirmation before writing](#confirmation-before-writing) |

## Local Development

### Option 1 β€” Run the cloned script directly

```bash
git clone https://github.com/vinconig/ShareLatexMCP.git
cd ShareLatexMCP
npm install
```

Then point Claude Desktop at the script and pass credentials via env vars (the same loader path the `npx` install uses):

```json
{
  "mcpServers": {
    "sharelatex": {
      "command": "node",
      "args": ["/absolute/path/to/ShareLatexMCP/sharelatex-mcp-server.js"],
      "env": {
        "SHARELATEX_PROJECT_ID": "...",
        "SHARELATEX_GIT_TOKEN": "..."
      }
    }
  }
}
```

On Windows, `args` should use `"C:\\Users\\you\\ShareLatexMCP\\sharelatex-mcp-server.js"`.

If you'd rather use a multi-project file:

```bash
cp projects.example.json projects.json   # then edit it
```

`projects.json` next to the script is the lowest-priority fallback, so this still works without env vars.

### Option 2 β€” Smoke-test the MCP protocol from the shell

No Claude Desktop required:

```bash
SHARELATEX_PROJECT_ID=... SHARELATEX_GIT_TOKEN=... node sharelatex-mcp-server.js
```

You should see `ShareLaTeX MCP server running on stdio` on stderr. The process stays open waiting for JSON-RPC on stdin; Ctrl+C to exit.

To drive a real tool call, pipe in an `initialize` handshake followed by a `tools/call`:

```bash
printf '%s\n' \
  '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"smoke","version":"0"}}}' \
  '{"jsonrpc":"2.0","method":"notifications/initialized"}' \
  '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"list_files","arguments":{}}}' \
| SHARELATEX_PROJECT_ID=... SHARELATEX_GIT_TOKEN=... node sharelatex-mcp-server.js
```

The working clone lives in your temp directory as `sharelatex-<project_id>`.

## Available Tools

All tools take an optional `projectName` (defaults to `"default"`). Every tool that writes takes a `commitMessage`.

### Reading and navigating

| Tool | Purpose |
|---|---|
| `list_projects` | List configured projects. |
| `list_files` | List files. Defaults to `.tex`; pass `extension: "*"` for everything, `includeBuild: true` to include `build/`. |
| `read_file` | Read a text file. Binary files (PDF, images) are rejected rather than returned as mojibake. Optional `maxBytes`. |
| `get_sections` | Sectioning commands in one file, with line numbers. |
| `get_outline` | **Document-order outline of the whole thesis**, following `\input`/`\include` recursively from `main.tex`. Use this instead of `get_sections` on `main.tex`, which contains only includes. |
| `get_section_content` | The content of one section by title. |
| `search_project` | Regex or literal search across files β†’ `file:line  text`. |
| `status_summary` | File counts by extension, chapter list, bibliography size, validation totals. |
| `validate_project` | See below. |

### Writing

| Tool | Purpose |
|---|---|
| `write_file` | Write a complete file. Creates parent directories. |
| `write_section` | Replace one existing section, leaving the rest of the file untouched. `occurrence` disambiguates repeated titles. |
| `insert_section` | Insert a **new** section `before`/`after` an anchor section, or at `end_of_file`. `write_section` cannot add sections that don't exist yet. |
| `append_to_file` | Append to a file, creating it if absent. |
| `delete_file` | Remove a file and push the deletion. |

### Bibliography

| Tool | Purpose |
|---|---|
| `list_bib_keys` | Citation keys already in the bibliography, with type and title. |
| `add_bib_entries` | Merge BibTeX **by citation key**. Existing keys are kept unless `overwrite: true`, so nothing already cited can be lost. Re-running with the same input is a no-op. |

### History

| Tool | Purpose |
|---|---|
| `file_history` | Recent commits, optionally for one file. |
| `show_diff` | Diff the working tree against a commit (default `HEAD~1`). |
| `revert_file` | Restore a file from a commit and push that restoration. |

## Validation

`validate_project` checks the whole `\input` graph **without compiling** β€” no LaTeX installation required, so it works identically in Claude Desktop:

| Check | Severity |
|---|---|
| Unbalanced braces | error |
| `\begin{X}` / `\end{X}` mismatch | error |
| `\input`/`\include` target missing | error |
| Citation key not in `bibliography.bib` | warning |
| `\ref`/`\autoref` to an undefined label | warning |
| Duplicate `\label` | warning |
| Undefined glossary or acronym key | warning |

Comments are ignored, and the bodies of `verbatim`/`lstlisting`/`minted` blocks are excluded so code samples with unbalanced braces don't produce false alarms.

Every write goes through the same structural gate: a write whose result would have unbalanced braces or an unclosed environment is **refused before pushing**, with the offending line numbers. Warnings never block β€” you routinely cite a key moments before importing it β€” they come back in the tool response instead.

## Confirmation before writing

By default **every tool that writes stops and shows you what would change before anything is pushed.** Claude Desktop does prompt before each tool call, but that prompt shows what Claude is *sending* β€” never the text about to be *overwritten* β€” and it stops appearing once you click "Allow always for this chat".

The first call writes nothing and answers with a preview:

```
write_section would change chapters/01_introduction.tex β€” 123 lines now, 123 after (+0).

@@ -2,7 +2,7 @@
 \chapter{Introduction}\label{chapter:introduction}
-Use with pdfLaTeX and Biber.
+This chapter motivates the work and states the research question.

Nothing has been written. To apply it, re-call write_section with the same
arguments plus confirm: "4b77477b".
```

Say "go ahead" and Claude re-calls with the token. If the client supports MCP **elicitation**, the server uses a real prompt instead and resolves it inside the single call β€” it detects this at startup and logs which path it took.

The token is derived from the file's current content plus the proposed content, not stored. That makes staleness self-detecting: **if the file changed since the preview β€” you edited it in the web editor, say β€” the token no longer matches**, and you get a fresh preview rather than an approval silently landing on different text.

A write whose result is byte-identical to what's already there skips the whole exchange.

| `SHARELATEX_CONFIRM` | Behaviour |
|---|---|
| `all` | *(default)* every writing tool confirms |
| `destructive` | only `delete_file` and `revert_file` |
| `off` | writes push immediately; relies on the client's own prompt |

```json
"env": {
  "SHARELATEX_PROJECT_ID": "...",
  "SHARELATEX_GIT_TOKEN": "...",
  "SHARELATEX_CONFIRM": "destructive"
}
```

An unrecognised value falls back to `all` with a warning on stderr β€” a typo must never quietly disable the safety net.

## Thesis workflows

### Citations, without exporting `.bib` by hand

If you also have a Zotero MCP configured in the same client, the manual "export from Zotero, upload to the web editor" loop disappears:

1. Find sources with the Zotero MCP (`zotero_semantic_search`, `zotero_advanced_search`).
2. `zotero_export_bibliography(item_keys=[...], export_format='bibtex')` β†’ raw BibTeX.
3. `add_bib_entries` merges it by key and pushes. Nothing already present is touched.
4. Cite it. Check `list_bib_keys` first if you're unsure what's already there.
5. `validate_project` confirms every `\cite` key resolves.

Two things to get right up front: `zotero_export_bibliography` renders through Zotero's **web API** even in local mode, so it needs `ZOTERO_API_KEY` and `ZOTERO_LIBRARY_ID`; and citation keys must be **stable** (BetterBibTeX with pinned keys), or citations silently rot when keys drift.

### Figures and tables

This server writes UTF-8 β€” it cannot push a PNG. That is a better fit for LaTeX than it sounds: write plots as **pgfplots code plus a CSV**, both plain text, both pushable:

```latex
\begin{figure}[htpb]
  \centering
  \begin{tikzpicture}
    \begin{axis}[xlabel={Epoch}, ylabel={Accuracy}]
      \addplot table[x=epoch, y=acc, col sep=comma] {figures/results.csv};
    \end{axis}
  \end{tikzpicture}
  \caption{Validation accuracy.}\label{fig:accuracy}
\end{figure}
```

Vector output, reproducible, diffable, and it regenerates when the numbers change β€” unlike an exported bitmap. Tables work the same way via `pgfplotstable`, or as `booktabs` markup. Genuinely binary assets (screenshots, photographs) still have to go through the web editor.

### Conventions that survive both Claude Desktop and Claude Code

Put a `CONVENTIONS.md` **in the LaTeX project itself** and start writing sessions with "read CONVENTIONS.md first". It travels with the project and is readable through `read_file` anywhere β€” unlike client-specific configuration. Worth encoding: which citation and cross-reference commands your template uses, label prefixes, table style, and **one sentence per line**, which is what makes `show_diff` and section rewrites reviewable.

### Safety

Avoid leaving the ShareLaTeX web editor open on a project while writing through the MCP β€” the editor auto-commits, which shows up here as a rejected push. After a substantial rewrite, `show_diff` then `revert_file` if it went wrong.

## Usage Examples

```
# List all projects
Use the list_projects tool

# Get project overview
Use status_summary tool

# Read main.tex file
Use read_file with filePath: "main.tex"

# Get Introduction section
Use get_section_content with filePath: "main.tex" and sectionTitle: "Introduction"

# List all sections in a file
Use get_sections with filePath: "main.tex"

# Write the full content of a file to the project
Use write_file with filePath: "main.tex", content: "...", commitMessage: "..."

# Write the content of a specific section to the project
Use write_section with filePath: "main.tex", sectionTitle: "Introduction", newContent: "\\section{Introduction}\n...", commitMessage: "..."
```

## Security Notes

- The Git token grants full read/write access to your project β€” treat it like a password.
- The token is passed to `git` through a credential helper reading it from the process environment. It never appears on a command line and is never written into the temp clone's `.git/config`.
- Prefer `SHARELATEX_GIT_TOKEN_FILE` over inlining the token in the Claude Desktop JSON if your config file is backed up or synced.
- `projects.json` is `.gitignore`d in this repo. Never commit real project IDs or Git tokens.
- File paths supplied through MCP tool calls are restricted to the cloned project directory; `..` traversal and absolute paths are rejected.

## License

MIT β€” see [LICENSE](LICENSE). Original work Β© mjyoo2.