Skip to main content
Glama
wspringer

lilypond-mcp

README.md
# <picture><source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/wspringer/lilypond-mcp/main/assets/logo-dark.svg"><img src="https://raw.githubusercontent.com/wspringer/lilypond-mcp/main/assets/logo-light.svg" alt="" width="52" align="top"></picture> lilypond-mcp

[![npm](https://img.shields.io/npm/v/lilypond-mcp.svg)](https://www.npmjs.com/package/lilypond-mcp)

MCP server that engraves [GNU LilyPond](https://lilypond.org) sources into
placeable assets: cropped PDF, EPS, SVG, and PNG, sized to the music rather
than a full page — ready to drop into page-layout software such as InDesign.

Nothing to install beyond Node: the server fetches a WebAssembly build of
LilyPond on first use and engraves with that — the same engine on every
machine.

## Quick start

Claude Code — `.mcp.json` in your project:

```json
{
  "mcpServers": {
    "lilypond": {
      "command": "npx",
      "args": ["-y", "lilypond-mcp@latest"]
    }
  }
}
```

Claude Desktop — `claude_desktop_config.json`, same entry under
`mcpServers`. Or skip the config entirely: grab the `.mcpb` file from
the [latest release](https://github.com/wspringer/lilypond-mcp/releases/latest)
and open it with Claude Desktop — a desktop extension with the engine
bundled in, so it works offline from the first engrave.

That's the whole setup. On the first engrave, the server downloads the
engine (~35 MB, checksum-verified, cached under
`~/.cache/lilypond-mcp` — safe to delete at any time).

## Tools

- **`engrave_file`** — engrave a `.ly` file. Defaults to cropped PDF —
  the format to place in InDesign (fonts embedded and subsetted, all PDF
  boxes defined). Also returns a preview PNG, both as a file and inline
  as an image in the tool result, so the agent sees what it engraved.
- **`engrave_code`** — engrave LilyPond code passed inline, for iterating
  on a musical idea without touching disk.
- **`lilypond_version`** — the LilyPond version the server engraves with,
  for picking the right `\version` header.

Both engrave tools accept `formats` (`pdf`/`eps`/`svg`/`png`), `crop`,
`include_dirs` (for shared `\include` libraries), `output_dir`, and
`preview` (default on; turn off to skip the inline image on batch runs). Paths
are resolved against the working directory the server is launched in — for
an `.mcp.json` entry, that is the project root. On failure the result
carries LilyPond's diagnostics, line numbers included, so an agent can fix
the source and retry.

## What you get

This snippet, sent to `engrave_code`:

```lilypond
\version "2.26.0"
\header { tagline = ##f }

\score {
  <<
    \new ChordNames \chordmode { g2. | c | d | g }
    \new Staff \new Voice = "m" \relative c'' {
      \key g \major
      \time 3/4
      \tempo "Waltz" 4 = 132
      g4 b d | e4.( d8) c4 | a4 fis d | g2 r4 \bar "|."
    }
    \new Lyrics \lyricsto "m" { Round and round the waltz goes, old and slow. }
  >>
  \layout { indent = 0 }
}
```

comes back cropped to the music, as PDF, EPS, SVG or PNG — this is the SVG:

<img src="https://raw.githubusercontent.com/wspringer/lilypond-mcp/main/docs/example.svg" alt="Four bars of a waltz in G major with chord names and lyrics, engraved by LilyPond" width="600">

(Source in [`docs/example.ly`](docs/example.ly).)

<br>

[![Works great with Sidekick for InDesign](docs/sidekick-banner.svg)](https://sidekick.eastpole.nl?utm_source=github&utm_medium=readme&utm_campaign=lilypond-mcp)

<br>

## Engine

Engraving runs a WebAssembly build of LilyPond from
[lilypond-wasi](https://github.com/wspringer/lilypond-wasi) releases,
pinned in `engine.json` and executed in a child Node process via
`node:wasi`. Node 22 or newer: the engine uses WebAssembly exception
handling that V8 first shipped in Node 22. (The `node:wasi` fast-call
regression in Node 22.21.1+ —
[nodejs/node#59600](https://github.com/nodejs/node/pull/59600) — is
sidestepped automatically with `--no-turbo-fast-api-calls`.)

The engine trails lilypond-wasi's stable releases: a weekly workflow
re-pins `engine.json` to the newest release, engraves with it as a real
consumer, and opens a PR.

## Development

```
npm install
npm run build     # tsc → dist/
npm test          # engraves real snippets with the wasm engine*
```

\* The tests need an engine dir and skip without one. Either download the
pinned release into the cache — `node scripts/assemble-engine-dir.mjs`
prints the dir — or build one from a lilypond-wasi checkout:
`./test/assemble-engine-dir.sh /tmp/engine-dir ../lilypond-wasi stable`.
Then `LILYPOND_MCP_ENGINE_DIR=<dir> npm test`.

Releases: conventional commits → Knope bot release PR → merge → npm
publish via OIDC trusted publishing (no tokens).

## Privacy Policy

Everything happens on your machine. LilyPond sources are engraved locally
by the bundled WebAssembly engine; neither your sources nor the generated
assets ever leave your computer, and the server collects no data — no
telemetry, no analytics, no accounts.

The npm package makes exactly one kind of network request: downloading
the pinned engine release from GitHub on first use (verified against
checksums, cached under `~/.cache/lilypond-mcp`). The desktop extension
(`.mcpb`) ships the engine inside the bundle and makes no network
requests at all.

Generated assets are written to the `output_dir` you choose and stay
under your control; the server retains nothing else. Questions:
[wilfred@eastpole.nl](mailto:wilfred@eastpole.nl) or the
[issue tracker](https://github.com/wspringer/lilypond-mcp/issues).

## Licence

MIT. The server runs a GPL-licensed engraver (the lilypond-wasi
WebAssembly build of GNU LilyPond) as a separate program, and the npm
package contains none of its bytes — see
`LICENSING.md` for the analysis and the design rules that keep that
boundary clean.

TDQS

A4.3/5.0

Scored across 3 tools

Disambiguation5/5

The three tools have clearly distinct purposes: engrave_file operates on a .ly path, engrave_code takes inline source, and lilypond_version reports the engine version. The descriptions explicitly contrast the two engrave tools, reducing any misselection risk.

Naming Consistency4/5

engrave_file and engrave_code share a consistent verb_noun pattern, but lilypond_version breaks it with a noun_noun form. The deviation is minor and still readable as a cohesive set.

Tool Count4/5

Three tools is a tight, focused scope appropriate for a single-purpose LilyPond engraver wrapper. It's on the lean side but each tool earns its place without redundancy.

Completeness4/5

Core workflow (engrave file, engrave inline, check version) is fully covered with no dead ends for typical use. A preview/validate-only or batch operation is absent, but agents can work around this via engrave_code plus engrave_file.

Maintenance

ActivityMaintained
ResponsivenessResponsive