About MusicXML Comparer
MusicXML Comparer is an MCP server for structured, music-aware comparison of two scores. It is intended for musicians and developers checking arrangements, OMR output, conversions, or score edits, with differences available from overall similarity down to individual notes.
Key features
- 01Compare MusicXML text or files
- 02Calculate similarity and summarize changes
- 03Show note-level differences by part or measure
- 04Generate readable comparison reports
- 05Export annotated MusicXML with color-coded differences
- 06Describe score structure and metadata
Use cases
- 01Compare editions or arrangements of a piece
- 02Evaluate OMR output against a reference score
- 03Check MusicXML conversion round trips
- 04Validate score edits and detect regressions
MCP support
Exposes score comparison, reporting, annotation, description, and health-check tools to MCP clients. Scores can be supplied inline or by path within configured allowed folders.
Configuration
Local server config is the same for every client. Copy it into your client’s MCP config file.
Tools
8Saved tool list · updated 2w ago
| Parameter | Type | Required | Description |
|---|---|---|---|
| options | object | No | Comparison options (docs/architecture.md §8). expand_repeats defaults to false (opt-in — see docs/PLAN.md 'Changed decisions'); normalize_pitch, ignore_articulations, part_filter, measure_range all default to off/unset. |
| options.part_filter | array | No | |
| options.measure_range | array | No | |
| options.expand_repeats | boolean | No | |
| options.normalize_pitch | boolean | No | |
| options.ignore_articulations | boolean | No | |
| target_xml | string | Yes | Target MusicXML document as a string, compared against the reference |
| reference_xml | string | Yes | Reference MusicXML document as a string |
Details
From the project READMEcomparer-mcp
MCP server for music-aware comparison of MusicXML files. Provides structured, multi-level diffs that report what changed between two scores — from global similarity down to individual notes.
What it does
- Version / arrangement comparison — Compare different editions, arrangements, or simplifications of the same piece
- OMR quality evaluation — Compare
omr-mcpoutput against a known-good reference - Round-trip fidelity — Verify MusicXML → ABC → MusicXML through
musicxml-abc-mcp - Regression testing — Detect regressions when OMR models or conversion logic change
- Score editing validation — Confirm LLM-driven edits only changed intended elements
Installation
bash install.sh
See SETUP.md for client config snippets (Claude Desktop, Cursor, Windsurf, Continue, Zed) and TROUBLESHOOTING.md if something doesn't work.
MCP Tools
| Tool | Description |
|---|---|
compare_musicxml | Full diff of two scores, inline or by path → structured ComparisonResult JSON |
compare_musicxml_files | Full diff of two MusicXML file paths (.musicxml, .xml, or .mxl) inside the allowed folders → structured ComparisonResult JSON |
quick_similarity | Similarity score (0.0–1.0) + summary statistics only, no per-note detail |
list_changes | Note-level diffs (operation != MATCH), optionally filtered by part name and/or measure range |
generate_comparison_report | Human-readable version comparison report — similarity headline, missing/extra parts and measures, key/time signature changes, note-level differences grouped by measure range (e.g. "transposed by 3 semitones in measures 17-24") |
export_annotated_musicxml | MusicXML with per-note color annotations marking the diff. Small results come back inline (reference_annotated_musicxml + target_annotated_musicxml); large ones are written to files and returned as *_path fields plus a score_summary (return: auto/path/inline). Pass the paths to render-mcp's render_to_pdf/render_to_image as musicxml_path |
describe_score | Title, key, time, tempo, parts (IDs, names, clefs, ranges), measures and duration of a score, without returning the score |
health_check | Server status — verifies music21 is importable and runs a self-comparison smoke test |
list_capabilities | Server metadata per project conventions |
Each score can be passed inline (reference_xml, target_xml) or as a file path
(reference_path, target_path; .musicxml, .xml or .mxl), exactly one of each pair. Paths
save copying whole scores through the conversation. They must be inside the output folder
(SHEET_MUSIC_OUTPUT_ROOT, default the working directory), one of the client's roots, or a folder
listed in SHEET_MUSIC_INPUT_ROOTS.
// Compare two MusicXML strings
{
"tool": "compare_musicxml",
"arguments": {
"reference_xml": "<score-partwise>...</score-partwise>",
"target_xml": "<score-partwise>...</score-partwise>"
}
}
// Compare two files
{
"tool": "compare_musicxml_files",
"arguments": {
"reference_path": "/path/to/reference.musicxml",
"target_path": "/path/to/omr_output.musicxml"
}
}
// Quick similarity check
{
"tool": "quick_similarity",
"arguments": {
"reference_xml": "<score-partwise>...</score-partwise>",
"target_xml": "<score-partwise>...</score-partwise>"
}
}
// → {"similarity_score": 0.87, "summary": {...}}
// Filtered changes, e.g. "what changed in the Alto, measures 17-24?"
{
"tool": "list_changes",
"arguments": {
"reference_xml": "<score-partwise>...</score-partwise>",
"target_xml": "<score-partwise>...</score-partwise>",
"part": "Alto",
"measures": [17, 24]
}
}
// → {"changes": [{"measure_number": 18, "operation": "PITCH_CHANGE", "part_name": "Alto", ...}], "count": 1}
// Human-readable report
{
"tool": "generate_comparison_report",
"arguments": {
"reference_xml": "<score-partwise>...</score-partwise>",
"target_xml": "<score-partwise>...</score-partwise>"
}
}
// → {"report": "Overall similarity: 87% (minor differences)\n...", "similarity_score": 0.87}
// Colored MusicXML diff export — chain straight into render-mcp to visualize
{
"tool": "export_annotated_musicxml",
"arguments": {
"reference_xml": "<score-partwise>...</score-partwise>",
"target_xml": "<score-partwise>...</score-partwise>"
}
}
// → {"reference_annotated_musicxml": "...", "target_annotated_musicxml": "...",
// "similarity_score": 0.87, "legend": {"PITCH_CHANGE": "#FF8800", ...}}
// For scores over 8 KB (return: "auto", the default):
// → {"reference_annotated_musicxml_path": ".../scores/annotated-reference_annotated_musicxml-....musicxml",
// "target_annotated_musicxml_path": "...", "similarity_score": 0.87, "legend": {...},
// "score_summary": {"reference_annotated_musicxml": {...}, "target_annotated_musicxml": {...}}}
// Paths instead of inline text
{
"tool": "quick_similarity",
"arguments": {
"reference_path": "scores/reference.musicxml",
"target_path": "scores/omr_output.mxl"
}
}
compare_musicxml, compare_musicxml_files, generate_comparison_report, and
export_annotated_musicxml all accept an optional options object:
{
"expand_repeats": false, // unfold repeats before comparing (default: false — opt-in)
"normalize_pitch": false, // transpose transposing instruments to concert/sounding pitch
"ignore_articulations": false, // exclude articulations from NoteInfo/diffs
"parts": ["Soprano"], // compare only these parts: IDs, names or voices ("P1", "S");
// part_filter is a deprecated alias
"measures": [1, 32] // only differences in measures 1-32, inclusive (scores are not
// cut); measure_range is a deprecated alias
}
Using the engine directly
The comparison engine (src/comparer_mcp/engine.py) has no MCP dependency and is fully usable as
a plain Python library, independent of the MCP server:
cd comparer-mcp
uv sync
from comparer_mcp.engine import compare, compare_files
# Compare two MusicXML strings
result = compare(reference_xml, target_xml)
# Compare two files (.musicxml, .xml, or compressed .mxl)
result = compare_files("reference.musicxml", "omr_output.mxl")
print(result.similarity_score) # float, 0.0-1.0
print(result.summary) # ComparisonSummary: parts/measures/notes matched, missing, changed
print(result.part_diffs) # list[PartDiff] -> list[MeasureDiff] -> list[NoteDiff]
Errors are raised as comparer_mcp.engine.ProcessingError (e.g. for a missing file or invalid
MusicXML), carrying a .error_code attribute (FILE_NOT_FOUND, INVALID_INPUT, etc.) matching
the format the MCP tools return.
Testing
# Unit tests
VIRTUAL_ENV= .venv/bin/pytest tests/ -v -m "not integration"
# Integration tests (real .mxl SATB fixtures shared with omr-mcp)
VIRTUAL_ENV= .venv/bin/pytest tests/ -v -m integration
# Install dependencies
uv sync
Dependencies
- music21 — MusicXML parsing, score object model, stream alignment
- numpy — distance matrix for note alignment (transitive via music21)
- mcp — MCP protocol
System requirements
- Python 3.11+
- No system libraries required
Phase status
| Phase | Status |
|---|---|
| Phase 1 — Core comparison (MVP) | Complete — 45/45 tests passing (43 unit + 2 integration) |
| Phase 2 — Rich detail (key/time signature diffs, voice-aware alignment, articulations) | Complete — 67/67 tests passing (65 unit + 2 integration) |
Phase 3 — MCP server & integration (server.py, all 6 tools, install.sh, SETUP.md, client config examples) | Complete — 94/94 tests passing (90 unit + 4 integration) |
Phase 4 — Advanced features (options wiring, generate_comparison_report, export_annotated_musicxml) | Complete — 134/134 tests passing (127 unit + 7 integration) |
UX Wave 2 — Score references (reference_path/target_path, return for the annotated export, describe_score) | Complete — 239 unit tests passing (+7 integration) |
Comments
New to MCP?
From what the protocol is to installing your first server, these answers cover most first questions.
An open protocol that defines how AI applications connect to external tools and data in a uniform way. Clients talk to the model; MCP servers provide tools, resources and prompt templates. They exchange standard messages, so one server can be reused by many clients.