Skip to main content
Glama

Aldine

Write LaTeX together. Fast, versioned, yours.

CI License: AGPL v3 PRs welcome Buy Me a Coffee

Aldine is a self-hosted, open-source collaborative LaTeX editor and an Overleaf alternative. It is slim and built for speed: real-time multi-cursor editing, every project a real git repo with branches, native Zotero, ~2s warm recompiles. Two containers and flat files by default: no database to migrate, nothing to babysit.

Homepage · Try the live demo (resets nightly) · Quick start · How Aldine compares · Screenshots · Self-hosting · Contributing

Guides on aldine.dev: Self-hosting Overleaf (Community Edition, Server Pro) compared with Aldine · Git with Overleaf and a git-native alternative · A LaTeX MCP server for Claude

Live collaboration, a recompile, and a SyncTeX jump, in one real recording (compile wait trimmed):

A collaborator's edits stream in live, the PDF recompiles in about two seconds, and double-clicking the PDF jumps the editor to the source line

Status: Aldine reached 1.0 in October 2026, three months after its first release. Within 1.x an upgrade starts on older data with no manual step, and stable settings, MCP tools and the plugin API keep their names until 2.0 (docs/STABILITY.md). That is a promise about upgrades, not a long track record. It compiles real papers daily and every headline feature has a Playwright end-to-end test; CI runs those on every pull request, next to typecheck, build and the integration suites. Expect rough edges, and file issues generously.

