Skip to main content
Glama
musescore

musescore

by bgng-co-jp
README.md
# musescore-mcp

## English summary

An MCP server (stdio, TypeScript) that drives the official **MuseScore Studio 4.7.6 headless** to inspect and fix score layout:
system and page breaks, systems of N measures, measure stretch, style values, element offsets, fitting a score to N pages,
overlap detection (including notes and staff lines, via the rendered SVG), and PNG / PDF export.
The original score file is never modified: the server works on a copy, and every change creates a new version that you can undo.

- Requirements: macOS, Node.js 22+, MuseScore Studio 4.7.6 at `/Applications/MuseScore 4.app`
- Setup: `git clone`, `npm ci`, then register it with Claude Code (see "起動と Claude Code への登録" below)
- License: MIT. MuseScore itself is a separate application (GPL-3.0) and is not included in this repository.

The rest of this README is written in Japanese.

## 概要

公式版 MuseScore Studio 4.7.6 を**画面なし**で呼び、譜面のレイアウト(改段・改ページ・小節の伸び縮み・スタイル・記号の位置)を
調べて直す MCP サーバー(stdio・TypeScript)。

- 設計と経緯: [docs/prototype-notes.md](docs/prototype-notes.md)(試作の作業メモ)

## しくみ

| やること | MuseScore のどの経路を使うか |
|---|---|
| 改段・改ページ・セクション区切り・nobreak・小節の伸び縮み・記号の位置ずらし・autoplace | 拡張機能 `extension/mcpb1`(macro, apiversion 1)を `mscore -j job.json --extension ...` で画面なしに実行 |
| スタイル | 変えるキーだけ書いた `.mss` を `mscore -S part.mss -o 次の版.mscz 今の版.mscz` で焼き込む |
| N 小節ごとに改段 | 拡張機能で `Score.makeIntoSystem`(段の固定, 4.6 以降)または改段 |
| N ページに収める | 詰める値の `.mss` を `-S` で読み込ませて拡張機能 `fit` を 1 回起動し、spatium を二分探索(試すたびに `endCmd(true)` で取り消す)→ 見つけた値を `-S` で焼き込み、ページ数を読み直して確かめる |
| 描画・情報 | `mscore -r <dpi> -j`(PNG/SVG)と `--score-meta` |

- 元の譜面ファイルには書き込まない。開いたときに作業フォルダへコピーし、`mscore -o` で 4.x 形式に正規化した版 v000 から始める。
- 変更のたびに新しい版(v001, v002…)を作る。`score_undo` は版を戻すだけ。
  版のファイル名は使い回さない(戻した後に変更すると、版の番号は同じでもファイルは新しい番号になり、捨てた版のファイルも残る)。
  描画・SVG・レイアウトのキャッシュは版のファイル名で引くので、捨てた版の画像や重なりが返ることはない。
- 同じ譜面(`score_id`)への呼び出しは、1 つずつ順に行う(前の呼び出しが終わってから、今の版と新しい版のファイル名を決める)。
  Claude Code が同じ譜面へ道具を並列に呼んでも、どの変更も消えない。別の譜面への呼び出しは待ち合わない。
- 保存は `score_save` だけ。既存のファイルは `overwrite=true` のときだけ上書きし、先に `<名前>.bak-日時ミリ秒-乱数` の控えを作る
  (控えは既存のファイルを上書きしない作り方なので、続けて上書きしても控えは毎回別に残る)。
  中身は同じフォルダの一時ファイルに作ってから、新規なら link(既にあれば失敗)、上書きなら rename で置き換える。
- 次の場所には保存しない。
  - 書き込み禁止のフォルダ(`MSCORE_MCP_READONLY_DIRS`、既定は試験譜面の testscores)と、作業フォルダ全体(`MSCORE_MCP_WORK`。ほかの譜面の版も含む)。
    保存先の親フォルダを `realpath` してから比べ、大文字小文字の違いも同じフォルダとみなす(祖先フォルダの dev/ino でも照合する)ので、
    記号リンクのフォルダ経由や `TESTSCORES` のような書き方でも拒む。
  - 保存先そのものが記号リンクのとき(リンク先を書き換えない)。
- 画面ありの MuseScore は起動しない。

## 道具

| 名前 | 何をするか |
|---|---|
| `score_open` | 譜面(.mscz/.mscx/.musicxml/.mxl)を開き、概要と段の並びを返す |
| `score_status` / `score_undo` | 版の一覧/版を戻す |
| `score_render` | ページを PNG で返す |
| `layout_inspect` | 段・ページ・小節・記号の位置と重なりの候補(`svg_overlaps=true` で音符・譜線との重なりも、動かす記号の id 付きで) |
| `layout_set_break` | 改段・改ページ・セクション区切り・nobreak を入れる/外す |
| `layout_set_stretch` | 小節の伸び縮み |
| `layout_systems_every` | N 小節ごとに改段(`method=lock`: 段の固定=メニューの「改段の追加/削除」と同じ/`method=breaks`: N 小節ごとの改段) |
| `layout_fit_pages` | N ページに収める(間隔を詰め、spatium を二分探索して焼き込む) |
| `style_list` / `style_get` / `style_set` | スタイルのキー一覧/読む/変える |
| `element_offset` | 記号をずらす(autoplace も切り替えられる) |
| `score_compare` | 2 つの版を比べる(段・小節・スタイル・画素) |
| `score_save` | 保存(.mscz / .pdf) |

