overleaf-forge 2.7.1 → 2.7.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +51 -3
- package/overleaf-mcp-server.js +8 -2
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -10,6 +10,27 @@ A [Model Context Protocol](https://modelcontextprotocol.io) server that lets an
|
|
|
10
10
|
|
|
11
11
|
Editing a LaTeX project through an AI normally means one of two bad options: paste files back and forth by hand, or let the model overwrite whole files and hope it didn't clobber an edit you made in the browser. This server removes both. It treats Overleaf's git remote as the source of truth and gives the model **anchored edits** (replace an exact string, refuse if it moved), a **conflict gate** (a stale or concurrent change is detected, never silently overwritten), and a **build verdict** (compile from scratch, PASS only on zero errors and zero undefined references), so an editing session is safe to run unattended and provable when it's done.
|
|
12
12
|
|
|
13
|
+
## Token economy
|
|
14
|
+
|
|
15
|
+
Safety is one motivation; keeping a large document out of the model's context window is the other, and it drove most of the tool design. A 50 KB chapter is about 12K to 13K tokens, so the cost of a naive workflow is dominated by moving that whole file in and out.
|
|
16
|
+
|
|
17
|
+
Rough token cost per operation on such a chapter, and the cut each tool buys:
|
|
18
|
+
|
|
19
|
+
| Operation (on a ~50 KB chapter) | Whole-file workflow | overleaf-forge | Reduction |
|
|
20
|
+
| --- | --- | --- | --- |
|
|
21
|
+
| One surgical edit | ~25K (read + write the file) | ~0.15K (`edit_file`) | ~99% |
|
|
22
|
+
| A dozen edits (one revision pass) | ~170K | ~3K | ~98% |
|
|
23
|
+
| One compile check | ~1.5K (raw `latexmk` log) | ~0.02K (`verify_build` verdict) | ~99% |
|
|
24
|
+
| Locating a passage | ~13K (read the whole file) | ~2K (`get_section_content` / `search_text`) | ~85% |
|
|
25
|
+
|
|
26
|
+
Figures are order-of-magnitude, for a chapter this size; the absolute numbers scale with file size, the percentages roughly hold.
|
|
27
|
+
|
|
28
|
+
- **Anchored edits instead of whole-file rewrites.** Changing one phrase by reading the whole file and writing it back costs roughly 25K tokens per edit: the file into context, then the file back out as the write payload. `edit_file` sends only the old and new strings and returns a one-line confirmation, on the order of 100 tokens. Across a dozen edits to a single chapter that is the difference between roughly 170K tokens and 3K.
|
|
29
|
+
- **A one-line build verdict instead of a raw log.** `verify_build` returns `✓ PASS — 24 pages` rather than the `latexmk` output. A raw log runs to hundreds or thousands of tokens per compile, and a multi-pass log buries the true final state under transient undefined-reference warnings from early passes (the exact trap `verify_build` classifies away by reading the final log). Over a session of repeated compiles that is a few thousand tokens against a few dozen.
|
|
30
|
+
- **Section and grep reads instead of the whole file.** `get_section_content` returns one section and `search_text` returns the matching lines, so locating something costs 1K to 3K tokens rather than the full 13K.
|
|
31
|
+
|
|
32
|
+
For an iterative edit, build, and review loop on a large document the tool traffic runs about an order of magnitude lighter than a read-and-rewrite-the-whole-file approach. The gain is workflow-dependent: a single full-file rewrite is a wash, since `write_file` moves the same bytes either way. It is the repeated, surgical work that compounds, which is exactly the shape of writing and revising a paper.
|
|
33
|
+
|
|
13
34
|
## Features
|
|
14
35
|
|
|
15
36
|
- **Surgical, conflict-safe edits**: `edit_file` replaces an exact anchor and refuses if the region changed on Overleaf; non-overlapping concurrent edits auto-merge via git.
|
|
@@ -33,14 +54,17 @@ Editing a LaTeX project through an AI normally means one of two bad options: pas
|
|
|
33
54
|
|
|
34
55
|
## Install
|
|
35
56
|
|
|
36
|
-
|
|
57
|
+
**Recommended: npx.** Nothing to clone, nothing to keep updated by hand. The whole install is a few lines of JSON in your MCP client, plus two values from Overleaf.
|
|
58
|
+
|
|
59
|
+
1. Get your two values: an Overleaf **git token** (Account Settings → Git Integration → create token) and your **project id** (the `<ID>` in `https://www.overleaf.com/project/<ID>`).
|
|
60
|
+
2. Add this block to your client's config file (locations in the table below):
|
|
37
61
|
|
|
38
62
|
```json
|
|
39
63
|
{
|
|
40
64
|
"mcpServers": {
|
|
41
65
|
"overleaf": {
|
|
42
66
|
"command": "npx",
|
|
43
|
-
"args": ["-y", "overleaf-forge"],
|
|
67
|
+
"args": ["-y", "overleaf-forge@latest"],
|
|
44
68
|
"env": {
|
|
45
69
|
"OVERLEAF_GIT_TOKEN": "olp_xxxxxxxxxxxxxxxxxxxxxxxx",
|
|
46
70
|
"OVERLEAF_PROJECT_ID": "0123456789abcdef01234567"
|
|
@@ -50,7 +74,17 @@ For most clients the whole install is a few lines of JSON: point the client at t
|
|
|
50
74
|
}
|
|
51
75
|
```
|
|
52
76
|
|
|
53
|
-
|
|
77
|
+
3. Restart the client (or reload its MCP servers). That's it.
|
|
78
|
+
|
|
79
|
+
`npx` fetches and runs the published package on demand. The token and project id are the entire setup for a single project, with no config file (this is **env-only mode**). `@latest` means each client restart picks up the newest published version automatically; pin `overleaf-forge@2.7.1` instead to freeze a version. For multiple projects, per-project contexts, or the SSA bootstrap, see [Configuration](#configuration).
|
|
80
|
+
|
|
81
|
+
An MCP server is not an app you launch yourself: the client starts it as a subprocess, so "installing" it just means making its command available to the client. The `npx` form above needs no install step. If you would rather have a real command on your `PATH`, install it globally:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
npm install -g overleaf-forge
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
and set `"command": "overleaf-forge"` with no `args` (keep the same `env` block). A global install does not auto-update: refresh it yourself with `npm update -g overleaf-forge`. `npx` is recommended precisely because it skips that step.
|
|
54
88
|
|
|
55
89
|
To hack on the server instead, run it from source:
|
|
56
90
|
|
|
@@ -75,6 +109,20 @@ The `mcpServers` schema is identical across clients; only the file location diff
|
|
|
75
109
|
|
|
76
110
|
Restart the client (or reload its MCP servers) after editing, so it spawns the server with the new config.
|
|
77
111
|
|
|
112
|
+
## Updating
|
|
113
|
+
|
|
114
|
+
**As a user.** With `overleaf-forge@latest` in your config (the recommended form), restart the client or reload its MCP servers and it fetches the newest published version. If you pinned a version (`overleaf-forge@2.7.1`), change the number. If `npx` seems to keep running an old version, clear its cache with `npx clear-npx-cache` and restart. If you installed globally instead, update with `npm update -g overleaf-forge`.
|
|
115
|
+
|
|
116
|
+
**As the maintainer (publishing a new release).** From the repository:
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
npm version patch # or minor / major; bumps package.json and tags
|
|
120
|
+
npm publish # enter your npm 2FA one-time code when prompted
|
|
121
|
+
git push --follow-tags # push the commit and the version tag
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Anyone on `@latest` picks the new version up on their next client restart.
|
|
125
|
+
|
|
78
126
|
## Configuration
|
|
79
127
|
|
|
80
128
|
Two modes, smallest first.
|
package/overleaf-mcp-server.js
CHANGED
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
ListToolsRequestSchema,
|
|
8
8
|
} from '@modelcontextprotocol/sdk/types.js';
|
|
9
9
|
import { readFile, writeFile, access, mkdir, readdir, stat, rm, rename, copyFile } from 'fs/promises';
|
|
10
|
-
import { existsSync, realpathSync } from 'fs';
|
|
10
|
+
import { existsSync, realpathSync, readFileSync } from 'fs';
|
|
11
11
|
import { promisify } from 'util';
|
|
12
12
|
import { execFile as execFileCallback } from 'child_process';
|
|
13
13
|
import path from 'path';
|
|
@@ -30,6 +30,12 @@ const execFile = promisify(execFileCallback);
|
|
|
30
30
|
// cache. Anything a user edits resolves user-copy-first, bundled-default-last.
|
|
31
31
|
const PACKAGE_DIR = __dirname;
|
|
32
32
|
|
|
33
|
+
// Single source of truth for the version: read it from the shipped package.json
|
|
34
|
+
// so `npm version` is the only place a release number changes. Fall back to '0.0.0'
|
|
35
|
+
// if package.json is somehow unreadable (never fatal).
|
|
36
|
+
let PKG_VERSION = '0.0.0';
|
|
37
|
+
try { PKG_VERSION = JSON.parse(readFileSync(path.join(PACKAGE_DIR, 'package.json'), 'utf-8')).version || PKG_VERSION; } catch { /* keep fallback */ }
|
|
38
|
+
|
|
33
39
|
// Expand a leading ~ to the user's home. Plain join elsewhere assumes absolute.
|
|
34
40
|
function expandHome(p) {
|
|
35
41
|
if (!p) return p;
|
|
@@ -927,7 +933,7 @@ async function readContext(projectKey, project) {
|
|
|
927
933
|
|
|
928
934
|
// MCP server
|
|
929
935
|
const server = new Server(
|
|
930
|
-
{ name: 'overleaf-forge', version:
|
|
936
|
+
{ name: 'overleaf-forge', version: PKG_VERSION },
|
|
931
937
|
{ capabilities: { tools: {} } }
|
|
932
938
|
);
|
|
933
939
|
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "overleaf-forge",
|
|
3
|
-
"version": "2.7.
|
|
3
|
+
"version": "2.7.3",
|
|
4
4
|
"description": "MCP server to read, edit, compile, and verify Overleaf/LaTeX projects over git: conflict-safe edits, figure upload, clean-build verification, citation and voice linting.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "overleaf-mcp-server.js",
|
|
7
7
|
"bin": {
|
|
8
|
-
"overleaf-forge": "
|
|
8
|
+
"overleaf-forge": "overleaf-mcp-server.js"
|
|
9
9
|
},
|
|
10
10
|
"files": [
|
|
11
11
|
"overleaf-mcp-server.js",
|