Features

  • Bring your Overleaf projects, and take them with you: download any Overleaf project as a ZIP, drop it on the home screen, keep writing — root file detected, first typeset automatic.

  • Real-time collaboration: CRDT-based (Yjs), multi-cursor with live presence, conflict-free by construction. Unlimited collaborators.

  • Git-native with branches: every project is a real git repository. Create branches, edit them independently, merge back from the UI. Publish a project to GitHub, GitLab, Gitea or Forgejo and a co-author can clone it and keep using VS Code; their commits come back with one Pull.

  • Fast, sandboxed compiles: TeX Live + latexmk with persistent incremental builds (~2s warm recompiles) in a no-egress container with restricted shell-escape; errors surfaced with line numbers and click-to-jump.

  • GitHub/GitLab/Gitea/Forgejo sync: import from GitHub, GitLab, Gitea or Forgejo as a project or publish a local project to a fresh repo, push/pull with ahead/behind indicators, branches, a conflicting pull that lists the files and keeps your version until you take the remote's, server-side autopush, and open a pull or merge request, all from the editor. gitlab.com and self-hosted GitLab, any Gitea or Forgejo instance (Codeberg included); a personal access token needs no OAuth app. A team instance can create every new project in a GitLab group automatically and push every autosave from the server.

  • Native Zotero integration: link your whole Zotero library or a single collection, from My Library or any of your group libraries, no premium tier required; keep a .bib in sync with cheap version-aware refresh, insert citations from a search panel or via \cite{ autocomplete.

  • Claude as a collaborator (optional, ALDINE_MCP=1): Aldine has a built-in MCP server. Connect your instance to claude.ai, Claude Desktop, Cowork or Claude Code over MCP and Claude edits, typesets and shows the PDF in the chat (inline on claude.ai, Claude Desktop and Cowork; a signed link elsewhere); every change is a git commit authored "Claude" with a review-and-revert toast in the editor. Setup, reachability and the security model: docs/AGENT_API.md.

    Claude edits the open document, adds a section, typesets, and History lists its commits with a review prompt

  • Visual editing mode: LaTeX renders as formatted text while the source stays authoritative and byte-stable (it never rewrites source you didn't deliberately edit). WYSIWYG math (click a formula to edit it in a MathLive popover; a formula MathLive would rewrite, such as one with a \label, alignment, a comment or a line break inside, opens as source), editable tables, inline tracked changes from review suggestions, paste-rich-text-to-LaTeX, image-previewing figure chips, and an outline. From the keyboard, Ctrl+Enter (⌘Enter on a Mac) with the caret in a formula's or a table's source opens its editor. Cursor-reveal shows raw source under the caret, including a remote collaborator's. Switch between Source and Visual in the toolbar or the command palette (⌘K); a .tex file opens as source until you pick Visual, and your browser remembers the last choice.

  • Review mode: select text and leave an anchored, threaded comment; optionally attach a suggested replacement the author accepts with one click. Comments highlight in the editor, resolve/reopen, and track edits.

  • AI error fix (optional, BYO key): on a failed typeset, get a plain-English diagnosis and one-click fixes. Set OPENROUTER_API_KEY, OPENAI_API_KEY, or ANTHROPIC_API_KEY on the server to enable (that precedence order if several are set; ALDINE_AI_MODEL overrides the model). With ALDINE_AI_AUTOFIX=1 a failed typeset opens the Fix tab and runs the diagnosis without a click, once per new error. The key stays server-side and never reaches the browser. Unset the key and Aldine is a 100% AI-free editor.

  • Find and cite papers without leaving the editor: search the literature by title or author (OpenAlex), or paste a DOI / arXiv id — one click appends the BibTeX and inserts the \cite (no account, free public APIs).

  • SyncTeX both ways: double-click the PDF to jump to source; ⌘J to jump the PDF to your cursor, with a highlight flash.

  • Plugin system: manifest + ES module plugins add sidebar panels and write into the editor. Zotero, references, and AI-fix ship as plugins; write your own.

  • Templates & import: article, IAC conference paper, beamer and report/thesis; a template for each publisher class the compiler's TeX Live carries (Elsevier, IEEE, ACM, REVTeX, LNCS and more: 15 in the published images); 25 official conference and journal kits (NeurIPS, ICML, ACL, CVPR, Springer Nature, Wiley, …) downloaded from the publisher when you create the project; plus your group's own templates from any git repository (TEMPLATE_REPOS). Or import an existing project from an Overleaf ZIP.

  • Editor niceties: auto-typeset on idle, live whole-document word count, spellcheck, Vim keybindings, PDF zoom + download, drag-drop figure upload, previews of images and PDFs from the file tree, plain-English error hints + raw log, command palette (⌘K / Ctrl+K).

  • Search and replace across the project: every text file on the branch, open or not, with match case, whole word and regular expressions; Replace all changes open documents live for everyone and lands as one commit.

  • Multi-user auth (optional): set AUTH_ENABLED=1 for login, per-project ownership, and sharing (invite-only or link). Google, GitHub & ORCID SSO, your own OpenID Connect provider (Keycloak, Authentik, Authelia, Pocket ID; docs/OIDC.md), or email/password (scrypt-hashed, revocable HTTP-only-cookie sessions); ALDINE_SSO_ONLY=1 disables passwords entirely. Off by default (single-tenant); the collab socket is access-checked. ALDINE_ADMIN_EMAILS=you@example.org opens /admin: accounts, active users (7 / 30 days, editing now), projects, compile time, and per account disable, confirm address, password reset and delete. Never project content. No compose file passes this variable through; add it under environment: of the app service. ALDINE_SIGNUP=approval keeps every new account, password or single sign-on, waiting until an administrator approves it on /admin; with email set up, the administrators get a mail when one starts waiting.

  • Scales when you need it: flat-file storage by default; set DATABASE_URL for Postgres and REDIS_URL for shared rate limits and cross-node collab events. One app node is still the supported topology; docs/SCALING.md says exactly what is and isn't built.

  • Apple-style UI: system fonts, hairline borders, light & dark mode, keyboard-first (⌘S typeset, ⌘J jump, ⌘K command palette).

Related MCP server: Overleaf MCP

Quick start

No clone, no build: save this as docker-compose.yml and run docker compose up -d:

name: aldine

services:
  app:
    image: ghcr.io/trahloff/aldine-app:${ALDINE_VERSION:-1.5.0}
    ports:
      - "8080:3000"
    volumes:
      - aldine-data:/data
      - aldine-secrets:/secrets
    networks: [frontend, backend]
    init: true
    restart: unless-stopped

  compiler:
    image: ghcr.io/trahloff/aldine-compiler:${ALDINE_VERSION:-1.5.0}${ALDINE_TEXLIVE:-}
    volumes:
      - aldine-data:/data
    # The compiler runs untrusted LaTeX. Keep this block.
    networks: [backend]
    mem_limit: 2g
    pids_limit: 256
    cap_drop: [ALL]
    security_opt: [no-new-privileges]
    init: true
    restart: unless-stopped

networks:
  frontend: {}
  backend:
    internal: true # no route to the internet

volumes:
  aldine-data:
  aldine-secrets:

Open http://localhost:8080. That's it. Projects live in the aldine-data volume, and everything else (auth, SSO, AI fix, email) is opt-in via environment variables when you want it.

That is equivalent to the repo-root docker-compose.yml (same services, images and sandbox settings), so a clone works identically: git clone https://github.com/trahloff/Aldine && cd Aldine && docker compose up -d. Keep the name: aldine line wherever you save it: it fixes the volume names, which is what lets you switch compose files later and what deploy/backup.sh looks for.

  • You are pinned to a version. The block above says ${ALDINE_VERSION:-1.5.0}, so a fresh copy installs the current release and nothing under a running install changes on its own. To upgrade, read the CHANGELOG, back up (deploy/backup.sh), write the new version into a .env file next to docker-compose.yml, then pull:

    # .env
    ALDINE_VERSION=<new version>
    
    docker compose pull && docker compose up -d

    Compose reads .env on every call, so a later up stays on that version. A prefix (ALDINE_VERSION=… docker compose pull && docker compose up -d) reaches only the first command, and up starts the old version again. To roll back, put the previous release in .env and run the same two commands. Your projects live in the aldine-data and aldine-secrets volumes and an image swap does not touch them. :latest still exists and points at the newest release; pinning is what lets you choose when to move.

  • Stability. Versions follow SemVer. A 1.x release starts on data from an earlier 1.x, or from 0.14 or later, with no manual step, and nothing covered is removed before 2.0. Every release is built from a commit that passed CI, and its images are booted and made to typeset a real document before :latest moves to them. Before that, it is also started on volumes left by the previous release and by 0.14.4, on the JSON store and on Postgres, and the projects, comments, shares and tokens have to survive. Back up first anyway. Watch the repo's Releases for security fixes: per SECURITY.md, only the latest release gets them. Which data, settings and APIs every 1.x keeps working, and how each promise is checked, is in docs/STABILITY.md.

  • The first pull is big. TeX Live lives in the compiler image: about 2.4 GB to download, about 5 GB on disk. It carries TeX Live's medium scheme plus the pictures, latexextra and bibtexextra collections (pgfplots, tikz-cd, cleveref, todonotes, minted, biblatex, …), the publisher classes, and the Arabic, Persian, Cyrillic and Greek scripts; the one family it leaves out is CJK. After the first pull, starts take seconds. Ready when curl localhost:8080/api/health returns {"ok":true,"name":"aldine"}.

  • Need a package it does not have? The missing-package error in the editor names it. Two ways to get it, and project settings show which image you are on:

    • Add just that package. Put a docker-compose.override.yml next to docker-compose.yml that builds a derived compiler image, then docker compose up -d --build. tlmgr installs from the same dated TeX Live snapshot the release was built from, so this keeps working after the release ages:

      services:
        compiler:
          image: aldine-compiler-local
          build:
            dockerfile_inline: |
              FROM ghcr.io/trahloff/aldine-compiler:1.5.0
              RUN tlmgr install pgfplots tikz-cd

      Bump the FROM tag when you bump ALDINE_VERSION. Compose builds the image on your machine, so this works for arm64 servers as well.

    • Get everything. Every release from 0.4.0 also ships all of TeX Live as -full (about 2.8 GB compressed, 9 GB on disk; CJK is only here). Add ALDINE_TEXLIVE=-full next to ALDINE_VERSION and pull again.

  • Port 8080 taken? Change the left side of ports:; with docker-compose.full.yml, set ALDINE_PORT=8081 in .env instead.

  • On a NAS, with the same compiler sandbox:

  • Everything beyond the minimum: building from source (latest main, not a release), auth/SSO/AI/email options, TLS, Postgres/Redis. All of it lives in docker-compose.full.yml, which carries the same compiler sandbox and the same volumes, so you can switch without losing data: docker compose -f docker-compose.full.yml up -d --build. The first build installs TeX Live into the compiler image; expect 20–60 minutes (the tlmgr install step is most of it), then seconds per start. ALDINE_TEXLIVE_SCHEME=full builds the all-of-TeX-Live variant instead.

How Aldine compares

Aldine

Overleaf CE (self-hosted)

git + VS Code + LaTeX Workshop

Deploy

2 containers, docker compose up

Overleaf Toolkit; sharelatex, mongo and redis containers (source)

n/a (local)

Real-time collaboration

✅ CRDT, unlimited collaborators

✅ (source)

❌ (async via git)

Review comments / suggested edits

✅

❌ commenting and track changes are Server Pro only (source)

PR reviews

Git branches from the UI

✅ projects are git repos

❌ git integration is Server Pro only (source)

✅ (it is git)

GitHub/GitLab/Gitea sync + PRs/MRs from the editor

✅

Overleaf documents GitHub synchronization as "a premium feature for Overleaf Cloud users" (source); absent from its on-premises comparison (source)

✅ natively

Zotero

Whole library or one collection

Overleaf lists Zotero among the third-party integrations of its cloud platform (source); absent from its on-premises comparison (source)

Via Better BibTeX auto-export

Warm recompile

~2s (persistent latexmk cache)

Not measured

Local

Templates gallery

4 starter templates, one per publisher class in the compiler's TeX Live (15 in the published images), 25 conference and journal kits fetched from the publisher, plus your own from git repositories

❌ private template management is Server Pro only (source)

CTAN / your own

Package coverage

TeX Live medium + pictures, latexextra, bibtexextra, publisher classes, most scripts by default (about 5 GB on disk); any extra with one tlmgr install line; all of TeX Live with ALDINE_TEXLIVE=-full

Minimal TeX Live in the image; full with tlmgr install scheme-full (source)

Whatever you install

Rich-text / visual editing

✅ byte-stable, WYSIWYG math, editable tables, review suggestions shown inline (accept/dismiss)

✅ Visual Editor; its code is in the open-source repository (source)

❌

Maturity

Young: 1.0 since October 2026, first release July 2026

Overleaf dates from 2011 (source)

Very mature

License

AGPL-3.0

AGPL-3.0 (source)

MIT/varies

If Overleaf CE fits you, use it; it's good software. Aldine exists for people who want review comments with suggested edits, git and Zotero without paid tiers, in a deployment they can hold in their head.

Every Overleaf CE cell except "Not measured" links to the Overleaf page it was checked against on 2026-10-12. For GitHub synchronization and Zotero we found no Overleaf page that states them for the on-premises editions, so those cells quote what Overleaf does say. A longer, dated version of this comparison is at aldine.dev/self-hosted-overleaf.

Screenshots

Home: every project shows page 1 of its last typeset. Search, sort, or start a new one from a template, a ZIP or a git host.

Visual editing: the source rendered as formatted text, byte-stable, with math and tables editable in place. Switch with Source/Visual in the toolbar.

Review mode: threads + one-click suggestions

Branches: create, switch, merge from the UI

Zotero: cite from your library or collection

History: auto-checkpoints, named checkpoints, diffs

Development

npm install
npm run dev:server     # API + collab on :3000
npm run dev:web        # Vite on :5173 (proxies to :3000)
docker build -t aldine-compiler apps/compiler
docker run -d -p 4020:4020 -v $PWD/.data:/data aldine-compiler

Tests

npm run typecheck -w apps/web && npm run test -w apps/web   # tsc + vitest
npm run test:github -w apps/server                          # GitHub-sync integration
npm run test:gitlab -w apps/server                          # GitLab-sync integration
npm run test:gitea -w apps/server                           # Gitea/Forgejo-sync integration
npm run test:db -w apps/server                              # datastore conformance

End-to-end (Playwright; covers compile, collab, branches, plugins, Zotero). The suite starts its own app on :3100 but not the compiler, and it runs against .data-e2e, so the compiler has to point at that directory or every compile test fails against a healthy-looking compiler:

npx playwright install chromium
DATA_DIR=$(pwd)/.data-e2e PORT=4020 node apps/compiler/server.js &   # or a container on the same dir
npm run test:e2e
ALDINE_URL=http://localhost:8080 npm run test:e2e   # against a running compose stack instead

Production deploy

Settings live in a .env file next to the compose files, so every later docker compose call picks them up. Give each comment its own line: anything after a value becomes part of the value, and AUTH_ENABLED=1 multi-user login is not 1, so auth silently stays off.

# app on loopback only; your reverse proxy fronts it
ALDINE_APP_BIND=127.0.0.1
# absolute URL of the app, used in OAuth callbacks, password-reset links and
# the PDF links the MCP connector hands out. Give it a path to serve Aldine
# under a prefix (https://server/internal/aldine); the Claude connector URL
# is <ALDINE_PUBLIC_URL>/mcp
ALDINE_PUBLIC_URL=https://aldine.example.com
# signs the connector's 15-minute PDF links; generated into META_DIR when
# unset — set it explicitly when several nodes do not share that volume.
# At least 32 characters (`openssl rand -base64 32`); shorter values refuse to boot
ALDINE_SIGNING_SECRET=
# MCP endpoint for Claude (docs/AGENT_API.md); needs AUTH_ENABLED=1 or ALDINE_MCP_TOKEN
ALDINE_MCP=
# static bearer for /mcp with auth off (single-tenant); ignored when AUTH_ENABLED=1
ALDINE_MCP_TOKEN=

# Everything below is optional and off unless set.
# multi-user login, ownership and sharing (unset = single-tenant)
AUTH_ENABLED=1
# Google SSO
GOOGLE_OAUTH_CLIENT_ID=
GOOGLE_OAUTH_CLIENT_SECRET=
# GitHub SSO
GITHUB_LOGIN_CLIENT_ID=
GITHUB_LOGIN_CLIENT_SECRET=
# ORCID SSO: a Public API client (orcid.org/developer-tools, HTTPS callback
# <ALDINE_PUBLIC_URL>/api/auth/oauth/orcid/callback); ORCID_SANDBOX=1 for sandbox.orcid.org
ORCID_CLIENT_ID=
ORCID_CLIENT_SECRET=
# OpenID Connect SSO (Keycloak, Authentik, Authelia, Pocket ID, …): redirect URI
# <ALDINE_PUBLIC_URL>/api/auth/oauth/oidc/callback; every option in docs/OIDC.md
OIDC_ISSUER=
OIDC_CLIENT_ID=
OIDC_CLIENT_SECRET=
OIDC_LABEL=
OIDC_ALLOWED_GROUPS=
# GitHub repo sync: a separate OAuth app with repo scope
GITHUB_CLIENT_ID=
GITHUB_CLIENT_SECRET=
# GitLab sync (gitlab.com or self-hosted); PAT connect works without these
GITLAB_URL=https://gitlab.com
GITLAB_CLIENT_ID=
GITLAB_CLIENT_SECRET=
# Gitea / Forgejo sync needs nothing here: users paste a token and their instance URL
# REMOTE_PROVIDERS=github,gitlab,gitea   # hide a provider by leaving it out
# Optional: every new project is also created on GitLab in this group (service
# account PAT with scope api and Owner on the group). Off unless both are set.
GITLAB_TOKEN=
GITLAB_DEFAULT_GROUP=
# GITLAB_DEFAULT_VISIBILITY=private   # or internal | public
# AUTOPUSH_DEBOUNCE_MS=30000
# AI error fix, bring your own key
OPENROUTER_API_KEY=
# password-reset email: SMTP, or SES_FROM + AWS_REGION instead
SMTP_HOST=
SMTP_PORT=587
SMTP_USER=
SMTP_PASS=
SMTP_FROM=
# error tracking, and which instance the errors came from
SENTRY_DSN=
SENTRY_ENVIRONMENT=production
# Templates from your own git repositories (see templates/README.md)
# TEMPLATE_REPOS='[{"id":"lab","label":"Lab templates","url":"https://gitlab.example.org/latex/templates.git","tokenEnv":"TEMPLATE_REPO_LAB_TOKEN"}]'
# TEMPLATE_REPO_LAB_TOKEN=
# Behind your existing reverse proxy (nginx, Traefik, …), the usual setup. The
# prod overlay trusts proxy headers, sets secure cookies, and rotates logs;
# point your proxy at 127.0.0.1:8080.
# Sample nginx vhost (WebSocket + body-size gotchas handled): deploy/nginx.conf
docker compose -f docker-compose.full.yml -f deploy/docker-compose.prod.yml \
  up -d --build

# …or, if nothing else owns ports 80/443, add the bundled Caddy for
# zero-config HTTPS: append `--profile tls` and set ALDINE_DOMAIN in .env.

# Back up the data + secrets volumes (and the bundled Postgres, if you use it):
deploy/backup.sh aldine-backup.tar.gz
# Restore, after stopping the stack with `docker compose down` (with Postgres,
# start the database alone first: deploy/README.md, "Restore"):
#   deploy/restore.sh aldine-backup.tar.gz

Every variable Aldine reads is listed in deploy/README.md.

Isolation & limits. The compiler runs on an internal-only Docker network (no internet egress), drops all Linux capabilities, and is bounded on CPU / memory / PIDs; LaTeX compiles with restricted shell-escape (whitelist only) and openin_any=p. Per-client rate limits guard login, AI, and reference lookups; compiles are concurrency-capped, with optional per-user compile quotas (ALDINE_COMPILE_QUOTA_MIN) if you host for a group.

See deploy/README.md for the full single-VPS runbook (nginx/Traefik/Caddy ingress, backups, SSO setup, Postgres/Redis, every config variable), deploy/aws for a Terraform/Fargate deployment, and SECURITY.md for the threat model and how to report vulnerabilities.

Architecture

┌────────────┐   HTTP/WS    ┌──────────────────────────────┐
│  Browser   │ ───────────► │  app (Node 22)               │
│  React +   │              │  Fastify API + Hocuspocus    │
│  CM6 + Yjs │              │  git repos + worktrees       │
└────────────┘              └──────────┬───────────────────┘
                                       │ shared volume /data
                            ┌──────────▼───────────────────┐
                            │  compiler (TeX Live medium)  │
                            │  latexmk wrapper, sandboxed  │
                            └──────────────────────────────┘
  • One Yjs document per file per branch (project::branch::path), persisted straight to the git worktree with debounced writes and auto-commits.

  • Branches are git worktrees, so every branch is editable concurrently.

  • Compile output stays inside the project tree (.aldine-out/, kept out of git history) which keeps latexmk's incremental cache warm.

Data & storage

Two separate concerns, behind two seams:

  • Project files: real git repos + worktrees on disk (store.ts). This is what gives you branches and history.

  • Relational/metadata: users, sessions, project metadata, review comments, and usage go through the DataStore interface (db/). Two backends:

    • JSON files (default, zero-dependency): the slim single-node self-host.

    • Postgres (set DATABASE_URL): the horizontally-scalable backend, and the prerequisite for ever running multiple app nodes. pg is an optional dependency; the same conformance suite runs against both.

For how this scales past one box (and what the remaining walls are), see docs/SCALING.md.

Plugins

A plugin is a folder in plugins/:

plugins/hello/
├── manifest.json   # { "id": "hello", "name": "Hello", "version": "1.0.0", "entry": "index.js" }
└── index.js        # export default { activate(aldine) { ... } }

The aldine API exposes ui.registerSidebarPanel, ui.showPanel (select one of the plugin's own panels), editor.insertAtCursor, project context, compile(), toast(), and fetch(). See plugins/zotero for a complete example.

Templates

A template is a folder with a template.json and the files to start from; the shipped ones live in templates/. A group that keeps its own thesis, poster or report templates puts the same layout in any git repository (GitHub, GitLab, Gitea, a bare repo over https), lists it in TEMPLATE_REPOS, and the templates appear in the gallery under the repository's name, refreshed on an interval or with "Refresh templates", with {{PROJECT_NAME}}, {{AUTHOR}}, {{DATE}} and {{YEAR}} filled in on create. Layout, manifest fields, placeholders and private-repository tokens: templates/README.md.

License

Copyright (C) 2026 Tobias Rahloff.

AGPL-3.0: self-host freely; if you offer a modified Aldine as a service, share your changes. Third-party plugins interact with Aldine over its plugin API and may use any license. Each folder under templates/ carries its own LICENSE and states it in template.json (the gallery shows it on the tile): the generic templates are MIT, and templates/iac-paper is LPPL-1.3c, the customary license for a LaTeX class file. Venue templates are generated from the classes installed in the compiler image, with the license tlmgr reports. Venue kits are downloaded from the publisher when a project is created, and their tile links the publisher's terms instead of naming a license. No publisher file is stored in this repository. Overleaf is a trademark of its owner. Claude, Zotero, GitHub, GitLab and other names are trademarks of their respective owners. Aldine is an independent project, not affiliated with or endorsed by any of them.

Two things stated plainly, because finding them out later feels like a bait-and-switch. A hosted Aldine service is planned, and contributions are accepted under a CLA that permits relicensing, so a commercially licensed edition is possible in future. What will not change: the self-hosted edition stays AGPL-3.0, and no feature that works today moves behind a paid tier. The name is handled separately in TRADEMARK.md.

Available Tools

16 tools
batch_writeChange several files in one commitA
Destructive

Apply one multi-file change as ONE named commit (author Claude). Each entry carries full content (new files) or quote-anchored edits (existing files, same rules as edit_file). All edits resolve before anything is written — a stale_anchor or ambiguous_anchor writes nothing (the error names the entry as path); fix that entry and resend the whole batch. A version_conflict on any entry also writes nothing: re-read that file and resend. Use it for a coherent change across files; use edit_file for a single file. message = the intent, imperative ("Add related-work section"). The result's commit and head are that commit; a batch that leaves every file as it was makes no commit and answers commit:null with unchanged:true (nothing is pending).

NameRequiredDescriptionDefault
filesYes
branchNoBranch name (defaults to 'main'). Every result, error results included, echoes {branch, head} — head is the branch's current commit (after a write, the commit that write made). Pass the branch explicitly whenever you are not on main.
messageYesCommit message stating the intent of the change, at most 200 characters.
projectNoProject id. Optional when the access token is scoped to exactly one project.

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare destructive/not-idempotent, so the description's added value is the all-or-nothing atomicity contract: 'All edits resolve before anything is written,' stale_anchor/ambiguous_anchor/version_conflict write nothing, and the error names the offending entry. It further discloses the no-op edge case (commit:null, unchanged:true, nothing pending), which no annotation covers.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the action and the sibling routing in the first and eighth clauses, with error semantics clustered after. It is dense and slightly run-on (errors, no-op result, and message style all in one paragraph), but essentially every sentence carries distinct information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a destructive, multi-file write with no output schema, the description covers atomicity, per-error recovery, and what the return value contains (commit, head, and the unchanged no-op case). Nothing an agent needs to invoke it safely and interpret the result is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 75%, so the baseline is 3; the description adds real meaning on top — the content-vs-edits mutual exclusion per entry ('full content (new files) or quote-anchored edits… same rules as edit_file') and the intent/imperative convention for message ('Add related-work section'). It stops short of discussing base_version/branch, which remain schema-only.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Apply one multi-file change as ONE named commit') and immediately distinguishes scope from the closest sibling: 'use edit_file for a single file.' An agent can tell batch_write from edit_file/write_file/commit without opening any schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicit selection rule: 'Use it for a coherent change across files; use edit_file for a single file.' It also gives the recovery procedure per error class (fix the entry and resend the whole batch; re-read and resend on version_conflict), which is when-to-use guidance in practical form.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

commitCommit pending changesA
Idempotent

Commit the files you have written on this branch that are not yet committed, as one commit under message (author Claude). Only your own paths land: a collaborator's unsaved typing stays out of it and reaches history as their own autosave. Call it when the user asks for a named checkpoint — every write already commits on its own as it lands, so {committed:false} is the normal answer, not a failure: it means they landed already, and the result names the current head and your latest commits on the branch. For a multi-file change you are making right now, use batch_write instead — it writes and commits it in one step under its own message. message titles the commit and replaces the per-file titles those writes would have had.

NameRequiredDescriptionDefault
branchNoBranch name (defaults to 'main'). Every result, error results included, echoes {branch, head} — head is the branch's current commit (after a write, the commit that write made). Pass the branch explicitly whenever you are not on main.
messageYesCommit message stating the intent of the checkpoint, at most 200 characters.
projectNoProject id. Optional when the access token is scoped to exactly one project.

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Adds substantial behavioral context beyond the annotations: only the caller's own paths are committed, a collaborator's unsaved typing is excluded and reaches history as their own autosave, and a {committed:false} result is a normal no-op that still reports the current head and latest commits. These details explain side effects and result interpretation in a way the annotations do not.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is front-loaded with the core action and then uses each subsequent sentence to convey a distinct operational nuance: path ownership, normal no-op results, batch_write alternative, and message semantics. Despite its density, no sentence is redundant.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given three parameters with full schema descriptions, annotations covering safety and idempotency, and no output schema, the description is complete enough to use correctly. It explains the unusual no-op outcome, return information such as head and latest commits, and the key behavioral boundary around collaborator paths.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the baseline is 3, but the description adds meaning for the required message parameter by explaining that it titles the commit and replaces per-file titles from prior writes. Branch and project semantics remain mostly in the schema, so this is a modest enhancement rather than full parameter coverage.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource: commit files written on this branch that are not yet committed, under a message and author Claude. It clearly scopes the operation to the caller's own paths and distinguishes itself from batch_write for multi-file changes being made right now.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly says to call it when the user asks for a named checkpoint, explains that every write already commits so {committed:false} is normal rather than a failure, and points to batch_write as the alternative for a current multi-file change. This leaves little ambiguity about when and when not to use the tool.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

compileTypeset the projectA
Idempotent

Typeset the project with latexmk: parsed errors [{type,file,line,message,context?,source?}] (type "error" rows before "warning" rows, first 50; errorsTotal counts the errors and warningsTotal the warnings, whether or not the list was capped; box warnings are omitted; context is the source line the engine was reading, source the tool that raised a warning — latex, a package name, biber — and a biber row names the .bib and its line), on failure ≤4 KB of the log around the first error, pages, and a deep link into Aldine. With errors present the PDF is complete but the bibliography and cross-references are not rebuilt — say so. pdfUrl is a signed link to this run's PDF that anyone can open for 15 minutes — hand it to the user as the way to see the result, and call get_pdf_url for a fresh link later instead of recompiling. pdfStale:true means this run wrote no PDF: pdfUrl shows the previous one, or is null when that no longer exists. Takes up to ~2 minutes; progress notifications arrive while it runs. Compile after a coherent set of edits, not after each one, and never just to check syntax. On errors: fix and recompile at most 3 times, narrating each attempt ("attempt 2 of 3: added natbib"), then stop and ask the user, quoting the failing file:line. A project with no .tex file cannot be typeset: write the main document first. A compiler-not-responding, compiler-cannot-see-the-project, missing-package (hint), quota, or typeset-already-running error is for the user to act on — relay it, do not retry.

NameRequiredDescriptionDefault
branchNoBranch name (defaults to 'main'). Every result, error results included, echoes {branch, head} — head is the branch's current commit (after a write, the commit that write made). Pass the branch explicitly whenever you are not on main.
projectNoProject id. Optional when the access token is scoped to exactly one project.

TDQS

A4.6/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already establish that this is a non-read-only, idempotent, non-destructive operation. The description adds substantial behavioral context beyond annotations: parsed error structure, first-50 cap with totals, omitted box warnings, failure-log size, PDF completeness caveats, signed URL lifetime, pdfStale semantics, and job duration with progress notifications. It does not contradict any annotation.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is dense but front-loaded, opening with the core operation before output and failure details. Nearly all content is useful for a complex compile tool with no output schema, though the long paragraph and nested parentheticals could be more scannable. It is not padded, but the structure is heavy.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Because no output schema exists, the description carries the full burden of explaining return values and does so thoroughly: error rows, total counts, log excerpt, pages, deep link, PDF URL and staleness behavior. It also covers timing, progress, error recovery, and user-actionable failure modes, leaving little an agent needs unstated.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

With schema description coverage at 100%, the schema already documents the branch and project parameters in detail. The description mentions project context but does not add branch/project semantics beyond what the schema provides. Baseline 3 is appropriate because the schema carries parameter meaning.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description begins with a specific verb and resource: "Typeset the project with latexmk." It also distinguishes itself from sibling get_pdf_url by explaining that get_pdf_url should be used for a fresh link instead of recompiling. An agent can identify the operation and immediate purpose without opening the schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives explicit when-to-use guidance (after coherent edits, not after each edit or just to check syntax) and when-not-to-use guidance. It also specifies retry limits, narration requirements, escalation behavior, and alternatives such as get_pdf_url. A missing-main-document precondition and user-actionable errors are also clearly called out.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

create_projectCreate a projectA

Create a new project, blank or from a template, and return its id for the write tools, with contentVersion (pass it as base_version to the first write) and the files the template seeded. Needs a token with access to all projects — a project-scoped token cannot create; if refused, relay that to the user (they can mint an unscoped token in Settings → Agent access).

NameRequiredDescriptionDefault
nameYesProject name shown in the workspace.
templateNoTemplate id from the workspace gallery: a folder template ("article", "beamer", "report", "iac-paper") or a venue kit ("venue:neurips", "venue:elsarticle", …; the publisher's class files are downloaded when they are not installed). An unknown id is refused with the full list. "blank" creates a project with no files and no main document — the first .tex written becomes it. Omit for a starter main.tex + references.bib.

TDQS

A4.4/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Goes well beyond the annotations: it discloses the auth requirement and the specific failure mode for project-scoped tokens, how to surface that failure to the user, and the returned contentVersion contract ('pass it as base_version to the first write') that prevents a downstream write error. That is exactly the kind of context annotations (readOnly=false, idempotent=false) cannot convey.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Return values and the blank/template distinction are front-loaded, then auth caveats follow. It's a single dense, em-dash-heavy sentence that takes close reading, but every clause carries information an agent needs.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema, the description supplies the return contract (id, contentVersion, seeded files) and the follow-on usage of contentVersion. Auth prerequisites and error handling round it out; nothing an agent needs to call and chain from this tool is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% and the 'template' property already documents folders, venue kits, 'blank', and the omit-default in detail. The description's mention of 'blank or from a template' and 'files the template seeded' restates rather than extends that. Baseline 3 applies since the schema does the heavy lifting.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Create a new project'), names both modes (blank or from a template), and enumerates what it returns (id, contentVersion, seeded files). This is clearly distinguishable from siblings like list_projects or trash_project without opening the schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives a concrete precondition (a token with access to all projects; a project-scoped token cannot create) and an escalation path (relay the refusal; the user can mint an unscoped token in Settings → Agent access). It doesn't contrast against any sibling tool, but for a create operation the usage context is otherwise fully covered.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

edit_fileEdit a fileA
Destructive

Replace exact quoted text inside an existing file — the default way to change a document, and the only safe way to change one a person has open (edits merge with their live typing; write_file would discard it). Each quote: ≥8 characters, copied verbatim from a read, matching exactly one place (or set occurrence). Nothing is applied on any error result. {error:"stale_anchor"}: the quote is not in the file — re-read it, re-anchor from the candidates, retry; at most 2 retries, then tell the user what you tried and ask. {error:"ambiguous_anchor"}: the quote matches several places — the candidates carry the occurrence that picks each; resend with it, no re-read needed. {error:"version_conflict"}: this file changed after the read that base_version came from (writes to other files never conflict, so parallel tools on different files are safe) — re-read this file, then re-apply once. Committed as author Claude before the tool answers, under message (the intent, imperative — "Tighten the abstract"); without one the commit is titled "Edit ". The result's commit and head are that commit; an edit that leaves the file as it was makes no commit and answers commit:null with unchanged:true (nothing is pending).

NameRequiredDescriptionDefault
pathYesFile path relative to the project root, e.g. "main.tex" or "sections/intro.tex" — no leading "/", no "..", and hidden folders (.git, .aldine-out) are off limits.
editsYes
branchNoBranch name (defaults to 'main'). Every result, error results included, echoes {branch, head} — head is the branch's current commit (after a write, the commit that write made). Pass the branch explicitly whenever you are not on main.
messageNoCommit message stating the intent of this change, imperative ("Tighten the abstract"), at most 200 characters. It titles the commit in the History panel; every call is one commit.
projectNoProject id. Optional when the access token is scoped to exactly one project.
base_versionNocontentVersion (or fileVersion) from your read of this file, or contentVersion from project_structure for a file you are creating. Refused with {error:"version_conflict", reason, currentVersion, fileVersion} when this file changed after that read (changes to other files on the branch do not count), or when base_version is newer than the branch's contentVersion (a server restart or another node issued it) — either way re-read the file and use the contentVersion that read returns.

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations only say destructive/not-idempotent; the description adds the traits that actually matter: nothing is applied on any error result (atomicity), edits merge with a human's live typing, every call is one commit authored as Claude, the default commit title when message is omitted, and that a no-op edit yields commit:null with unchanged:true. That is substantive context the annotations cannot express.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the core rule and the write_file contrast before error handling, and every sentence carries operational content. The parenthetical-heavy, brace-notation style packs a lot into one block, which is dense but not padded.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema, the description still covers the return contract (commit and head, commit:null with unchanged:true), all three documented error shapes, and the retry policy. Nothing an agent needs to invoke this destructive tool safely is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 83%, so the baseline is 3, but the description adds real semantics beyond it: it explains that occurrence is what the ambiguous_anchor candidates carry (so no re-read is needed) and what happens when message is absent ("Edit <path>" title). It says less about project and branch beyond what the schema already documents, so it stops short of a 5.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and mechanism — replace exact quoted text inside an existing file — and immediately distinguishes it from the sibling write_file ("write_file would discard it"). An agent can pick between edit_file and write_file without opening either schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly declares itself the default mutation path and names the condition that makes it mandatory (a person has the file open) versus the alternative that would destroy their work. It further routes per error class: re-read and retry at most twice on stale_anchor, resend with occurrence on ambiguous_anchor, re-read once on version_conflict, and clarifies that parallel tools on different files are safe.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_pdf_urlGet a link to the PDFA
Read-only

A fresh signed link to the PDF on disk for the branch, without recompiling — use it when a compile result's pdfUrl has expired (15 minutes) or the user asks to see the PDF again. Returns {pdfUrl, pdfFile, pages, typesetAt, deepLink}; pdfStale is always false here because the link is to that file, whichever typeset wrote it (by you or by a person). It does not contain edits made since that typeset: if you have written on this branch since your last compile result, call compile instead. No output yet → compile first.

NameRequiredDescriptionDefault
branchNoBranch name (defaults to 'main'). Every result, error results included, echoes {branch, head} — head is the branch's current commit (after a write, the commit that write made). Pass the branch explicitly whenever you are not on main.
projectNoProject id. Optional when the access token is scoped to exactly one project.

TDQS

A4.6/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations only say readOnlyHint=true and openWorldHint=false; the description goes well beyond by disclosing the 15-minute expiry, the signed-link nature, the exact response shape, why pdfStale is always false, and the staleness caveat that the link excludes edits made since that typeset. That is precisely the staleness semantics an agent could otherwise get wrong.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with purpose and trigger, then return shape, then caveats — good ordering with no filler. The 'pdfStale is always false here because the link is to that file, whichever typeset wrote it (by you or by a person)' clause is dense and slightly convoluted, keeping it short of a 5.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

No output schema exists, yet the description enumerates the returned fields {pdfUrl, pdfFile, pages, typesetAt, deepLink} and explains the staleness semantics, plus the prerequisites (compile first). Nothing an agent needs to call this correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so `branch` and `project` are already fully documented in the schema, including the echo of {branch, head}. The description only refers to 'the PDF on disk for the branch' and adds no syntax or defaulting detail beyond that schema text, so the baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('a fresh signed link to the PDF on disk for the branch, without recompiling') and immediately contrasts it with recompiling, separating it from the `compile` sibling. An agent can tell exactly what this returns.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives explicit trigger conditions ('when a compile result's pdfUrl has expired (15 minutes) or the user asks to see the PDF again') and explicit exclusions with the alternative named ('if you have written on this branch since your last compile result, call compile instead'). It even handles the no-output case: 'No output yet → compile first.'

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_citationsList citation keysA
Read-only

Citation keys defined in the project's .bib files: [{key, title, author, year, file}]. Always call it before writing a \cite — never invent a key. A key you need that is missing: add it with references_add, or ask the user.

NameRequiredDescriptionDefault
branchNoBranch name (defaults to 'main'). Every result, error results included, echoes {branch, head} — head is the branch's current commit (after a write, the commit that write made). Pass the branch explicitly whenever you are not on main.
projectNoProject id. Optional when the access token is scoped to exactly one project.

TDQS

A4.2/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already cover the safety profile (readOnlyHint=true, openWorldHint=false), and the description adds genuinely useful context: the shape of each returned record and the workflow rule against fabricating keys. It does not discuss cost, ordering, or behavior on empty .bib files, which keeps it from a 5.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three short sentences: return shape first, then the mandatory-call rule, then the missing-key remedy. No filler, and nothing important is buried.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

There is no output schema, and the description compensates by spelling out the record fields an agent will receive. Combined with rich parameter documentation, an agent has enough to call it correctly; only edge cases such as an empty or missing .bib file go unaddressed.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so both `branch` and `project` are fully documented in the schema, including the branch/head echo behavior. The description adds nothing about either parameter beyond the incidental phrase 'the project's .bib files', so baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States the resource precisely (citation keys defined in the project's .bib files) and even lists the returned fields, which lets an agent distinguish it from siblings like references_add or read_file. The verb is only implicit via the tool name rather than stated in the description, so it falls just short of a 5.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly instructs when to call ('Always call it before writing a \cite') and states a prohibition ('never invent a key'), then names the alternative path for missing keys: references_add or ask the user. That is exactly the when/when-not/alternative triad.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_labelsList labelsA
Read-only

\label targets defined in the project's .tex files: [{label, file}]. Call it before writing \ref, \eqref, or \cref — never invent a label.

NameRequiredDescriptionDefault
branchNoBranch name (defaults to 'main'). Every result, error results included, echoes {branch, head} — head is the branch's current commit (after a write, the commit that write made). Pass the branch explicitly whenever you are not on main.
projectNoProject id. Optional when the access token is scoped to exactly one project.

TDQS

A4.2/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true and openWorldHint=false, so safety is covered; the description adds value beyond that by disclosing the return shape ({label, file}) and a policy constraint (never invent a label). It does not mention pagination or the {branch, head} echo, though that is documented in the schema's branch parameter.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two short sentences, zero filler. The return shape and the pre-write trigger are both front-loaded, and each sentence carries distinct information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema, the description usefully states the result shape, and the branch/project semantics live in a fully documented schema. Only minor gaps remain (ordering, handling of duplicate or commented-out labels), which an agent can discover from the call itself.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, and both the branch default/'main' behavior and the project scoping are fully explained in the schema itself. The description adds nothing about either parameter, so the baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('List \label targets defined in the project's .tex files') and even sketches the return shape '[{label, file}]'. No sibling tool (read_file, references_add, list_citations) does this, and the description alone lets an agent distinguish it without opening the schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives a concrete precondition — 'Call it before writing \ref, \eqref, or \cref — never invent a label' — which is a real when-to-use rule plus a prohibition. It does not name an alternative tool or an explicit when-not case, so it falls just short of a 5.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_projectsList projectsA
Read-only

List the LaTeX projects this token can reach: id, name, branches, rootFile, engine, agentCreated (true for projects made with create_project — the only ones trash_project accepts). Call it when you do not know the project id. A token scoped to one project makes the project argument optional everywhere.

NameRequiredDescriptionDefault

No parameters

TDQS

A4.3/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already cover readOnlyHint=true and openWorldHint=false, so the safety profile is known; the description adds genuine context beyond that — results are filtered by token scope, and the agentCreated flag marks exactly the projects trash_project accepts. It does not mention ordering, result size, or what an empty result means, which keeps it off a 5.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three sentences, front-loaded with what is returned, then the calling condition, then the token-scope implication. The nested parenthetical about agentCreated and trash_project is information-dense but makes the second sentence slightly run-on.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no input parameters and no output schema, the description carries the return-shape burden itself and does so by listing the fields returned. Nothing critical is missing for a zero-argument list tool, though ordering or result-size behavior is left unstated.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool takes zero parameters, so the schema baseline is already 4 and the description does not need to explain inputs. It still adds cross-tool value by noting that a project-scoped token makes the project argument optional everywhere, which informs how the agent calls sibling tools.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('List the LaTeX projects') plus the exact scope ('this token can reach') and enumerates the returned fields (id, name, branches, rootFile, engine, agentCreated). Naming create_project and trash_project in passing makes it distinguishable from those siblings without opening their schemas.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly says 'Call it when you do not know the project id', which is a clear triggering condition, and adds that a project-scoped token makes the project argument optional elsewhere. It stops short of naming a when-not or a specific alternative tool, so it is clear context rather than a full routing rule.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

pingCheck the connectionA
Read-only

Check that the Aldine connector is reachable and authenticated. If it fails, tell the user their Aldine instance is not responding or the connector needs reconnecting — do not go on to other tools.

NameRequiredDescriptionDefault

No parameters

TDQS

A4.4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true and openWorldHint=false, so safety is covered. The description adds genuinely new behavioral context: that the check verifies authentication as well as reachability, and what a failure means diagnostically (instance not responding vs. connector needs reconnecting). It does not describe what a success response contains, but with no output schema that is a minor gap.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences with zero filler: the first defines the check, the second front-loads the failure branch and the required agent behavior. Every clause earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a zero-param health-check with annotations covering safety and no output schema, the description is nearly complete — purpose, scope, and failure handling are all present. The only omission is the shape of a successful result, which an agent can infer but would benefit from seeing stated.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool takes zero parameters, so there is nothing for the description to disambiguate; baseline 4 applies. No parameter semantics are needed or missing.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Check that the Aldine connector is reachable and authenticated') and goes beyond a bare connectivity ping by naming the authentication dimension. It is clearly distinct from every sibling tool, which all operate on projects, files, or citations.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The failure clause gives an explicit exclusion ('do not go on to other tools') plus the user-facing action to take, which is exactly the routing guidance an agent needs. It stops short of saying when to call it proactively (e.g., first call in a session), so it is strong but not fully explicit about the positive trigger.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

project_structureShow a project's filesA
Read-only

File tree of a project branch plus rootFile, engine, and contentVersion (pass it as base_version to edit_file, write_file or a batch_write entry — a write is refused only when its own file changed after that version). Call it before working in a project you have not read yet.

NameRequiredDescriptionDefault
branchNoBranch name (defaults to 'main'). Every result, error results included, echoes {branch, head} — head is the branch's current commit (after a write, the commit that write made). Pass the branch explicitly whenever you are not on main.
projectNoProject id. Optional when the access token is scoped to exactly one project.

TDQS

A4.2/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true and openWorldHint=false, so the safety profile is covered; the description adds substantive context beyond that — the meaning of contentVersion and the precise stale-write rule (a write is refused only when its own file changed after that version). It does not mention size limits or truncation behavior for large trees, so it is not fully transparent.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences, zero filler, with the core resource (file tree) front-loaded and the actionable version-handoff detail immediately after. The parenthetical about write refusal is dense but directly load-bearing.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

There is no output schema, so the description carries the burden of return values — it names the key fields (rootFile, engine, contentVersion) and the {branch, head} echo is covered by the schema. It never sketches the tree's shape or ordering, which would help an agent parse a large result, so it is strong but not fully complete.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100% and both parameters (branch, project) are documented there in detail, including defaults and token-scoping behavior. The description adds no parameter-specific syntax or defaults of its own, so the baseline 3 is appropriate; its value is concentrated on output semantics rather than inputs.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource — the file tree of a project branch — and immediately names the additional payload fields (rootFile, engine, contentVersion). This distinguishes it from siblings like list_projects (which lists projects) and read_file (which reads one file), so an agent can pick it without opening the schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives clear proactive guidance: "Call it before working in a project you have not read yet," and explains that contentVersion should be forwarded as base_version to edit_file, write_file, or batch_write. It does not explicitly name an alternative for the already-known-project case (e.g. going straight to read_file), so it stops just short of full when/when-not coverage.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

read_fileRead a fileA
Read-only

Read a text file as the editor shows it right now (open documents are flushed first). Reads cap at ~100 KB — window larger files with from_line/to_line (1-based, inclusive; the result echoes the window it served, to_line clamped to the last line). Read before you edit: edit_file quotes must match this content verbatim. The result carries contentVersion (the branch version — pass it as base_version when you write this file) and fileVersion (the version at which this file itself last changed). Text is decided by content, not extension: a file with binary bytes is refused.

NameRequiredDescriptionDefault
pathYesFile path relative to the project root, e.g. "main.tex" or "sections/intro.tex" — no leading "/", no "..", and hidden folders (.git, .aldine-out) are off limits.
branchNoBranch name (defaults to 'main'). Every result, error results included, echoes {branch, head} — head is the branch's current commit (after a write, the commit that write made). Pass the branch explicitly whenever you are not on main.
projectNoProject id. Optional when the access token is scoped to exactly one project.
to_lineNo
from_lineNo

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations only declare readOnlyHint and openWorldHint; the description adds substantial behavior beyond that: open documents are flushed first, reads cap at ~100 KB, to_line is clamped to the last line, the result echoes the window served, and files with binary bytes are refused based on content rather than extension.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Dense but front-loaded: freshness/flush first, then size cap and windowing, then the edit dependency, then version fields, then binary refusal. Every sentence carries actionable information with no filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema, the description explains the important return fields (contentVersion, fileVersion, the echoed window and branch/head context implied by the schema notes). It covers freshness, limits, pagination, failure mode for binaries, and the downstream edit_file dependency.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 60% and from_line/to_line have no schema descriptions; the description compensates by defining them as 1-based, inclusive, and clamped in the echo. path, branch, and project semantics are already fully documented in the schema, so the description adds value only where the schema is thin.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Read a text file') and immediately defines the exact semantics it returns: the editor's current state with open documents flushed. It distinguishes itself from the write siblings by tying its output to edit_file quote matching.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives explicit conditions: window files larger than ~100 KB with from_line/to_line, and read before editing so edit_file quotes match verbatim. It does not name a read alternative (none exists among siblings), so the guidance is clear context without an explicit when-not-to-use.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

references_addAdd a reference by DOI or arXiv idA
Idempotent

Resolve a DOI, doi.org URL, arXiv id, or OpenAlex id to a BibTeX entry and append it to a .bib file on the branch (default: references.bib next to the root file). A bibFile that does not exist is created, folders included (created:true in the result). Returns {key, bibFile, duplicate, created} — use \cite{key} right away; a note says when no .tex on the branch loads that .bib (\addbibresource or \bibliography), in which case the citation stays undefined until one does. Titles are not lookups: for a paper you only know by name, ask the user for its DOI or arXiv id. If the lookup budget is reached or the upstream service fails, tell the user — do not retry in a loop.

NameRequiredDescriptionDefault
queryYesA DOI (10.xxxx/…), doi.org URL, arXiv id (2301.12345 or arXiv:2301.12345), or OpenAlex id (W…).
branchNoBranch name (defaults to 'main'). Every result, error results included, echoes {branch, head} — head is the branch's current commit (after a write, the commit that write made). Pass the branch explicitly whenever you are not on main.
bibFileNoTarget .bib path relative to the project root. Defaults to references.bib beside the root file; a missing file (and its folders) is created.
projectNoProject id. Optional when the access token is scoped to exactly one project.

TDQS

A4.4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already give readOnly=false, idempotent=true, openWorld=true, destructive=false, and the description adds real context beyond them: missing .bib files and folders are created, duplicates are reported, and an undefined-citation warning appears when no .tex loads the .bib. It also surfaces external-service failure modes and rate/budget limits, which the annotations do not imply.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the action and resource, then returns, then caveats, then anti-patterns — a sensible order. It is dense with parentheticals but nearly every clause carries operational information; only the restatement of the bibFile default is redundant with the schema.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema, the description carries the return-value burden and does so fully: {key, bibFile, duplicate, created} plus the `created:true` case and the undefined-citation note. For a mutating, network-dependent tool it covers success, side effects, and partial-failure behavior.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the baseline is 3. The description largely restates schema content (default references.bib beside the root file) but does add meaning for `query` by excluding titles, which is the real semantic constraint on that parameter.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb chain — resolve a DOI/arXiv/OpenAlex identifier to a BibTeX entry and append it to a .bib file on a branch — and names the concrete artifact involved. No sibling tool does this (list_citations reads, this writes), so an agent can distinguish it immediately.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly bounds usage: titles are not lookups, so ask the user for a DOI/arXiv id; on lookup-budget exhaustion or upstream failure, tell the user rather than retry in a loop. These are concrete when/when-not rules plus a failure-handling policy that no schema field could convey.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

trash_projectMove a project to the trashA
DestructiveIdempotent

Move a project that was created with create_project to the trash, where its owner can restore it from the workspace for 30 days; nothing in Aldine is deleted for good by this tool, but a GitLab repository Aldine provisioned for the project is deleted (a restore provisions a new one). Refused for any project a person created (agentCreated:false in list_projects) — ask them to delete it in Aldine themselves — and for projects the token's owner does not own. Use it for scratch projects the user asked you to throw away; never on your own initiative.

NameRequiredDescriptionDefault
projectYesProject id (from list_projects or create_project). Required: this tool never falls back to a scoped token's project.

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The annotations already mark this as destructive and idempotent, but the description adds important behavioral details: the 30-day restore window, that Aldine itself does not permanently delete data, and that the provisioned GitLab repository is deleted while a restore provisions a new one. It also discloses ownership and agentCreated refusal conditions.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is front-loaded with the core action, followed by side effects, refusals, and usage guidance. Despite its density, every sentence carries decision-relevant information for an agent.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a destructive one-parameter mutation with no output schema, the description covers the key concerns an agent needs: restore behavior, external GitLab repository deletion, refusal conditions, and when to invoke versus avoid the tool. The annotations are consistent with the description.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, and the schema already explains that 'project' is a required project id from list_projects or create_project with no scoped-token fallback. The description reinforces the lifecycle context but does not add syntax or format details beyond the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description gives a specific verb and resource: moving a project to the trash. It also distinguishes this from create_project and list_projects by naming the lifecycle stage and the source of the project id.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It explicitly states when to use the tool ('scratch projects the user asked you to throw away') and when not to use it ('never on your own initiative'). It also names refusal cases and an alternative action: asking the user to delete person-created projects in Aldine.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

wordcountCount wordsA
Read-only

Word count of the document as it compiles — the root file plus its \input/\include graph, commands and comments excluded: {rootFile, total, files}. Use it for length questions instead of estimating from source. A project with no main document yet is an error, not a count of 0.

NameRequiredDescriptionDefault
branchNoBranch name (defaults to 'main'). Every result, error results included, echoes {branch, head} — head is the branch's current commit (after a write, the commit that write made). Pass the branch explicitly whenever you are not on main.
projectNoProject id. Optional when the access token is scoped to exactly one project.

TDQS

A4.5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Beyond the readOnly/openWorld annotations, it discloses what is counted and excluded (commands, comments, included files), that results come from compilation, and the error contract for a project with no main document — exactly the failure mode an agent would otherwise misread as a zero count.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two dense sentences, front-loaded with the counting definition, then the usage direction and the error caveat. No filler, and every clause carries distinct information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema, the description voluntarily supplies the return field list and the failure semantics, and the parameters are fully covered by the schema. Nothing an agent needs to invoke or interpret this call correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so branch and project are already documented, including the {branch, head} echo behavior. The description adds no further parameter meaning, so the baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Names a specific verb+resource (word count of the document) and pins the exact scope — root file plus \input/\include graph, commands and comments excluded — plus the return shape {rootFile, total, files}. No sibling competes for this job, and an agent can tell instantly what it computes.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

"Use it for length questions instead of estimating from source" gives an explicit use condition and rules out the obvious wrong approach. It also pre-empts the ambiguous case (no main document is an error, not 0). No named sibling alternative is needed since none exists, but the guidance is clear rather than exhaustive.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

write_fileWrite a whole fileA
DestructiveIdempotent

Create a new file, or replace a whole file. For an existing file prefer edit_file — a whole-file write on an open document teleports collaborators' viewports and can drop their in-flight typing. Pass base_version (contentVersion from a prior read of this file) to get {error:"version_conflict"} instead of overwriting newer content — only a change to this file conflicts, never one to another file; on conflict re-read and re-write once, then ask. Folders come into being with the first file written inside them. The first .tex written into a project with no main document becomes the main document (newRoot in the result). Committed as author Claude before the tool answers, under message (the intent, imperative); without one the commit is titled "Update ". The result's commit and head are that commit; content identical to the file makes no commit and answers commit:null with unchanged:true (nothing is pending).

NameRequiredDescriptionDefault
pathYesFile path relative to the project root, e.g. "main.tex" or "sections/intro.tex" — no leading "/", no "..", and hidden folders (.git, .aldine-out) are off limits.
branchNoBranch name (defaults to 'main'). Every result, error results included, echoes {branch, head} — head is the branch's current commit (after a write, the commit that write made). Pass the branch explicitly whenever you are not on main.
contentYes
messageNoCommit message stating the intent of this change, imperative ("Tighten the abstract"), at most 200 characters. It titles the commit in the History panel; every call is one commit.
projectNoProject id. Optional when the access token is scoped to exactly one project.
base_versionNocontentVersion (or fileVersion) from your read of this file, or contentVersion from project_structure for a file you are creating. Refused with {error:"version_conflict", reason, currentVersion, fileVersion} when this file changed after that read (changes to other files on the branch do not count), or when base_version is newer than the branch's contentVersion (a server restart or another node issued it) — either way re-read the file and use the contentVersion that read returns.

TDQS

A4.6/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare destructiveHint=true, idempotentHint=true, and readOnlyHint=false, so the safety profile is covered structurally. The description adds genuinely non-obvious behavior beyond that: the collaborator viewport side effect, folder auto-creation, the .tex main-document promotion (newRoot), author/commit semantics, and the no-op case returning commit:null with unchanged:true. It does not discuss permissions or rate limits, hence a 4 rather than 5.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The action statement and the edit_file routing are front-loaded, and nearly every clause carries information an agent needs. It is a dense single block with several ideas chained together, which costs some readability, but there is little wasted text.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

There is no output schema, so the description must explain results, and it does: version_conflict shape, newRoot, the returned commit and head, and the unchanged:true/commit:null no-op case. For a 6-parameter destructive write tool this is complete enough to call correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is high (83%), so the schema carries most parameter meaning and the baseline would be 3. The description nevertheless adds value the schema does not state: conflict scope ('only a change to this file conflicts, never one to another file'), the re-read-once retry protocol, and the default commit title 'Update <path>' when message is omitted.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Opens with a specific verb and resource plus scope: 'Create a new file, or replace a whole file.' It immediately distinguishes itself from the sibling edit_file, so an agent can tell the two apart without opening either schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

States explicitly when to prefer the alternative ('For an existing file prefer edit_file') and gives the reason (collaborator viewport teleporting, dropped typing). It also gives a decision procedure for conflicts: re-read and re-write once, then ask.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 16 tool updatesv0.1.0
    • First observedbatch_write
    • First observedcommit
    • First observedcompile
    • First observedcreate_project
    • First observededit_file
    • First observedget_pdf_url
    • First observedlist_citations
    • First observedlist_labels
    • First observedlist_projects
    • First observedping
    • First observedproject_structure
    • First observedread_file
    • First observedreferences_add
    • First observedtrash_project
    • First observedwordcount
    • First observedwrite_file

TDQS

A4.3/5.0

Scored across 16 tools

Disambiguation5/5

Each tool targets a distinct action or resource: project discovery, file reading, three clearly differentiated write modes, compilation, PDF URL retrieval, commits, citations, labels, and word count. The potentially overlapping write tools are explicitly distinguished by use case and safety constraints (edit_file for open files, write_file for whole-file replacement, batch_write for multi-file commits).

Naming Consistency3/5

Most core CRUD tools use a readable snake_case verb_noun pattern, such as list_projects, create_project, read_file, and edit_file. However, the set mixes conventions with noun-only names like project_structure and wordcount, a reversed noun_verb form in references_add, and bare verbs like ping, compile, and commit.

Tool Count4/5

At 16 tools, the surface is slightly above the typical 3-15 range but still well-scoped for a LaTeX project connector. Each tool appears to earn its place across project management, editing, compilation, references, and inspection.

Completeness4/5

The set covers the main agent workflow well: list/create/trash projects, inspect structure, read/edit/write files, batch edits, compile, retrieve PDFs, commit, add references, list citations and labels, and count words. Minor gaps remain around deleting, renaming, or moving existing files, and branch operations are implied but not exposed.

Maintenance

ActivityActive
ResponsivenessResponsive

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    B
    maintenance
    An MCP server for Overleaf that allows Claude or other agents to navigate projects, read and edit .tex files, compile, and interact with review-panel comments via Overleaf's real-time Socket.IO API, with seamless support for tracked changes as pending suggestions.
    17
    346 npm
    6
    AGPL 3.0