## 重なりの見つけ方

| 経路 | 見えるもの | id |
|---|---|---|
| 拡張機能の bbox(`overlapCandidates`) | 記号どうし(強弱・歌詞・テキスト・ヘアピンなど) | あり |
| SVG の bbox(`svg_overlaps=true`) | 音符・符幹・譜線を含む、描かれたもの全部 | 記号の側に照合して付ける |

SVG の塊(class=型名、単位 1/1200 inch、ページの左上が原点)と記号(型、ページ座標の sp)を、
**ページ・型・座標**で照合する。換算は `1 sp = spatium[mm] × 1200 / 25.4`。
塊の面積の 50% 以上が記号の bbox に入るものを候補にし、重なりの大きい順に 1 対 1 で割り当てる。

重なりごとに `movable`(動かせる側の `id`・`suggestedDy`・`direction`・`alternatives`)を返す。
`suggestedDy` は、上下それぞれ相手から 0.3 sp 離し、そこで譜線・符幹・ほかの記号に当たればそれも越える位置まで進め、動きの小さい方。
そのまま `element_offset`(`id`, `dy`)に渡せる。`autoplace=true` の記号は MuseScore が押し戻すことがあるので、`rectAfter` で確かめる。

制限: 小さい譜表(mag≠1)の記号は座標の単位がずれるので、照合できないことがある(`unmatched` に数が出る)。

## セットアップ

前提: macOS、Node 22 以上、公式版 MuseScore Studio 4.7.6(`/Applications/MuseScore 4.app`)。
段の固定(`layout_systems_every` の `method=lock`)は MuseScore 4.6 以降の機能。スタイルの一覧は 4.7.6 に合わせている。

```sh
git clone https://github.com/bgng-co-jp/musescore-mcp.git ~/projects/musescore-mcp-server
cd ~/projects/musescore-mcp-server
npm ci
npm run typecheck
npm test            # MuseScore を使わない単体の試験
```

## 起動と Claude Code への登録

```sh
npx tsx src/server.ts      # stdio で待つ(手で試すとき)
```

Claude Code への登録(ユーザー範囲)。環境変数は `-e` で渡す。

```sh
claude mcp add --scope user musescore \
  -e MSCORE_SANDBOX=$HOME/projects/musescore-mcp/sandbox-config/server \
  -- $HOME/projects/musescore-mcp-server/node_modules/.bin/tsx $HOME/projects/musescore-mcp-server/src/server.ts
```

`MSCORE_SANDBOX` は普段の MuseScore の設定と分けるためのフォルダ(下の「環境変数」)。省略すると `<リポ>/.sandbox` を使う。

## 環境変数

すべて任意。未設定なら既定値を使う。一覧は [.env.example](.env.example) にもある。秘密の値は使わない。
サーバーは `.env` を読み込まない。Claude Code に登録するときは `claude mcp add -e 変数=値` で、手で起動するときはシェルの環境変数で渡す。

| 変数 | 既定値 | 内容 |
|---|---|---|
| `MSCORE_BIN` | `/Applications/MuseScore 4.app/Contents/MacOS/mscore` | mscore 本体 |
| `MSCORE_SANDBOX` | `<リポ>/.sandbox` | mscore の設定フォルダの親。4.7.6 には `-c`(設定フォルダの指定)が無いので、呼び出しごとに `<MSCORE_SANDBOX>/runs/<runId>` を作って `HOME` / `CFFIXED_USER_HOME` に向け、普段の MuseScore の設定と分ける |
| `MSCORE_MCP_WORK` | `<リポ>/work` | 譜面の作業コピー(版)と mscore の起動記録(`mscore-pids.log`)を置くフォルダ |
| `MSCORE_MCP_READONLY_DIRS` | `~/projects/musescore-mcp/testscores` | 保存を拒むフォルダ(`:` 区切り)。作業フォルダ(`MSCORE_MCP_WORK`)は指定しなくても常に拒む |
| `MSCORE_TIMEOUT_FACTOR` | `1` | タイムアウトの既定値(情報 120 秒・書き出し 180 秒・描画 300 秒・拡張機能 180 秒)に掛ける係数。大きい譜面なら `2` など |
| `MSCORE_MCP_MAX_PARALLEL` | `1` | 1 つのサーバーの中で同時に動かす mscore の数 |
| `MSCORE_MCP_KEEP_FAILED_RUNS` | `1` | 失敗した回の `runs/<runId>`(ログ入り)を残すか。`0` で残さない。成功した回は必ず消す。1 日より古いものは起動時に消す |

### 呼び出しごとの HOME(サーバーを同時に複数動かすため)

