Development¶
Local verification¶
npm install
npm run check # tsc --noEmit
npm run lint # eslint
npm test # vitest, no network needed
npm run build # emits dist/
npm pack --dry-run # verifies the published file list
Every push and pull request runs the same steps in CI on Node 20 and 24. Releases run them again before publishing.
Unit and deterministic integration tests cover revision identity, Unicode positions, section
parsing, tracked and untracked OT operations, history normalization, update limits, queue and
cache behaviour, Socket.IO frames, timeout recovery, comment attachment, file-tree events, MCP
registration, progress notifications, and the server instructions on both MCP protocol eras.
Folder sync is tested against an in-memory project: git blob hashes against git hash-object
values, ignore rules and symbolic links, plan comparison and folder collapsing, an upload failing
midway (no delete runs, and the returned token resumes), a remote or local change between plan and
sync (REMOTE_DRIFT, nothing applied), and a collaborator's edit during the sync
(REVISION_CONFLICT for that file only).
Other tests guard the shape of the code. test/server.test.ts compares tools/list with the
committed snapshot in test/__snapshots__/list-tools.json, so a change to a tool's name,
description, schema, or annotations shows up in review; update it with npx vitest run -u only
for a change listed in CHANGELOG.md. test/contracts/operations.test.ts checks every
operation's annotations against its effects. The access policy is tested through a real runtime
over an injected fetcher and connection factory, counting requests, and error sanitization by
feeding a sentinel string through every upstream channel and checking no serialized error holds
it.
Layout and boundaries¶
src/contracts/ defines every operation once, src/service/ shapes arguments for the domain
engine in src/overleaf/, src/protocol/, and src/http/, and src/mcp/ is one adapter over the
service; the internals page describes the layers. ESLint enforces the
boundary: only src/mcp/, src/server.ts, src/cli.ts, and src/index.ts may import the MCP
SDK or the adapter, type imports included, so src/sdk.ts, the overleaf-web-mcp/core entry,
works without it. console is refused everywhere in src/, and process.stdout and
process.stderr outside src/cli.ts and src/server.ts. CI imports both built entry points by
package name after the build.
Live tests¶
Live tests are disabled by default and must target a disposable project you own:
RUN_OVERLEAF_LIVE_TESTS=1 \
OVERLEAF_LIVE_TEST_PROJECT_ID=0123456789abcdef01234567 \
npm test -- test/live
Add RUN_OVERLEAF_LIVE_REVIEW_TESTS=1 for review reads,
RUN_OVERLEAF_LIVE_TRACKED_WRITE_TESTS=1 for a disposable tracked file create and delete,
RUN_OVERLEAF_LIVE_HISTORY_TESTS=1 for read-only history normalization, or
RUN_OVERLEAF_LIVE_LIFECYCLE_TESTS=1 to create a throwaway project named mcp-lifecycle-<time>,
set its root document, compile it once, and trash it, or
RUN_OVERLEAF_LIVE_SYNC_TESTS=1 to create a throwaway project named mcp-sync-<time>, mirror a
temporary local folder into it with plan_sync and sync_directory, check that a second plan
finds nothing to do, and trash it. Neither test deletes a project
permanently; remove the trashed project by hand from the web UI's Trashed view. Feature
availability depends on the deployment and account. Keep request volume low and treat cleanup
failures as test failures.
Documentation site¶
The site is built with MkDocs Material from
docs/ and deployed to GitHub Pages by the Docs workflow on every push to main that touches
the docs, ROADMAP.md, or CHANGELOG.md.
pip install mkdocs-material
mkdocs serve # live preview at http://127.0.0.1:8000
mkdocs build --strict # what CI runs; broken links fail the build
docs/roadmap.md and docs/changelog.md include the root ROADMAP.md and CHANGELOG.md
through snippets, so edit the root files. Use absolute URLs for cross-links inside them so they
work on GitHub, npm, and the site alike.
Download badge¶
The npm count badge (labelled installs) in the README and on the site's home page reads npm-downloads.json from
the badges branch through a shields.io endpoint badge. The Download count workflow
(.github/workflows/download-count.yml) refreshes it every Monday and can be run by hand: it runs
scripts/npm-downloads.ts, which sums the package's all-time downloads from npm's downloads API,
and replaces the branch's only commit when the number changed. npm cuts any range longer than 18
months to its last 18 months without saying so, so the script asks for one year at a time from
the day the package was created and fails, leaving the badge as it was, if an answer covers less
than it asked for. Never commit to the badges branch by hand; the next run replaces it.
Releasing¶
- Update
CHANGELOG.md. Set the same version inpackage.jsonandpackage-lock.json, for example withnpm version X.Y.Z --no-git-tag-version, inSERVER_VERSIONinsrc/version.ts, and in bothversionfields ofserver.json. - Merge to
mainwith CI green. -
Publish, in either of two ways. Both run the
Publish to npmworkflow, which checks that the tag equals the package version and thatCHANGELOG.mdhas a section for it, re-runs check, lint, test, and build, and publishes to npm with trusted publishing. No npm token is stored in the repository.- From the Actions tab: run
Publish to npmonmainwith the version, for example0.4.0(orgh workflow run publish.yml --ref main -f version=0.4.0). After publishing, it creates thevX.Y.Ztag on the published commit and the GitHub Release, titledvX.Y.Z, with that version'sCHANGELOG.mdsection as its notes. - From a GitHub Release: create a Release with tag
vX.Y.Zyourself; publishing it starts the workflow on that tag.
Once the package is on npm, the workflow also publishes
server.jsonto the MCP Registry, signing in with GitHub OIDC.Re-running a publish that already reached npm skips the upload, and one already in the MCP Registry skips that too, so a run that failed after publishing can be re-run to create whatever is missing.
- From the Actions tab: run
For coding agents¶
AGENTS.md in the
repository root states the rules that must keep holding, the test expectations, and the release
steps in a form meant for an agent working on the code. CLAUDE.md imports it for Claude Code.
Current exclusions¶
Git workflows, collaborator and sharing administration, account and billing settings, chat, background history watching, backward history pagination, version diffs and restoration, label mutation, and editing or deleting individual comment messages are outside the current release. Private API compatibility is version-specific and maintained on a best-effort basis. See the roadmap for what is planned.