comparer-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 MusicXML strings → structured ComparisonResult JSON |
compare_musicxml_files |
Full diff of two MusicXML file paths (.musicxml, .xml, or .mxl) → 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; returns reference_annotated_musicxml + target_annotated_musicxml, chainable straight into render-mcp's render_to_pdf/render_to_image |
health_check |
Server status — verifies music21 is importable and runs a self-comparison smoke test |
list_capabilities |
Server metadata per project conventions |
// 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",
"measure_range": [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", ...}}
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
"part_filter": ["Soprano"], // compare only named parts (case-insensitive)
"measure_range": [1, 32] // compare only measures 1-32, inclusive
}
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) |
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file comparer_mcp-0.1.0.tar.gz.
File metadata
- Download URL: comparer_mcp-0.1.0.tar.gz
- Upload date:
- Size: 155.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f777dc03a42e8f5f4c399fe4fc1db9f0ec1e9095b2436272db2288d42edcbc2b
|
|
| MD5 |
60c31c9f3b2887204c0ef630988183fe
|
|
| BLAKE2b-256 |
3fe3743b84ea75834a08058c2e33865a1d51a02dc8804ce2ab41136f54aa2296
|
Provenance
The following attestation bundles were made for comparer_mcp-0.1.0.tar.gz:
Publisher:
publish.yml on raulkivi/music-assistant
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
comparer_mcp-0.1.0.tar.gz -
Subject digest:
f777dc03a42e8f5f4c399fe4fc1db9f0ec1e9095b2436272db2288d42edcbc2b - Sigstore transparency entry: 2485434577
- Sigstore integration time:
-
Permalink:
raulkivi/music-assistant@532f406340d23b1bfe39a7ca15057a06f8330cf3 -
Branch / Tag:
refs/tags/comparer-mcp/v0.1.0 - Owner: https://github.com/raulkivi
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@532f406340d23b1bfe39a7ca15057a06f8330cf3 -
Trigger Event:
push
-
Statement type:
File details
Details for the file comparer_mcp-0.1.0-py3-none-any.whl.
File metadata
- Download URL: comparer_mcp-0.1.0-py3-none-any.whl
- Upload date:
- Size: 22.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6641bd23b67639b6a4edd8da7d8946dd52edefbe318fd4f3ebb21dc69fd96228
|
|
| MD5 |
c3cf58b63f0961398cff19eb3579403f
|
|
| BLAKE2b-256 |
b5f78948c691cd9a770f6f124451336255c9a8e2d122b103ce20ec60b864e51f
|
Provenance
The following attestation bundles were made for comparer_mcp-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on raulkivi/music-assistant
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
comparer_mcp-0.1.0-py3-none-any.whl -
Subject digest:
6641bd23b67639b6a4edd8da7d8946dd52edefbe318fd4f3ebb21dc69fd96228 - Sigstore transparency entry: 2485434659
- Sigstore integration time:
-
Permalink:
raulkivi/music-assistant@532f406340d23b1bfe39a7ca15057a06f8330cf3 -
Branch / Tag:
refs/tags/comparer-mcp/v0.1.0 - Owner: https://github.com/raulkivi
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@532f406340d23b1bfe39a7ca15057a06f8330cf3 -
Trigger Event:
push
-
Statement type: