Changelog¶
All notable changes to this project are documented here. This project follows Semantic Versioning. Until 1.0.0, tool schemas and result shapes may change in a minor or patch release; each such change is listed below.
0.6.1 - 2026-10-20¶
A refreshed README and package description, and a session deadline that no longer drifts. No tool was added, removed, or changed in schema or result shape; the server still registers 31 tools.
Changed¶
- The npm package and the documentation site describe the server by what it does. The note that it is an independent project, not affiliated with or endorsed by Overleaf, moves to one line at the end of the README, and the site footer keeps its note. The Terms-of-Service and account risk caution becomes a short note under Get started and on the site's home page.
- The npm download-count badge, an all-time total refreshed weekly, is labelled
installs. - Dependencies are refreshed within their declared ranges, which clears every
npm auditfinding; all were in development-only packages, and the published package had none.
Fixed¶
- The session deadline is dated from when Overleaf set the cookie. Overleaf sets its session
cookie with
Max-Age, which the cookie library counted from the cookie's latest use rather than from its arrival, sosessionExpiresAtinauth_statusandkeepalivemoved later with every request and could report a deadline after the real one. The server now fixes the deadline when the cookie arrives, and the jar file and both reports agree to the second.
Documentation¶
- The README has a navigation row, one badge style with CI status and the supported clients, and a dark-theme version of the hero image, which the site's home page also uses. The tool table, the configuration for other clients, and the comparison's notes are collapsible.
- Codex is listed among the supported clients, with its
codex mcp addcommand in the README and a Codex tab in the installation guide. The README notes that VS Code uses aserverskey.
0.6.0 - 2026-10-19¶
Compile and build ergonomics. compile_project now reads the build's logs and says plainly
whether the paper built cleanly, with errors and warnings by file and line, and two new tools read
a build's log and save its PDF or any other output without compiling again. The three safety
defaults 0.5.0 announced are enforced: a folder sync needs a plan, and an upload or batch upload
replaces nothing unless asked. The server registers 31 tools, two more than 0.5.x. Planned in
ROADMAP.md.
Added¶
get_compile_log(read-only): a bounded tail of a build's LaTeX log, or withkind: "bibtex"its.blglog, withformat: "errors-only"andtail(default 200 lines, at most 2,000). It reads at most 1 MB and returns{ buildId, file, log, truncated, errorLines }.download_compile_output(destructive, writes locally): savesoutput.pdf, or any output such asoutput.logoroutput.synctex.gz, to a local path in one call, returning{ localPath, file, sizeBytes, buildId, replaced }. An existing file is replaced only withoverwrite: true, atomically; a.pdfthat does not start with%PDF-is refused.compile_projectreturns asummary:{ status, builtCleanly, pdfProduced, logRead, errorCount, warningCount, errors, warnings, undefinedReferences, undefinedCitations, missingFiles, pageCount?, pdfSizeBytes? }.builtCleanlyis the field to check, since Overleaf reportssuccesswhenever a PDF came out, even after LaTeX errors. Errors and warnings are{ file?, line?, message }, the first 20 of each, messages cut to 240 characters and never the source excerpt TeX prints afterl.<n>. The log parser is this package's own, written for MIT and tested against fixture logs; it reads both TeX error forms, BibTeX, and Biber.buildRefoncompile_project's result, and on a timed-out or terminated build's error details: an opaque token naming the build toget_compile_loganddownload_compile_output, in this process or a later one. Without it, both use the latest build compiled in this process.stopOnFirstErroranddraftoncompile_project, passed to Overleaf's compile endpoint.compile_projectdeclares anoutputSchemaand returnsstructuredContent, as do both new tools.- Read-only MCP resources
overleaf://project/{projectId}/output/output.logandoverleaf://project/{projectId}/output/output.pdf, for the latest build in this process (a PDF of at most 16 MB). - Five error codes:
COMPILE_RATE_LIMITED(retryable, withretryAfterMswhen known),COMPILE_UNAVAILABLE(retryable),COMPILE_TIMEOUT,NO_BUILD, andBUILD_NOT_FOUND. batch_uploadonConflict: "fail", now the default; see Changed.details.missingon the refusals below, naming the parameter to pass.
Changed¶
- A build that ran is a result, not an error.
compile_projectwith Overleaf statusfailureorstopped-on-first-errorreturns the summary instead of throwingCOMPILE_FAILED. A compile with no build to report on fails with a code by status:too-recently-compiled,rate-limited,autocompile-backoff,compile-in-progress, and HTTP 429 areCOMPILE_RATE_LIMITED;clsi-maintenanceandunavailableareCOMPILE_UNAVAILABLE;timedoutisCOMPILE_TIMEOUT; the rest,validation-problemsincluded, stayCOMPILE_FAILED. - Compile error details are exactly
{ status, rootFilePath, buildId?, buildRef? }, plusretryAfterMsforCOMPILE_RATE_LIMITEDwhen known.details.result, deprecated in 0.5.0, is gone. - The compile response is validated where it arrives.
outputFilesentries are reduced to{ path, type, build? }, withouturl;statskeeps numbers only;timings,pdfDownloadDomain,validationProblems, and every other field are dropped. A response without astatus, or with malformedoutputFiles, isPROTOCOL_UNSUPPORTEDwith{ endpoint: "compile", expectedKeys, receivedKeys }. - A
TIMEOUTon the compile request is no longer retryable, because Overleaf may still be compiling. - The compile request adds
?file_line_errors=true, so TeX names the file of each error, andcompile_projectreadsoutput.logand any.blglog after the build, so it needs theoverleaf-readeffect as well ascompileunder an access policy. sync_directoryrequires aplanToken. Without one, and withoutunplanned: truefor an additive sync, it fails withCONFIRMATION_MISMATCHanddetails.missing: "planToken"before anything is read. A mirror sync always needs one.upload_filereplaces nothing unless asked. Onto a binary withoutoverwrite: trueorexpectedHashit isCONFIRMATION_MISMATCH(details.missing: "overwrite"); onto a text document withoutuncheckedDocumentReplace: trueit isINVALID_ARGUMENT(details.missing: "uncheckedDocumentReplace"), namingwrite_filewithlocalPath. Nothing is sent in either case.batch_uploadhas no default that replaces.onConflictdefaults to"fail": when any destination holds a document or binary file, the call fails withCONFIRMATION_MISMATCH(details.missing: "onConflict",count, and up to 25paths) before anything is uploaded. With"overwrite", a text document is replaced only withuncheckedDocumentReplace: true, and otherwise that file fails withINVALID_ARGUMENT. The result'sonConflictcan be"fail".- No result lists
deprecations: the field is removed from theupload_file,batch_upload, andsync_directoryoutput schemas, since every default it announced is now enforced. - The tool descriptions of
compile_project,upload_file,batch_upload, andsync_directory, and the connect-time instructions (417 words), describe the new behaviour.
Documentation¶
- The tool reference documents the summary, the status-to-code table, both new tools, and the
output resources, and drops the 0.5.x transition tables for the enforced defaults. The safety
model lists the new codes, the compile output guarantees, and what changed in 0.6.0. The private
API catalogue documents the output route,
file_line_errors,stopOnFirstError,draft, the statuses, and the build id and server id formats, and no longer says the response is passed through. The README and the site's home page list the new tools, and the comparison now marks reading the compile log and errors as supported.
0.5.1 - 2026-10-12¶
Listed in the official MCP Registry. No tool was added, removed, or changed in schema or result shape; the server still registers 29 tools.
Added¶
- An MCP Registry entry. The server is published to
registry.modelcontextprotocol.io as
io.github.mhmdaskari/overleaf-web-mcp, which the MCP clients and directories that read the registry use to find and install it.server.jsonin the repository root describes the npm package, itsserveargument, and the optional environment variables, andpackage.jsoncarries the matchingmcpNamethe registry checks against the published package. - The
Publish to npmworkflow lists each release in the registry once the package is on npm, signing in with GitHub OIDC, so no registry token is stored. A test keepsserver.json's name and versions aligned withpackage.json.
0.5.0 - 2026-10-10¶
Shared core and safety. Every operation is now defined once, in an operation registry the MCP
server is one adapter over, and the engine behind it is published without MCP as
overleaf-web-mcp/core. An optional access policy limits which projects, local folders, and
kinds of change the server may touch; ids are checked before they reach a request path; and error
details no longer carry text from Overleaf. Three safety defaults that depended on the caller
picking the safe option are announced here and enforced in 0.6.0: a folder sync without a plan,
an upload that replaces a file without saying so, and batch_upload's overwriting default. No
tool was added or removed; the server still registers 29 tools. Planned in
ROADMAP.md.
Added¶
overleaf-web-mcp/core, a subpath export that never loads the MCP SDK. It exportsOverleafRuntime,readConfig,createOverleafService,McpError(the same class the root exports),ERROR_CODES,SERVER_VERSION, and the contracts:OPERATIONS,OPERATION_NAMES,EFFECTS, and theOverleafService,OverleafServiceRuntime,OperationContext,OperationInput, andOperationOutputtypes.createOverleafService(runtime)returns one function per operation, keyed by tool name, that takes the tool's input and resolves with its result or rejects withMcpError. It follows the same 0.5.x defaults and deprecations as the MCP server; v0.7.0 documents its stable surface.- An access policy, configured by four environment variables, all unset by default. Every operation is checked against it before anything is sent, through the MCP server and the exported runtime alike:
OVERLEAF_ALLOWED_PROJECTS, the project ids any tool may touch.list_projectsandauth_statuslist and count only these, and a project created by this process is allowed for the rest of it.OVERLEAF_LOCAL_READ_ROOTSandOVERLEAF_LOCAL_WRITE_ROOTS, absolute folders separated by:(;on Windows) that local reads (write_fileandupload_filefrom disk,batch_upload,import_project_zip,plan_sync,sync_directory) and local writes (download_file,download_project_zip) must stay inside, judged byrealpath.OVERLEAF_ALLOWED_EFFECTS, which ofoverleaf-read,overleaf-write,overleaf-delete,project-lifecycle,compile,local-read,local-write, andunchecked-replacean operation may have. An operation checks every effect it will use before it starts, the reads a write makes afterwards included, so a refusal never lands after a change.
A relative or missing root, an unknown effect, or an unsafe project id stops the server at
startup with INVALID_ARGUMENT naming the variable.
- POLICY_DENIED, a new error code for a project (details.parameter) or effect
(details.effect) the policy does not allow. Nothing is sent.
- upload_file takes overwrite, expectedHash, and uncheckedDocumentReplace, checked
inside the upload's queue job after a fresh look at the tree, before anything is sent.
overwrite: false onto a document or binary file is CONFIRMATION_MISMATCH; an expectedHash
that does not match what is at the path is REMOTE_DRIFT. overwrite: true or a matching
expectedHash confirms replacing a binary, and uncheckedDocumentReplace: true replacing a text
document. expectedHash is a preflight, not a compare-and-swap: the upload route takes no
expected state.
- upload_file declares an outputSchema and returns structuredContent:
{ entityId?, entityType?, path, replaced, hash?, trackChangesActive, writeMode, deprecations? }.
- batch_upload takes uncheckedDocumentReplace, the same opt-in, per file.
- sync_directory takes unplanned: true runs an additive sync without a planToken on
purpose. With a planToken or in mirror mode it is INVALID_ARGUMENT before anything is read.
The result gains planned, whether a planToken checked the call.
- deprecations in the results of upload_file, batch_upload, and sync_directory:
[{ parameter, message, enforcedIn: "0.6.0" }] when a call relied on a default that 0.6.0
removes.
- Diagnostics on stderr. serve writes {"diagnostic":{"tool":…,"code":…}} to stderr when a
call fails (its error code) or relies on a deprecated default (DEPRECATED), and nothing else:
no message, path, or text from Overleaf. Library use: createMcpServer and serveOverStdio
take an optional third argument, and runStdioServer an option, onDiagnostic.
- Tests that guard the contract: tools/list against a committed snapshot, every operation's
annotations against its effects, every destructive operation taking a confirm value or an
expected state (stop_compile excepted), the policy through a real runtime with zero requests
on refusal, and a sentinel string fed through every upstream channel that no serialized error
may contain. CI imports both built entry points by package name.
Deprecated¶
Each of these still works in 0.5.x and lists a deprecations entry in its result. From 0.6.0 it
is refused with nothing sent.
sync_directorywithout aplanToken(parameter: "planToken"). From 0.6.0CONFIRMATION_MISMATCHwithdetails.missing: "planToken"; an additive sync may passunplanned: trueinstead.upload_filereplacing a binary file withoutoverwrite: trueorexpectedHash(parameter: "overwrite"). From 0.6.0 that call isCONFIRMATION_MISMATCH. A document's replacement is governed byuncheckedDocumentReplaceinstead; only an explicitoverwrite: falserefuses one.upload_filereplacing a text document withoutuncheckedDocumentReplace: true(parameter: "uncheckedDocumentReplace"). From 0.6.0INVALID_ARGUMENT, namingwrite_filewithlocalPath.batch_uploadreplacing a file withonConflictomitted (parameter: "onConflict"), or a text document withoutuncheckedDocumentReplace: true. From 0.6.0 no default replaces anything, and the document rule applies to each file.COMPILE_FAILED'sdetails.result, now reduced to{ status }. Removed in 0.6.0, when a failed build returns a parsed summary instead.
Changed¶
download_fileis annotated{ readOnlyHint: false, destructiveHint: true, idempotentHint: false }instead ofreadOnlyHint: true: it writes a local file and can replace one.upload_file'swriteModeistrackedwhen a text document replaced a text document while track changes is on for this account, because Overleaf then records the difference as tracked changes. It always readuntrackedbefore. Its description is rewritten for the new parameters.batch_upload'sonConflicthas no schema default, so an omitted value is told apart from an explicit"overwrite". An omitted value still overwrites and is reported asoverwritein the result;tools/listno longer shows"default": "overwrite".- Path-safe ids. A
projectId,sourceProjectId, orthreadIdholding/,\,.,?,#,%, or a control character isINVALID_ARGUMENTwithdetails.parameter, before any request. Every id in a request path is URI-encoded, and a created project's id that is not path-safe isPROTOCOL_UNSUPPORTED. - Requests stay on
OVERLEAF_BASE_URL's origin. A request that would leave it isINVALID_ARGUMENTbefore it is sent. A request that changes something no longer follows redirects, since fetch would carry the CSRF token to another origin: a redirect to/loginis stillAUTH_EXPIRED, and any other isREMOTE_ERRORwithdetails.status. - Four refusals apply with no policy variable set: a download onto the cookie jar, its lock
or temporary files, or the browser profile, and a local read of any of them, a folder sync of a
folder that holds them included, since an upload or a sync would put the session into a project
(
PATH_OUTSIDE_ROOT,details.kind: "session_files"); an.olignorethat links to a file outside its folder, whose patterns a plan would echo back (PATH_OUTSIDE_ROOT,details.kind: "outside_folder"); and the unsafe ids above. A local path is judged the way the file system resolves it, a symbolic link before the..that follows it, and a dangling link by where it leads. PATH_OUTSIDE_ROOTcarriesdetails.kind:outside_folder, now also on the existing refusal of a symbolic link out oflocalFolderPath,outside_read_roots,outside_write_roots, orsession_files.- A sync upload is checked against the plan. Just before each upload,
sync_directorychecks that a new path is still empty and a changed binary still has the hash the plan compared; otherwise that file fails withREMOTE_DRIFT, which withholds every delete. It used to replace whatever had appeared there. - Error details no longer carry text from Overleaf.
COMPILE_FAILEDdetails are{ status, rootFilePath, result: { status } }, with a status that is not a short identifier (^[a-z][a-z0-9-]{0,63}$) reported asunrecognized. They used to carry the whole compile response underdetails.result.- Socket rejections (
connectionRejected, stillAUTH_EXPIRED), failedjoinDoc,leaveDoc, andapplyOtUpdateacknowledgements,otUpdateError, and Socket.IO error packets no longer put the upstream payload in the message;details.reasonis an identifier for a message this release knows, orunrecognized.otUpdateError'sdetails.message, which could quote the rejected update, is gone. A raw socket error while joining a project isREMOTE_ERRORwith a fixed message. - An upload's or zip import's
errorreachesdetails.overleafErroronly as a short lowercase code. manage_entity'screate_folderresult hascreated: { _id, name }, not Overleaf's raw folder object.- A response that should be JSON and is not is
PROTOCOL_UNSUPPORTEDwith{ path, contentType }. It used to be aREMOTE_ERRORwhose message could quote the body. - The connect-time instructions are 392 words, down from 445, leaving headroom for 0.6.0 and
0.8.0. They now cover confirming replacements,
deprecations, andPOLICY_DENIEDandPATH_OUTSIDE_ROOT, and leave out what the tool descriptions already say. - The
sync_directoryandbatch_uploaddescriptions describe the transition. - Inside the package, tool definitions moved from
src/mcp/tools.tstosrc/contracts/, the argument shaping tosrc/service/, andOverleafToolRuntimebecameOverleafServiceRuntime.McpErrorCodeis derived fromERROR_CODES. The move itself lefttools/listunchanged.
Documentation¶
- The configuration guide has an access policy section, with the four variables, the effects, and
an example. The safety model adds "What changes in 0.6.0", "The access policy is not a
sandbox", the id, origin, and redirect rules, how upstream text is kept out of errors, and
POLICY_DENIEDand thePATH_OUTSIDE_ROOTkinds in its error table. - The tool reference documents the new parameters, a table of what an upload needs by what is at
the path in 0.5.x and from 0.6.0,
download_fileas destructive, anddeprecations. The README tool table gains a Local effect column, and its safety list mentions the policy. - The internals page describes the layers (contracts, service, domain engine) and shows
overleaf-web-mcp/corein use; the development guide covers the interface boundary and the snapshot and contract tests; the private API catalogue covers redirects, non-JSON bodies, and socket error reasons. AGENTS.mdrecords the one-minor rule for tightening a default, which parameters count as confirmation, the policy and boundary rules, and that stdout is reserved underserve.- The roadmap marks v0.5.0 shipped and records how its design settled.
0.4.1 - 2026-10-06¶
The v0.4.x point release. batch_upload sends a list of local files to the project paths given,
creating missing folders, in one call, and download_project_zip saves the whole project as one
zip archive, the backup step before a mirror sync. The server now runs on the v2 MCP SDK and also
serves clients on MCP 2026-07-28, and the documentation no longer overstates what a folder sync
without a planToken, a destructive tool, or an upload that replaces a file guarantees. The
server registers 29 tools. Planned in ROADMAP.md.
Added¶
batch_uploaduploads 1 to 500{ localPath, destinationPath }entries, wheredestinationPathis the full project path including the file name. Every entry is checked before anything is sent: an invalid or duplicate destination, one that is the parent folder of another, a missing local file, or a folder given as one fails the whole call with nothing changed. Missing folders are created once each, parents first; a folder that could not be created is not tried again, and the files under it fail with its error code. Files upload in the order given, one at a time through the project's queue.onConflictisoverwriteby default, replacing what is at the path asupload_filedoes, orskip, which leaves an existing document or file alone and checks again just before each upload. A destination that is a folder, or runs through a file, fails that one file. Failures are per file unlessstopOnError, and nothing is retried. The call also stops at the firstRATE_LIMITED, listing the rest inremainingand sending nothing more, since Overleaf allows about 500 uploads per project in 15 minutes and would refuse them too. Afterwards the tree is read back: an upload that is missing, or a binary whose hash differs, moves tofailedwithREMOTE_ERROR. A timed-out upload is classified from that read-back without being sent again: completed withrecoveredAfterTimeout: truewhen a binary with the local file's hash is at the path,TIMEOUTwhen the path is unchanged (it had not landed, though it could still land late), andOUTCOME_UNKNOWNotherwise, a document included. The result carriesstatus,onConflict,completed,skipped,failed,remaining, andverified. It is annotated destructive and sends progress notifications per file.download_project_zipsaves the whole project, sources and binaries, fromGET /Project/:id/download/ziptolocalPath, withoverwrite(defaultfalse) andtimeoutMs(default 5 minutes, at most 15, for the whole transfer). A missing parent folder isNOT_FOUND, a folder atlocalPathINVALID_ARGUMENT, and an existing file withoutoverwrite: trueCONFIRMATION_MISMATCH, each before any request. The archive is streamed to a temporary file in the same folder, created before the request, so a folder that cannot be written fails first withINVALID_ARGUMENTanddetails.errno; it is never held in memory. It must begin with a zip signature, elsePROTOCOL_UNSUPPORTED, and end with a zip end-of-central-directory record: Overleaf builds the archive while it sends it, so a transfer cut short still arrives with HTTP 200, and that, like a body that stops inside the signature, fails withREMOTE_ERROR, retryable. Either way nothing is written. A complete archive is then renamed into place, so a failed download never destroys an existing file; withoutoverwrite, a file that appeared in the meantime is not replaced. Overleaf allows about 10 downloads per project a minute and sends noRetry-After, soRATE_LIMITEDusually has noretryAfterMsand the description says to wait a minute. The route checks project access rather than the login, so an expired session arrives asPERMISSION_DENIED; the description says to checkauth_status. The end check cannot tell when Overleaf left out a file it failed to read, which it does without an error; the documentation says so. The result is{ projectId, localPath, bytes, replaced }. It changes nothing on Overleaf and is annotated destructive because it can replace a local file.outputSchemaandstructuredContenton both new tools.
Changed¶
- Built on the v2 MCP SDK. The server now depends on
@modelcontextprotocol/server2.3 in place of@modelcontextprotocol/sdk1.x, andserveanswers both protocol eras over stdio. Clients on the 2025-era protocol versions (2024-10-07 through 2025-11-25) connect withinitializeexactly as before; clients on MCP 2026-07-28 are served throughserver/discover, whose result carries the same usage instructions. The existing tools' names, input fields, annotations, and result shapes are unchanged. An install no longer pulls in the v1 SDK's HTTP server stack (Express, Hono, and their dependencies), andnpm audit --omit=devreports no advisories. - The
inputSchemaandoutputSchemaof each tool intools/listnow declare JSON Schema draft 2020-12, the default dialect since MCP 2025-11-25, instead of draft-07. The schemas themselves are unchanged. Tools no longer carryexecution: { taskSupport: "forbidden" }, which is the default when the field is absent. - Calling a tool name the server does not register now fails with JSON-RPC error
-32602instead of returning anisErrorresult. servenow exits when its client closes stdin, releasing the Overleaf session and sockets, instead of waiting for a signal. Tool calls still running at that moment are not answered, as the MCP stdio transport specifies, but get up to 1.5 seconds to finish against Overleaf before the sockets close, so a multi-step operation such ascreate_filewith content is not cut off between its steps.- Library use:
createMcpServer()returns theMcpServerclass from@modelcontextprotocol/server. Connecting it by hand withconnect()serves 2025-era clients only; the newserveOverStdio(runtime, transport?)serves both eras, asservedoes, and returns a connection withclose()andwhenIdle(timeoutMs). CONFIRMATION_MISMATCHalso coversdownload_project_zipcalled on an existing local file withoutoverwrite: true.download_filekeepsINVALID_ARGUMENTfor the same case.upload_file's description is corrected after a read of Overleaf's source. It no longer says a replacement keeps the entity ID: a replaced text document keeps it, and a replaced binary file may get a new one. It no longer says replacing a document is never tracked: Overleaf may record it as tracked changes when track changes is already on for this account. The result'swriteModestill readsuntrackedin that case; v0.5.0 resolves it. The tool's behaviour is unchanged.- The
INVALID_ARGUMENTmessage for Overleaf'sduplicate_file_nameupload rejection now says an entity the upload cannot replace, usually a folder, already has that name. It used to say a text document cannot replace a binary or the reverse, which Overleaf in fact allows.details.overleafErroris unchanged. - The connect-time instructions mention
batch_uploadfor a list of files, offerdownload_project_zipas a backup before a mirror sync, and no longer say an upload is never tracked, within the 450-word bound (445 words).
Documentation¶
- Corrections planned for v0.5.0, shipped early. The README, the documentation home page, and
the safety model now make the folder-sync guarantee conditional on a
planToken: without one,sync_directoryhas nothing to compare against. The README's "each one confirms by value" now names the destructive tools that do not:upload_file,batch_uploadwith its defaultonConflict,stop_compile, andsync_directoryin additive mode. The safety model says aplanTokenrecords whatplan_syncobserved rather than a person's approval, and that afailedentry in a per-item result leaves the earliercompletedentries applied. - The tool reference, safety model, usage guide, internals page, and private API catalogue cover the two new tools, and the README lists them. The usage guide adds the zip backup before a mirror sync.
- The tool reference, safety model, and private API catalogue no longer say an upload keeps a
binary's entity id or is never tracked, and no longer say uploading over the other kind of
entity fails. They state Overleaf's upload, folder-creation, and download rate limits, that an
expired session reaches
download_project_zipasPERMISSION_DENIED, and that Overleaf can leave a file it failed to read out of an otherwise complete archive. - The README and the documentation site show the package's all-time npm downloads. A weekly
Download countworkflow sums them from npm's downloads API, one year at a time so npm's 18-month limit cannot shorten the total, and publishes the badge data to thebadgesbranch, so the badge stays current on npmjs.com without a release andmaingets no weekly commits. - The roadmap gains an "Interfaces beyond MCP" section (one engine behind MCP, a command line,
and a TypeScript library, with HTTP, Git, WebMCP, and A2A as conditional triggers rather than
stages) and three new stages: v0.5.0 shared core and safety, v0.7.0 CLI, SDK, and Skills, and
v0.8.0 tracked-change review (accept and reject). Compile and build ergonomics moves from
v0.5.0 to v0.6.0 and multi-file documents from v0.6.0 to v0.9.0. v1.0.0 becomes "Hardening and
compatibility" (a
doctorcommand, backend capabilities, and an MCP protocol compatibility matrix), and Stage 0 checks the v2 SDK packages. - The roadmap marks v0.4.0 shipped and records how the point release's design settled: a
skippedlist and averifiedflag, timed-out uploads classified rather than resent,CONFIRMATION_MISMATCHfor an existing local file, a streamed download with an atomic replace and an end-record check, andtimeoutMs. It records what a read of Overleaf's source found (replaced binaries may get a new id, replacing a document can be tracked, whatduplicate_file_namemeans, and the rate limits), corrects its "What already works well" and Stage 0 notes to match, and adds a v0.5.0 task to resolveupload_file'swriteModewhen track changes is on. Its v0.5.0 documentation-corrections task is marked done.
0.4.0 - 2026-09-27¶
Bulk and sync operations. Bringing one folder up to date used to take a hand-rolled hash
comparison and a tool call per file: the session the roadmap was written from spent 24 calls on
it, 20 of them single deletes. plan_sync and sync_directory now do it in two, and
delete_entities removes a list of entities in one. The server registers 27 tools. Planned in
ROADMAP.md.
Added¶
plan_synccompares a local folder with the project, or with one project folder throughdestinationFolderPath, and changes nothing. Binary files compare by the git blob hash already in the tree; text documents are read and compared as LF-normalized text. It returnstoUpload(new or changed, withcomparedByandremoteType),identical(a count and the first 25 paths unlessverbose),remoteOnly(what mirror mode would delete, each folder collapsed to one entry with acontainscount),conflicts(a file where the other side has a folder, or non-UTF-8 text where the project has a document),ignored, and aplanToken.sync_directoryapplies a plan inadditiveormirrormode, which must always be given. With aplanTokenit first compares both sides with the plan again and fails withREMOTE_DRIFT, changing nothing, if either one moved, identical files included. Changed documents are replaced through revision-checkedwrite_fileedits, so an edit made during the sync fails that one file withREVISION_CONFLICT; binaries and new files are uploaded, and missing folders are created. WithwriteMode: "tracked", changed documents are written as tracked changes and new text files are created with tracked content, since an upload is never tracked. Uploads and writes run first and are confirmed in the tree; deletes run only in mirror mode, only withconfirmDeleteCountequal to the planned count, never after any upload or write failed, and each entity is re-checked just before it is deleted. Failures are per file and nothing is retried. The result carriesstatus,completed,failed,remaining,identicalCount, and aplanTokenthat resumes a partial run while still stopping for a change someone made in between.delete_entitiesdeletes a list of paths onceconfirmCountequals its length. Every path is resolved before any is deleted, so a missing one fails the call withNOT_FOUNDand changes nothing; a duplicate, or a path inside a listed folder, isINVALID_ARGUMENT.- Ignore rules for folder sync. Gitignore-style patterns: by default
.git/,.DS_Store, hidden files and folders,__MACOSX/, and LaTeX build output (*.aux,*.log,*.bbl,*.blg,*.out,*.toc,*.synctex.gz,*.fdb_latexmk,*.fls), then a.olignorefile in the folder, then theignoreparameter, so!patternre-includes. A path an ignore rule matches is protected on the project side too and is never compared or deleted. REMOTE_DRIFTfor a sync whose plan no longer matches the project or the local folder, withdetails.changedset toremote,local, orboth, andPATH_OUTSIDE_ROOTfor a symbolic link that leads out oflocalFolderPath.- Progress notifications. The three new tools send
notifications/progresswhile documents are read and files are applied, when the client supplies a progress token. outputSchemaandstructuredContenton the three new tools.- A gated live test (
RUN_OVERLEAF_LIVE_SYNC_TESTS=1) that mirrors a temporary folder into a throwaway project, checks that a second plan finds nothing to do, and trashes the project. - New runtime dependency:
ignore, for gitignore-compatible pattern matching.
Changed¶
CONFIRMATION_MISMATCHalso coversconfirmCountandconfirmDeleteCount, including a mirror sync called withoutconfirmDeleteCount.- The initialize instructions gain a paragraph on folder sync, and the rest is tightened so the whole stays under the 450-word bound the test enforces.
- Releases can be published from GitHub Actions. Running the
Publish to npmworkflow onmainwith a version checks it againstpackage.jsonand this changelog, runs the checks, publishes to npm with trusted publishing, and then creates thevX.Y.Ztag and the GitHub Release with the changelog section as its notes. Publishing a GitHub Release by hand works as before.
Documentation¶
- A "Folder sync and bulk delete" section in the tool reference, with the comparison rules, the ignore rules, and the order in which a sync applies changes.
- The safety model covers what a plan token guarantees and what it does not, and lists the two new error codes. The usage guide's manual hash comparison is replaced by the two-call workflow.
- The README lists the three tools, and its comparison table now marks folder comparison as supported, where it was binaries only.
- The tool reference's
auth_statusentry listssessionExpiresAt, added in 0.3.0 but missing from that page until now. AGENTS.mdrecords the sync invariants contributors must keep and the new confirm-by-value parameters.- The roadmap marks the first v0.4.0 release shipped and records how the design changed on the
way: a stateless plan token that covers both sides,
destinationFolderPath, aconflictslist, and noonConflictoption.batch_uploadanddownload_project_zipfollow in a point release.
0.3.2 - 2026-09-22¶
Documentation only. No tool was added, removed, or changed in schema or result shape; the server still registers 24 tools.
Documentation¶
- "How it compares" in the README: a capability table comparing this server with the three
most-starred Overleaf MCP servers and the two closest in design, each checked against its own
source code, with numbered notes for every partial entry. The related-projects table in the
internals page now lists
olcliand describesoverleaf-mcp-rtcorrectly. - An updated workflow illustration in the README and the package.
0.3.1 - 2026-09-22¶
Proxy support. On a network that allows outbound traffic only through an HTTP proxy, listing
projects could work while every project tool failed with ENOTFOUND, because the collaboration
WebSocket never used the proxy. Reported, diagnosed, and first fixed by
@Yusuf00Aras in
#6. No tool was added or removed, and no
schema or result shape changed; the server still registers 24 tools.
Fixed¶
- REST requests, the Socket.IO handshake, and the collaboration WebSocket all honour
HTTPS_PROXY,HTTP_PROXY, andNO_PROXY, and always take the same route. The server resolves one proxy forOVERLEAF_BASE_URLat startup and applies it to every connection. Previously REST requests used a proxy only on recent Node releases withNODE_USE_ENV_PROXY=1, and the WebSocket never did. The variable follows the base URL's scheme, lowercase names take precedence, a barehost:portmeans an HTTP proxy, andNO_PROXYmatches a host and its subdomains, optionally by port. This now works on Node 20, with noNODE_USE_ENV_PROXYneeded. - An unusable proxy value fails at startup with
INVALID_ARGUMENTnaming the variable, for example a SOCKS URL or one that does not parse. The message never includes the value, which can hold credentials, and proxy credentials are never printed.
Changed¶
- A proxy variable set in the server's environment now takes effect on every supported Node
version. If Overleaf is reachable directly while such a variable is set for other tools, list
its host in
NO_PROXY. - New runtime dependencies:
undicifor proxied REST requests andhttps-proxy-agentfor the proxied WebSocket.
Documentation¶
- "Behind a proxy" in the configuration guide, the proxy variables in its reference table, and a troubleshooting entry for networks that require a proxy.
0.3.0 - 2026-09-14¶
Session keepalive. Overleaf's web session lasts five days from its last use and is refreshed by
every request, so a saved session that went unused for five days made the server exit at startup
with AUTH_EXPIRED on stderr, which MCP clients surface only as "Connection closed". No tool was
added or removed; the server still registers 24 tools.
Added¶
overleaf-web-mcp keepalive, a CLI command that runs the server's startup bootstrap, merges the refreshed session cookie into the jar, printsrefreshed,baseUrl,sessionExpiresAt, anduserId, and exits. Against a dead session it exits with status 1 and theAUTH_EXPIREDerror on stderr, so a scheduler can alert on it. Daily scheduling withlaunchd,cron, and Task Scheduler is documented in the configuration guide.auth_statusreportssessionExpiresAt, the earliest deadline among the cookies sent to Overleaf, as ISO 8601. The field is absent when no cookie carries a deadline.auth_statusnow declares anoutputSchemaand returnsstructuredContent; its other fields are unchanged.- A table of all 24 tools in the README, grouped as in the tool reference, with a test that fails if a registered tool is missing from it.
Fixed¶
- The cookie jar now persists the session deadline. The Netscape serializer read
expires, which tough-cookie leaves unset for aMax-Agecookie, so the session cookie was written with expiry0and its five-day deadline was lost on every save. It now usesexpiryTime(), which accounts forMax-Age. Behaviour was otherwise unaffected, since a cookie without an expiry is still sent; what was missing was any way to persist or report the deadline. - Domain cookies are written with the include-subdomains flag set and a leading dot, derived
from tough-cookie's
hostOnlyrather than from a leading dot it had already stripped. The server read its own jar correctly either way; other Netscape readers,curlincluded, treated the session cookie as host-only and did not send it towww.overleaf.com. Jars written by earlier releases keep loading exactly as before.
Changed¶
- The initialize instructions mention
sessionExpiresAtand the keepalive command.
Documentation¶
- "Keeping the session alive" in the configuration guide, a troubleshooting entry for a client that reports only "Connection closed" at startup, and a note on the five-day limit in the README's sign-in step.
- The roadmap makes session keepalive its own shipped stage, moves bulk sync to v0.4.0 with
plan_sync,sync_directory, anddelete_entitiesfirst, and renumbers the later stages.
0.2.1 - 2026-09-10¶
Documentation only. No tool was added, removed, or changed in schema or result shape; the server still registers 24 tools.
Changed¶
- The example project names in the README, the documentation site, and the roadmap are now generic placeholders, consistent with the other examples around them. They previously named a real project.
0.2.0 - 2026-09-10¶
Project lifecycle. Until now every tool took an existing projectId, so an agent could not create,
rename, trash, or configure a project without a person doing it in the web UI. The server now
registers 24 tools. Planned in ROADMAP.md,
which this release also merges with a second draft into one document with per-stage tasks and
acceptance criteria.
Added¶
create_project,clone_project, andimport_project_zipcreate a project from a name, an existing project, or a local.ziparchive, and returnprojectId,name,url, and (forcreate_project)rootDocPath. A blank project still holds Overleaf's stubmain.tex; the descriptions say so and point atupdate_project_settings.manage_projectrenames, trashes, restores, archives, unarchives, or permanently deletes a project.trash,archive, anddeleterequireconfirmNameto equal the current project name exactly, anddeleteonly succeeds on a project that is already trashed, following Overleaf's own trash-first model.update_project_settingspersists the root document (rootFilePath, which must resolve to a text document),compiler, TeX LiveimageName, orspellCheckLanguagein the project, so the web editor's Recompile follows the change. It returns the settings as re-read from a fresh project join.list_projectsfilters:query(case-insensitive substring),includeArchived,includeTrashed(both defaultfalse),limit(default 50, at most 200), andsort(lastUpdated, newest first, orname).RATE_LIMITEDerror code for HTTP 429, retryable, withdetails.retryAfterMsparsed fromRetry-Afterwhen Overleaf sends it. Zip import and project creation are the routes Overleaf throttles first.CONFIRMATION_MISMATCHerror code for every confirm-by-value failure (confirmPath,confirmName). Nothing is changed when it is returned.outputSchemaandstructuredContenton the five new tools and onlist_projects. The JSON text block is still returned for clients that do not read structured results.get_project_treereportsspellCheckLanguagealongsidecompilerandimageName.docs/private-api.mdcatalogues every Overleaf route the server calls, the fields it sends and reads, and the error code each failure maps to.- A gated live test (
RUN_OVERLEAF_LIVE_LIFECYCLE_TESTS=1) that creates a disposable project, sets its root document, compiles it, and trashes it. It never deletes permanently. - Usage instructions in the MCP initialize response. The server now sets the MCP
instructionsfield, so clients that surface it (Claude Code among them) give the model the safety contract, read before write, revision handling,upload_filesemantics, confirmations, compile allowance, and what to do onAUTH_EXPIRED, without anyone pasting the README into a prompt. Exported asSERVER_INSTRUCTIONS; a test keeps it under 450 words. - Documentation site at https://mhmdaskari.github.io/overleaf-web-mcp/, built with MkDocs
Material from
docs/and deployed by GitHub Actions on every push tomain. It holds the full tool reference, safety model, configuration, internals, development guide, roadmap, and this changelog. AGENTS.mdfor coding agents contributing to the repository, with the rules that must keep holding, test expectations, and the release steps.CLAUDE.mdimports it for Claude Code.
Changed¶
list_projectsreturns an object instead of a bare array:{ projects, totalMatched, totalProjects }, where each project carriesid,name,accessLevel,lastUpdated,archived, andtrashed. Archived and trashed projects are hidden by default, and the default order is newest first rather than by name. The list now comes from the dashboard endpoint (POST /api/project) instead of the legacyGET /user/projects, and its shape is validated; an unexpected shape surfaces asPROTOCOL_UNSUPPORTED.auth_status.projectCountcounts every project the account can access, archived and trashed included, through the same endpoint.manage_entitywith a wrongconfirmPathnow fails withCONFIRMATION_MISMATCHinstead ofINVALID_ARGUMENT. The message is unchanged.- The initialize instructions gain a paragraph on project lifecycle and
confirmName; they remain under the 450-word bound the test enforces. - README rewritten for people. It now leads with what you can say to an assistant, a three-step install, what the server can do, and how it keeps a project safe, in plain language, and links to the site for everything else. The tool tables, the configuration table, the protocol notes, and the step-by-step workflows moved to the documentation site.
- The tool-count test now checks the README badge and the docs tool reference instead of two places in the README.
Documentation¶
- A "from nothing to a compiled PDF" walkthrough in the usage guide that needs no web-UI step.
- The README and site list the lifecycle tools, and the related-projects section states that web-session peers also offer tracked changes while Git-bridge servers need a paid plan and bypass tracked changes.
- The roadmap is one merged document with a Stage 0 verification checklist, conventions, tool signatures, tasks, acceptance criteria, an error taxonomy, and a competitive table.
0.1.3 - 2026-09-01¶
Documentation, metadata, and small additive fixes. No tools were added or removed; the server still registers 19 tools. Planned in ROADMAP.md.
Changed¶
get_project_treenow returns an object instead of a bare array. The entities are underentities, alongsiderootDocPath,compiler,imageName,trackChangesActive, and ahashNotedescribing the hash format. Callers that iterated the result directly must now iterateresult.entities. This is the only response-shape change in this release.compile_project.rootFilePathis now optional. Omitted, it compiles the root document configured in Overleaf itself, matching the web UI's Recompile button. A project with no configured root and norootFilePathreturnsINVALID_ARGUMENTinstead of failing to resolve a path. Its description no longer leaks the internalrootDoc_idparameter name.upload_fileis annotateddestructiveHint: true. It replaces an existing entity at the destination path in place, so the previousdestructiveHint: falsemisdescribed it.upload_filereturns a normalized result:entityId,entityType,path,replaced, and, for binary files,hash. Overleaf's raw response is no longer passed through under anuploadkey.compile_projectresults now includerootFilePath, the document actually compiled.download_fileno longer silently overwrites a local file. It fails withINVALID_ARGUMENTunless the newoverwriteparameter istrue, and returnslocalPathalongsidebytes.
Added¶
write_fileacceptslocalPathas an alternative tocontent, mutually exclusive with it. This keeps a whole-file replacement revision-checked and optionally tracked without sending the file through the MCP client's tool-argument budget. Non-UTF-8 content is rejected rather than written as replacement characters, and a leading byte order mark is stripped.upload_fileacceptsdestinationName, so a local file can be stored under a different name in the project.- Overleaf's upload rejections are translated into actionable errors. Overleaf reports
duplicate_file_name,invalid_filename,project_has_too_many_files, andfolder_not_foundas HTTP 422; these now surface asINVALID_ARGUMENTwith an explanation and the original code indetails.overleafError, instead of a bareREMOTE_ERROR. Only a short lowercase identifier is ever propagated, so no response content can leak through an error. - Continuous integration on pushes and pull requests running type-check, lint, tests, build, and the packed-file check. Previously these ran only when a release was published.
- ROADMAP.md describing the planned path from 0.1.3 to 1.0.0.
- This changelog.
Documentation¶
- The
hashfield is documented as a git blob hash,sha1("blob " + byteLength + "\0" + content), identical togit hash-object <file>. Plainsha1sumnever matches. The hash is present only on binaryfileentities; Overleaf stores no content hash fordocentities. upload_file's upsert semantics are documented, including that Overleaf decidesdocversusfileby extension and UTF-8 validity, that it is a valid way to replace.tex,.bib, and.bstdocuments from disk, and that replacing a document this way is a blind, untracked write.- A decision table for
write_fileversusupload_file, and the real document and update size limits, so callers no longer have to guess a size threshold. - A worked example of the hash-comparison workflow for uploading only changed binaries.
- A test asserts the README's tool count matches the registered tool list, so the badge cannot drift.
0.1.2 - 2026-07-20¶
- Project history monitoring through
monitor_project_history. - Tracked document writes through
writeModeonwrite_file,create_file, andwrite_section.
0.1.1 - 2026-07-16¶
- Documentation and packaging corrections.
0.1.0 - 2026-07-15¶
- First release: browser-assisted session capture, project and file management, revision-checked and section-level writing, compilation, and review comments.