mscore は呼び出しごとに `<MSCORE_SANDBOX>/runs/<runId>/` を `HOME` にして起動する(`runId` = 時刻・サーバーの pid・乱数)。

- 拡張機能 `mcpb1` と引数 `params.js` はその中の `Library/Application Support/MuseScore/MuseScore4/extensions/mcpb1/` に置く。
  試作は設定フォルダの params.js を共有していたので、サーバーを 2 つ動かすと引数が混ざるおそれがあった。
- ログもその中にできる。失敗したときは**その回のログの ERROR 行**を応答に入れ、ログの場所も返す
  (共有の HOME だとログ名が起動秒単位なので、同じ秒に起動した別の回と同じファイルに混ざる)。
- 新しい HOME での起動に余分な時間はかからない(実測 0.67 秒 / 使い回し 0.65 秒、Amazing_grace の PDF 書き出し)。
- どの呼び出しにもタイムアウトが必須。超えたらプロセスグループごと SIGTERM → 3 秒後に SIGKILL。
  サーバーが終わるときも、自分が起動して動いている mscore だけを止める。起動と終了は `mscore-pids.log` に残る。
- 制限: mscore は別のプロセスグループで起動するので、サーバーの node が SIGKILL などで強制終了されると、その時に動いていた mscore は
  タイムアウトまで残ることがある(クライアントが正常に閉じた場合は止まる)。そのときは `mscore-pids.log` で end の無い pid を探して止める。

## 試験

| 種類 | コマンド | MuseScore | 場所 |
|---|---|---|---|
| 型検査 | `npm run typecheck` | 使わない | CI と手元 |
| 単体 | `npm test`(test/unit) | 使わない | CI と手元 |
| 端から端まで | `npm run e2e`(scripts/e2e.ts) | 使う(画面なし) | macOS の手元だけ |
| 追加 | `npm run extra`(scripts/extra.ts、goldberg の所要時間など) | 使う(画面なし) | macOS の手元だけ |

e2e は MCP の client SDK でこのサーバーを stdio で起動し、道具を順に呼んで確かめる。出力は `e2e-out/`。
試験譜面は `MSCORE_MCP_TESTSCORES`(既定 `~/projects/musescore-mcp/testscores`)から読むだけで、書き込まない。
試験譜面はこのリポジトリに含めていない(MuseScore の楽譜を各自で用意する)。

```sh
MSCORE_SANDBOX=$HOME/projects/musescore-mcp/sandbox-config/A-fix npm run e2e
E2E_SECTIONS=fixes MSCORE_SANDBOX=... npm run e2e   # 節を選ぶとき(b1,harden,tools,overlap,fixes)
```

## CI

GitHub では `.github/workflows/ci.yml` が型検査と単体の試験を走らせる。`.gitlab-ci.yml` は社内の GitLab 用(社内の共通テンプレートを使うので、社外からは使えない)。
Linux の runner に MuseScore が無いので、CI で走るのは型検査・単体の試験(社内の GitLab では秘密スキャンも)だけ。

## デプロイ

常駐サービスではない(Claude Code が必要なときに stdio で起動する)。配備は「clone → `npm ci` → `claude mcp add`」だけ。

## ライセンス

[MIT](LICENSE)。MuseScore Studio は別のアプリケーション(GPL-3.0)で、このリポジトリには含まない。
このサーバーは MuseScore を外部のプログラムとして呼び出す。`data/style-catalog.json` は MuseScore 4.7.6 のスタイルのキー名・型・既定値の一覧に、説明を付けたもの。

TDQS

A3.9/5.0

Scored across 15 tools

Disambiguation4/5

Most tools target a clearly distinct object+action pair (style_get reads values vs. style_list enumerates keys; score_status reports the current version vs. score_compare diffs two versions). The main overlap is between layout_set_break, layout_systems_every, and layout_fit_pages, which all manipulate breaks/spacing, but the descriptions explicitly differentiate them (systems_every clears existing breaks, fit_pages tunes spatium and distances). An agent could still occasionally hesitate between set_stretch and element_offset for spacing tweaks, but overall boundaries hold.

Naming Consistency4/5

Strong, predictable prefix-namespace pattern (score_*, style_*, layout_*, element_*) in consistent snake_case, with verb-ish suffixes (open, save, undo, render, compare, set, offset). Minor deviations: score_status and element_offset are noun_noun with no verb, and layout_systems_every / layout_fit_pages use unusual non-verb phrasing. Still readable and groupable at a glance.

Tool Count5/5

15 tools sits at the top of the ideal range but each maps to a concrete operation in the edit-render-save workflow (open/status/undo/compare/save plus style and layout subtools). Nothing appears redundant enough to drop, and the surface is not so large that selection becomes a burden.

Completeness4/5

Lifecycle coverage is solid: open, status, undo, compare, render, and save/export (mscz/pdf) plus full style read/list/set and layout inspection/break/stretch/system/fit/element-offset operations. Gaps are minor: no redo, no explicit close/reset of a score, no direct note or metadata editing, and no way to apply a style preset wholesale — all workable around given the layout-focused scope.