Dorico Maestro
Dorico Maestro is a local MCP server that lets an AI assistant read, write, analyze, and export music in an open Steinberg Dorico project — and compose or analyze scores entirely offline.
Connect to Dorico over local WebSocket and read live app status (
connect_to_dorico,get_status); safe to reconnect, needs one-time user authorization.Inspect the active selection's durations, dots, articulations, accidentals, and event types (
read_selection); pitches require a MusicXML export.Write music at the caret: single notes/chords (
add_notes), rests (add_rest), or full multi-part scores from a ScoreSpec (write_score,render_to_dorico, with dry-run planning).Transpose selected notes chromatically, diatonically, or by octave (
transpose); key/time signatures are refused here with alternatives, since API can't type into popovers (set_key_signature,set_time_signature,open_popover).Navigate and control the workspace: scroll the viewport (
navigate), move the caret (goto_bar), switch Dorico modes (switch_mode), and run transport (playback).Export the current or all layouts to PDF unattended (
export_pdf) and save the open project in place (save).Work offline with files: generate (
export_musicxml), import (import_musicxml), read MusicXML measure-by-measure (read_score), read .dorico metadata/flows/players (read_project_info), and snapshot the open score (read_open_score).Analyze music without Dorico: Roman-numeral harmony (
analyze_harmony), voice-leading audits (check_voice_leading), first-species counterpoint checks (check_counterpoint), functional next-chord suggestions (suggest_next_chord), and instrument range lookups (instrument_range).Discover and dispatch the broader Dorico command catalog via
search_commands,run_command, andscore_schema, with destructive commands blocked unless explicitly confirmed.
Provides tools to control Steinberg Dorico through its Remote Control API, enabling note input, rests, transposition, mode switching, playback control, and score edits in an open Dorico project.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Dorico Maestro Add a C major chord in the piano part"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Dorico Maestro
An AI composition and orchestration partner for Steinberg Dorico®. Speak in natural language to write notes directly into your score, audit voice leading, analyze harmony, check instrument ranges, and manage engraving layouts, right inside your open project.
Stay in the creative flow
Composing and engraving music in a notation program requires deep focus, but constantly breaking your train of thought to navigate popovers, click through staves, and proofread voice leading can pull you out of the zone.
Dorico Maestro acts like an assistant orchestrator and copyist sitting beside you at the scoring desk:
Keep your eyes on the score: Dorico stays open in front of you. You watch the notation appear, evaluate voicing and balance, and make the musical decisions.
Natural scoring dialogue: Ask for chord voicings, chorale harmonizations, counterpoint audits, transpositions, or layout adjustments in plain musical language.
Real notation, real scores: Nothing is simulated or pre-rendered. Everything is written directly into your active Dorico project through the live caret or standard MusicXML as editable Steinberg Dorico notation events.
Related MCP server: mcp-score
How it feels at the scoring desk
Here is what collaborating with Maestro looks like during a scoring session:
1. Score Writing & Harmony
You: "Write a 4-part chorale phrase in D minor at the caret. Soprano starts on F4 and moves step-wise down to D4. Build a classical SATB voicing with smooth contrary motion."
Assistant: Entered a 4-bar phrase in D minor across Soprano, Alto, Tenor, and Bass staves:
Soprano: F4 (half note) -> E4 (quarter note) -> D4 (half note).
Alto / Tenor: Inner voices provide full triadic harmony with step-wise motion.
Bass: Contrary root motion resolving firmly to the final tonic cadence.
Verification: Audited voice leading: no parallel fifths or octaves detected.
Notes are in your score and ready to play.
2. Voice Leading & Counterpoint Audit
You: "Check the active string quartet passage for voice crossing, parallel octaves, and unresolved leading tones."
Assistant:
Audited the four parts using local music theory analysis.
Result: Detected parallel octaves between Violin 2 and Cello on beat 3 of bar 14 (A3->B3 and A2->B2).
Voice Leading: Unresolved leading tone in Viola (G#4 steps down to E4 instead of resolving up to A4).
Recommendation: Resolve Viola up to A4 to complete the cadence and hold Violin 2 on E4 to eliminate the octave parallel.
3. Orchestration & Engraving
You: "Check if the Cor Anglais part is within comfortable range, transpose the French Horn in F down a minor third, and export the current layout to PDF."
Assistant:
Verified Cor Anglais compass: all notes fall between G3 and C6 (well within comfortable orchestral range).
Transposed the French Horn selection down a minor third using chromatic transposition with flat-enharmonic spelling.
Exported the current layout directly to PDF into your project directory without opening a modal dialog.
What you can do
Workflow | Example prompt | What Maestro does |
✍️ Caret Note Entry | "Input a 4-bar melody at the caret in G major with dotted quarters and eighths" | Steps through the score at the caret, sets durations and pitches, applies accidentals and ties |
🎼 Full-Score Writing | "Create a string quartet sketch with Violin 1, Violin 2, Viola, and Cello" | Generates complete multi-part scores with staves, voices, key signatures, and meters |
🔍 Harmony & Analysis | "Analyze the Roman numeral harmony of this progression in E-flat major" | Performs local key detection, chord labeling, and harmonic analysis |
📐 Voice Leading & Rules | "Audit this counterpoint passage against first-species voice leading rules" | Identifies parallel fifths, octaves, voice crossing, and forbidden melodic leaps |
🎺 Orchestration & Range | "Check whether the Trumpet 1 line exceeds the comfortable orchestral range" | Validates instrument compasses and warns of difficult register extremes |
🖨️ Engraving & Layout | "Switch to Print mode and export the full score layout as PDF" | Navigates Dorico modes, switches views, and runs unattended PDF exports |
📂 Offline Project Inspection | "Read the flows, players, and metadata from this .dorico file" | Inspects |
📖 Reading Your Open Score | "Read the score I have open, then add a viola line that fits" | Inspects the active flow via one-click MusicXML export to extract key, meter, bar count, and upbeat presence for exact subsequent caret operations |
🔄 Enharmonic & Transpose | "Transpose selected notes up a whole tone and respell using sharps" | Dispatches diatonic/chromatic transpositions and enharmonic respelling |
Why Maestro is reliable
Most AI music tools generate raw MIDI or static audio files without understanding the underlying notation rules.
Dorico Maestro is built on a robust, safety-first architecture:
Lifecycle & Caret Safety: Note input commands use strict session context managers (
try ... finally: NoteInput.Exit). Even if an operation fails or arguments are malformed, Dorico is never left stranded in an open note-input state.Destructive Command Guard: Commands that could discard musical work (
Edit.Delete,File.Close,File.Quit) are automatically blocked unless explicitly authorized withconfirm=True.Overwrite Mode Transparency: Because Dorico defaults to Overwrite mode and the Remote Control API cannot read bar contents back, Maestro monitors
noteInputModeand flagsdisplaces_existing: truein its response whenever notes land in Overwrite mode.Data-Driven & Verified: Built on a comprehensive catalog of 348 commands, with 190 of 348 commands verified live against Dorico 6.
Dual Writing Paths: Offers direct live caret input for fast interactive editing, and native MusicXML generation powered by music21 for complete multi-voice orchestral scores.
Quick start
You need Steinberg Dorico 4, 5, or 6 and uv. No repository, no virtual environment, no Python knowledge.
1. Install it
uv tool install git+https://github.com/romanstark/dorico-maestro.git2. Connect to Dorico
Open Steinberg Dorico and open any project. Dorico automatically listens on local WebSocket port
4560.On first connection, Dorico will display a permission prompt ("Do you want to allow Dorico Maestro to connect?"). Click Authorize. A persistent session token is saved automatically (
%APPDATA%\dorico-maestro\session_token.jsonon Windows,~/Library/Application Support/dorico-maestro/session_token.jsonon macOS) for future sessions.
3. Connect your AI assistant
Add Dorico Maestro to your MCP client configuration (Claude Desktop, Cursor, Antigravity IDE):
{
"mcpServers": {
"dorico-maestro": {
"command": "dorico-maestro"
}
}
}To run it without installing anything permanently, let uvx fetch it per launch:
{
"mcpServers": {
"dorico-maestro": {
"command": "uvx",
"args": ["--from", "git+https://github.com/romanstark/dorico-maestro.git", "dorico-maestro"]
}
}
}Open a project in Dorico, connect, and start with: "What mode is Dorico currently in?"
Your music stays yours
100% Local: All communication between your AI assistant and Dorico takes place over a local loopback WebSocket connection (
127.0.0.1:4560) on your machine.No Telemetry or Cloud Tracking: Dorico Maestro collects zero analytics and transmits no prompts, scores, or project files to any external server.
Local Storage: Your music,
.doricoarchives, and MusicXML files remain strictly on your local disk.
What stays in your hands
Dorico's Remote Control API is powerful, but certain tasks are intentionally reserved for you in the Dorico interface:
Task | Why | How to do it |
Typing into Popovers | Remote API opens popovers (Shift+D, Shift+T, etc.) but cannot type text into them | Maestro opens the popover and tells you the exact value; you press Enter, or use |
Modal Dialog Confirmation | Dorico requires manual confirmation for file open and export dialogs | Press Enter on the export prompt once, and |
Initial Connection Approval | Dorico security model requires one-time user authorization | Click Authorize on the Dorico permission prompt on first run |
Artistic Judgment | AI can check rules and draft parts, but musical intent and taste belong to you | Guide the score, listen to playback, and refine the music |
Current Verification Status
Verified live (190 commands), grouped by category:
NoteInput101 of 125: Pitches, durations, accidentals, articulations, chords, tuplets, and popovers.Window26 of 29: Window modes, layout views, panels, toolbar, and zoom controls.Play24 of 31: Transport commands, playhead placement, and mixer controls.View13 of 16: Viewport scrolling and zoom operations.NoteEdit8 of 8: Diatonic, chromatic, and octave transposition, plus enharmonic respelling.Edit6 of 16: Selection, copy, delete, and undo operations.EventEdit4 of 57: Selection navigation commands (Navigate*).File4 of 11: Project save and MusicXML import/export filters.UI3 of 7: Panel focusing commands.Print1 of 7: Unattended PDF export (Print.ExportCurrentLayoutAsPDF).
The remaining 12 categories are currently untested.
Testing & Environment
Empirical testing was conducted against Dorico 6.2.30 Elements on Windows 11. Dorico restricts commands depending on product tier (SE, Elements, Pro). Commands requiring Pro (such as Lua scripting) return kUnknownCommand and are classified as unavailable rather than broken.
Dorico Maestro exposes 30 tools and one resource (dorico://commands) for complete command discovery.
A note to Steinberg (and Dorico Pro users)
Dorico Maestro is an independent open-source project developed with a personal Dorico Elements license. Advanced features exclusive to Dorico Pro (such as Lua scripting via Script.*, full engraving options, and advanced dialog automation) are currently classified as unavailable or untested simply because they cannot be executed on Elements.
If anyone from Steinberg discovers this project and would like to support bringing first-class AI integration to Dorico: an NFR or developer license for Dorico Pro would be immensely appreciated to test, verify, and unlock the remaining Pro-specific commands for the entire community. Feel free to get in touch via GitHub or email at mail@romanstark.de!
Related Maestro projects
Live Maestro controls Ableton Live, musical timing, MIDI and sample placement.
UVR Maestro separates and restores local audio with UVR5 models.
Each server works independently. Use ordinary MIDI, audio and JSON files when combining their workflows.
Development
Working on the server itself rather than composing with it:
git clone https://github.com/romanstark/dorico-maestro.git
cd dorico-maestro
python -m venv .venvActivate the environment with .venv\Scripts\activate on Windows or source .venv/bin/activate on macOS and Linux, then install dependencies:
pip install -e ".[dev]"Verify tests without needing Dorico:
pytestPoint your MCP client at the checkout's interpreter while you work on it:
{
"mcpServers": {
"dorico-maestro": {
"command": "/absolute/path/to/dorico-maestro/.venv/Scripts/python.exe",
"args": ["-m", "dorico_maestro.server"]
}
}
}On macOS and Linux the interpreter is .venv/bin/python rather than .venv/Scripts/python.exe.
Documentation & Developer Resources
For architectural details, wire protocols, and contributor information:
docs/architecture.md – Internal architecture and multi-layer design
docs/protocol.md – Dorico Remote Control WebSocket protocol specification
docs/dorico_command_catalog.md – Complete base key-command catalog (340 commands)
CONTRIBUTING.md – Guidelines for contributing and command verification
THIRD-PARTY.md – Third-party notices, dependencies, and trademark attributions
LICENSE – AGPL-3.0 License
Legal & Trademarks
Steinberg and Dorico are trademarks or registered trademarks of Steinberg Media Technologies GmbH, registered in Europe and other countries. Ableton is a registered trademark and Live is a trademark of Ableton AG.
Dorico Maestro is an independent open-source project and is not affiliated with, endorsed, sponsored, or supported by Steinberg Media Technologies GmbH or Ableton AG. For full third-party notices, licenses, and attributions, see THIRD-PARTY.md.
Available Tools
30 toolsadd_notesA
Input notes at the caret, then leave note-input mode cleanly.
ONE insertion at the current caret. For a SEQUENCE of notes or chords over
time, use ``write_score`` / ``render_to_dorico`` with a ScoreSpec (a chord is
one event with >=2 pitches). Repeated ``add_notes`` calls do NOT chain: each
re-enters note input at the same spot, so successive chords stack on one beat.
Returns:
Result dictionary with the notes and duration entered, the note input mode
they landed in, whether an undo is available, and displaces_existing.
Note:
Read displaces_existing before treating this as an addition. Dorico has an
overwrite note input mode, and in it these notes replace the music already
at the caret instead of pushing it along. The mode belongs to the
application rather than to this call, so it is reported back and not chosen
here. get_status reads it beforehand.
Uses :class:`NoteInputSession`, so note input is always exited even on
error. Success indicates command acceptance (kOK). Verify note placement
via get_status, playback, or score inspection. Do not read can_undo as
that verification: entering note input alone already sets it.
Inputs: notes (required), as_chord, duration. Returns: object
add_restA
Input one rest at the caret, then leave note-input mode cleanly.
One rest at the current caret position, which then advances by that duration.
Note input is always exited afterwards, including when the call fails.
Returns:
Result dictionary with success, the duration entered, the undo flag and the
resulting mode.
Note:
Success means Dorico accepted the command (kOK), not that the rest is where
it was wanted (docs/protocol.md, 'Command Acceptance vs Effect'). Check with
get_status or by inspecting the score.
For a passage rather than one rest, put rest events in a ScoreSpec and use
write_score: repeated calls here do not chain, because each one re-enters
note input at the caret. Use goto_bar first to choose where it lands.
Do not read can_undo as evidence: entering note input alone already sets it.
Inputs: duration. Returns: object
analyze_harmonyARead-onlyIdempotent
Estimate the key of a ScoreSpec and name its chords in Roman numerals.
Entirely offline: the score is analysed as data and Dorico is not involved, so
this works on music that has never been in a project.
Returns:
Result dictionary with the key estimate, its confidence, and a Roman numeral
reading of each sonority.
Note:
Key estimation relies on pitch distribution. For short or highly chromatic
excerpts, verify the estimated tonal center before relying on Roman numerals.
This answers what the harmony is. For whether the voices move well between
those chords use check_voice_leading, for strict two-part exercises
check_counterpoint, and for what could come next suggest_next_chord.
Inputs: score (required). Returns: object
check_counterpointARead-onlyIdempotent
Check a two-part first-species counterpoint against the classic rules.
Entirely offline and read-only. Enforces the strict set: begin and end on a
perfect consonance, consonant verticals only, no consecutive perfect fifths,
octaves or unisons, no voice crossing, and one melodic climax in the
counterpoint.
Returns:
Result dictionary with one entry per issue, each naming the rule and the beat
it happens on. An empty list means the exercise passes.
Note:
Only first species is implemented, so passing any other species is refused
rather than checked loosely. The two lines must be the same length, since
note against note pairs them one to one.
These rules are stricter than ordinary part-writing on purpose. For a
chorale or a quartet use check_voice_leading, which applies the
common-practice rules to any number of voices.
Inputs: counterpoint (required), cantus_firmus (required), species. Returns: object
check_voice_leadingARead-onlyIdempotent
Audit a ScoreSpec for parallel fifths and octaves, overlaps and spacing.
Entirely offline and read-only: nothing in the score is changed and Dorico is
not involved. Written for part-writing of any number of voices, so a chorale or
a string quartet is the natural input.
Returns:
Result dictionary with one entry per issue found, each naming the rule, the
voices involved and where it happens. An empty list means nothing was found.
Note:
These are the common-practice rules, so a passage that breaks them on purpose
is reported too. The findings are advice and nothing here rewrites the music.
For a strict two-part species exercise, check_counterpoint applies the
stricter set of rules that belongs to it. For what the chords are rather than
how the voices move between them, use analyze_harmony.
Inputs: score (required). Returns: object
connect_to_doricoARead-onlyIdempotent
Connect to Dorico Remote Control and return application status.
Call this before anything else here: every other tool that talks to Dorico
needs the session this opens. It is safe to call again on an open connection.
Returns:
Result dictionary with the connection outcome and the first status snapshot.
Note:
On the first connection Dorico shows an authorization prompt inside the
application, and nothing proceeds until a person accepts it. The token is
then kept in AppData and later runs connect without asking.
Use get_status afterwards to re-read the state without reconnecting.
Inputs: no named parameters. Returns: object
export_musicxmlADestructive
Write a ScoreSpec to a MusicXML file on disk, without Dorico.
Entirely offline: no connection is opened and Dorico need not be running. Key,
time signature, clefs, dynamics and tempo all survive the round trip, which is
what makes this the way to set the two signatures no popover will accept.
Returns:
Result dictionary with the outcome and the path that was written.
Note:
A specified path is overwritten without confirmation. Omit the path to
write to a temporary file.
To get the file into Dorico afterwards, import_musicxml opens it as a new
flow, and write_score(method='musicxml') does both steps in one call. For a
printable file rather than an interchange one, use export_pdf.
Inputs: score (required), path. Returns: object
export_pdfA
Export the score to PDF unattended without opening a dialog.
Writes the file next to the .dorico project, named after the layout. This is the
unattended path. File.Export opens a modal dialog and is not usable from here.
Returns:
Result dictionary reporting whether the command was accepted.
Note:
Receiving kOK does not prove a file exists yet. Dorico runs commands on its
UI thread, so a request sent while a modal dialog is open waits in the queue
until the dialog closes. Tested against Dorico Elements 6.2.30. Check the
expected path on disk rather than trusting the return.
The all_layouts path is catalogued as untested, unlike the current-layout
path which is verified, so treat a multi-layout export as unproven and check
what actually landed. For an interchange format rather than a printable one,
use export_musicxml.
Inputs: all_layouts. Returns: object
get_statusARead-onlyIdempotent
Return Dorico's current pushed application status snapshot.
The state of the application rather than of the music: which mode is active,
whether note input is running, whether anything is selected, whether an undo is
available.
Returns:
Result dictionary carrying the merged status snapshot.
Note:
Dorico pushes status as deltas and this is the accumulated snapshot of them,
so it costs nothing to read and needs no command to be sent.
This is also how to check whether a command that answered kOK actually took
effect, since kOK only says the UI queue accepted it (docs/protocol.md,
'Command Acceptance vs Effect'). For the properties of the selected notes
rather than the state of the application, use read_selection.
Inputs: no named parameters. Returns: object
goto_barA
Move the caret to a bar and return the assumed position.
The position is assumed, not read. Dorico exposes no caret coordinates over the
Remote API, so this dead-reckons: it enters note input, rewinds to bar 1 of the
top staff, then steps forward to the bar, staff and beat asked for.
Returns:
Result dictionary with the assumed caret position and the caveat attached.
Note:
Because it counts rather than measures, the answer drifts from the truth if
anything moved the caret in between, and it cannot detect that. Treat it as
the position it aimed for, and confirm what was written with read_selection
or by inspecting the score.
This enters note input, so it is the call to make before add_notes or
add_rest. To scroll the view without touching the caret, use navigate.
Ask whether the flow opens with a pickup bar before relying on a bar
number, because Dorico leaves a pickup out of the count and this lands one
bar short without the pickup flag. The API cannot tell, so the person with
the score on screen has to say, or read an exported MusicXML with
read_score.
The move costs the same handful of commands at any flow length, so a long
flow is no less exact than a short one.
Inputs: bar (required), beat, staff, pickup. Returns: object
import_musicxmlA
Import a MusicXML file into Dorico via the Remote Control API.
Dispatches File.Open with MusicXMLImportFilter.
Returns:
Result dictionary with success, supported, attempted,
requires_confirmation, code, path and note.
Note:
An import arrives as a new flow rather than joining the one on screen, and
Dorico may raise a player assignment prompt that waits for a person. While
that prompt is open, later commands are accepted but sit unexecuted in the
queue (docs/protocol.md, "Modal Dialogs").
To add music into the flow already open, use write_score(method="caret").
Use export_musicxml to produce the file in the first place, or
write_score(method="musicxml") to do both steps in one call.
Inputs: path (required). Returns: object
instrument_rangeARead-onlyIdempotent
Look up a standard instrument compass, or test one pitch against it.
Two questions through one door: without a pitch it reports the range, with one it
reports whether that note is inside it. Entirely offline and read-only.
Returns:
Result dictionary with in_range when a pitch was given, or the lowest and
highest playable pitches when it was not.
Note:
These are the standard written ranges for a competent player, not the limits
of the instrument or of a particular one: professionals exceed them and
beginners do not reach them.
Worth calling before write_score when writing for an instrument, since
write_score with preflight left on runs the same check over a whole score and
reports what falls outside.
Inputs: instrument (required), pitch. Returns: object
open_popoverA
Open an input popover at the caret for manual text entry.
Opens the popover and stops there. Dorico's Remote API cannot populate popovers
directly, so this positions the caret if requested, triggers the
NoteInput.Create* command, and returns the required input format.
Returns:
Result dictionary with the popover kind, caret location, and typing
instructions.
Note:
Requires user interaction in Dorico. Opening a popover leaves it waiting
for keyboard input. Treat anything sent while it waits as unsafe: a modal
dialog leaves later commands accepted but unexecuted (docs/protocol.md,
'Modal Dialog Detection'), and a waiting popover has not been measured to
behave any better.
For an unattended write, put the marking in a ScoreSpec and use write_score:
dynamics and clefs are ScoreSpec fields, and a key or time signature goes in
through write_score(method='musicxml').
Inputs: kind (required), bar, staff, pickup. Returns: object
playbackAIdempotent
Start, stop or rewind Dorico playback.
Transport only: no note, dynamic or layout changes as a result.
Returns:
Result dictionary reporting whether the transport command was accepted.
Note:
Playback is the one way to hear whether a write landed, since kOK proves
only that the command was accepted (docs/protocol.md, 'Command Acceptance
vs Effect'). It makes sound, which matters if a person is in the room.
Use navigate to scroll the score without playing, and goto_bar to move the
caret. Neither of those moves the playhead.
Inputs: action, location. Returns: object
read_open_scoreA
Read the whole open score back by way of a MusicXML export.
The only route to the full contents of the score that is open. Reads over the
Remote API see the selection alone, so everything else here works blind. This
is what makes bar count, key, time signature and above all a pickup bar
knowable instead of guessed.
Returns:
Result dictionary with the parsed summary, the pickup finding, the last
bar number, and the file that was read.
Note:
Dorico's MusicXML export filter opens a modal dialog that requires user
confirmation. This tool triggers the export dialog and awaits the resulting
file in export_dir. Point the export dialog to export_dir once in Dorico to
enable one-key export confirmations on subsequent reads.
The result is a snapshot of the score at export time. Re-export after
edits to refresh score state.
Inputs: export_dir (required), trigger, wait_seconds. Returns: object
read_project_infoARead-onlyIdempotent
Read metadata, flows, and player rosters from a saved .dorico file.
Opens the project as the ZIP archive it is and reads the document and per-flow
metadata out of it. Entirely offline: Dorico need not be running, and this works
on a project nobody has open.
Returns:
Result dictionary with the document metadata, the flows, and the players.
Note:
This reads the project wrapper, not the music: for notes and rhythms, export
with export_musicxml and read that with read_score. For the project open in
Dorico right now, get_status reports its state and read_selection its
selection.
Inputs: path (required). Returns: object
read_scoreARead-onlyIdempotent
Read an existing MusicXML file measure by measure using music21.
Entirely offline and read-only. This is the way to see pitches, which Dorico
itself will not report over the Remote API.
Returns:
Structured dictionary with the metadata, parts, measures and note events.
Note:
Use the bars filter rather than reading everything when the question is
local: a full read of a long score returns every note of every part.
To see the music currently in Dorico, export it with export_musicxml first
and read that. read_project_info reads a .dorico project wrapper instead,
and read_selection reports the live selection without pitches.
Inputs: path (required), bars. Returns: object
read_selectionARead-onlyIdempotent
Read rhythmic and notation properties of the active selection.
Reports duration, dots, articulations, accidental and event type for whatever a
person has selected in Dorico. Nothing here can change the selection, so make it
in Dorico or move the caret with goto_bar first.
Returns:
Result dictionary with has_selection, and the properties when it is true.
An empty selection answers has_selection=False rather than an error.
Note:
Pitch and bar or beat position are not exposed by Dorico's Remote API, so
they are absent here and no amount of selecting will produce them. To read
pitches, export the flow with export_musicxml and read it with read_score,
or read a saved project with read_project_info.
Inputs: no named parameters. Returns: object
render_to_doricoA
Render a ScoreSpec through the caret path, or plan it without sending.
The lower half of write_score, exposed on its own for the two cases where that
matters: seeing the command plan before it runs, and skipping the preflight
checks and the MusicXML option that write_score adds on top.
Returns:
Result dictionary with the command plan when dry_run is true, or the
execution outcome when it is false.
Note:
Prefer write_score for ordinary composition: it runs the range and voice
leading checks first and can take the MusicXML path when a key or time
signature is needed. Come here to inspect what would be sent, or when the
caret path is specifically what is wanted.
With dry_run true this is a read: nothing is dispatched and Dorico need not
even be running.
Inputs: score (required), dry_run. Returns: object
run_commandA
Dispatch any catalogued Dorico command by ID, with optional parameters.
The escape hatch, reaching the whole catalogued command set rather than the
handful of tools here. What it can send is bounded by the catalog.
Returns:
Result dictionary with the command outcome and the catalog row status, which
says how well that command is actually established.
Note:
Read the registry_status in the answer before trusting the result: a row may
be verified, reachable, unavailable, broken or untested, and only verified
has been seen to work. Combined with kOK meaning acceptance rather than
effect (docs/protocol.md), a clean return from an untested row is weak
evidence. Check with get_status or by inspecting the score.
Prefer a dedicated tool where one exists: write_score, transpose,
switch_mode, playback and save all add validation or verification this does
not. Use search_commands to find a command ID and what it takes.
Inputs: command_id (required), params. Returns: object
saveADestructive
Save the open project over its existing file on disk.
Sends File.Save, which writes the project in place. A project that has never
been saved has no path to write to, so this cannot be relied on to save one:
check that the project has a file before treating it as a save.
Returns:
Result dictionary reporting whether the command was accepted.
Note:
When to use: Save changes in an already-saved project file on disk.
When NOT to use: Do not use if the project has never been saved (has no
path). To produce a file for external use without altering the open
project file, use export_pdf or export_musicxml instead.
Acceptance is not completion here either (docs/protocol.md, "Command
Acceptance vs Effect"), so a kOK does not prove the file on disk has changed.
This overwrites the .dorico project file in place.
Inputs: no named parameters. Returns: object
score_schemaARead-onlyIdempotent
Return the ScoreSpec input format write_score/render_to_dorico expect.
Call this instead of guessing or reading source. Unknown keys are rejected by
the parser rather than ignored, so the shape has to match exactly.
Returns:
Result dictionary with a copyable minimal example in both the flat and the
nested form, the allowed enum values (durations, articulations, dynamics,
clefs) and the indexing rules.
Inputs: no named parameters. Returns: object
search_commandsARead-onlyIdempotent
Search the Dorico command catalog by query, category, or status.
Entirely offline: the catalog ships with this server, so nothing is asked of
Dorico and it need not be running.
Returns:
Result dictionary with the total number of matches, a count per status, and
the matching commands with their IDs, parameters and documentation.
Note:
This is how to find a command ID and what it takes before sending it with
run_command, and how to see the status that tells you how far to trust it.
Only 'verified' rows have been observed to work.
Prefer a dedicated tool where one exists: much of what the catalog can reach
is already covered by write_score, transpose, playback, save and the rest,
with validation run_command does not perform.
Inputs: limit, query, status, category. Returns: object
set_key_signatureARead-onlyIdempotent
Report that a key signature cannot be set here, and what to use instead.
This writes nothing and always reports failure, for the same reason as
set_time_signature: Dorico takes a key signature only through an interactive
popover, and the Remote API cannot type into one.
Returns:
Result dictionary explaining the limitation and naming the alternatives.
Note:
For a person at the keyboard, open_popover(kind='key') opens the popover for
them to type into. For an unattended write, put the key in a ScoreSpec and
use write_score(method='musicxml') or export_musicxml, which set it through
the file rather than the UI.
Inputs: key. Returns: object
set_time_signatureARead-onlyIdempotent
Report that a time signature cannot be set here, and what to use instead.
This writes nothing and always reports failure, which is the honest answer
rather than a silent no-op: Dorico takes a time signature only through an
interactive popover, and the Remote API cannot type into one.
Returns:
Result dictionary explaining the limitation and naming the alternatives.
Note:
Two ways round it. For a person at the keyboard, open_popover(kind='time')
opens the popover for them to type into. For an unattended write, put the
time signature in a ScoreSpec and use write_score(method='musicxml') or
export_musicxml, both of which set it through the file rather than the UI.
Inputs: signature. Returns: object
suggest_next_chordARead-onlyIdempotent
Suggest functional continuations for a Roman numeral progression.
Entirely offline and read-only: this reasons about numerals as symbols and never
touches a score or Dorico.
Returns:
Result dictionary with the candidate next chords, each carrying its harmonic
function and why it follows.
Note:
Suggestions come from common-practice function, so they describe what usually
follows rather than what must. Nothing is written anywhere. To hear a
candidate, put it in a ScoreSpec and use write_score.
This takes numerals, not notes. To get numerals out of actual music, run
analyze_harmony first and feed its reading in here.
Inputs: key (required), progression (required). Returns: object
switch_modeAIdempotent
Switch which of Dorico's five workspace modes is on screen.
Changes the window and nothing in the music: no notes, no playback position and
no selection move as a result.
Returns:
Result dictionary reporting whether the switch was accepted.
Note:
Note entry needs write mode, so switch there before add_notes, add_rest,
goto_bar or write_score if the project might be in another one. get_status
reports which mode is active without changing it.
This is not navigation. To scroll the score use navigate, and to move the
caret use goto_bar.
Inputs: mode (required). Returns: object
transposeA
Transpose currently selected notes up or down in the score.
Directly modifies the score by altering the pitches of the current selection
in place. Relative and repeatable: calling it twice moves the selection twice
as far, with no absolute target pitch.
Returns:
Result dictionary with the command outcome and the catalog row
registry_status.
Note:
When to use: Shift existing selected notes by semitone, diatonic step,
or octave intervals.
When NOT to use: Do not use to enter new music (use write_score or
add_notes). Do not use to change key signatures (use set_key_signature).
Operates strictly on the active selection (reads are selection-only). If
nothing is selected, Dorico ignores the command and no notes are modified;
make a selection first or place the caret via goto_bar.
Parameters: direction sets shift orientation ('up'/'down'). chromatic
shifts by exact semitone when True or diatonically when False. octave=True
overrides chromatic and shifts by a full octave.
Maps to NoteEdit.Pitch{Up,Down}[Chromatic|Octave]. Read registry_status in
the returned result and verify the change in the score or via playback.
Inputs: direction (required), octave, chromatic. Returns: object
write_scoreA
Render a ScoreSpec into Dorico by caret entry or MusicXML import.
The main way to write music here, and the one to reach for whenever there is
more than a single beat to enter: add_notes and add_rest do not chain, because
each re-enters note input at the caret and stacks on the same beat.
Returns:
Result dictionary with the execution report, the preflight warnings, and the
caveats attached to what was dispatched.
Note:
Choose the method by what the score needs. 'caret' adds to the current flow
and leaves the rest of the project alone, but it cannot set a key or time
signature, because those go in through a popover no API can type into.
'musicxml' carries them, at the price of arriving as a new flow rather than
joining the current one.
Call score_schema first for the exact shape: unknown keys are refused rather
than ignored. Dorico must be in write mode, so use switch_mode if unsure.
Nothing is verified by the return, since kOK means accepted rather than
effective (docs/protocol.md), so check the result in the score.
The caret path repositions between staves by jumping to the start of the
flow, so it needs to know nothing about the flow's meter or length.
Inputs: score (required), method, preflight. Returns: object
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 30 tools
Most tools have clearly distinct purposes, but a few could be confused: write_score and render_to_dorico overlap significantly, and read_score, read_open_score, and read_project_info all deal with reading scores but from different sources. The descriptions do a good job of clarifying the differences, so misselection is unlikely for an agent that reads them carefully.
Tool names mostly follow a verb_noun pattern (e.g., search_commands, read_selection, write_score), but a few deviate: transpose, playback, and save are single verbs, and score_schema is a noun. These deviations are minor and the overall naming is still predictable and readable.
With 30 tools, this server exceeds the 25-tool threshold that the rubric considers too many. While the domain is complex and the tools cover a broad range of operations (editing, playback, export, analysis), the large surface area may overwhelm agents and increase the chance of misselection. The count is not extreme (not 50+), but it is still high.
The tool set covers the core workflows: writing notes (add_notes, add_rest, write_score), editing (transpose), reading (read_selection, read_score, read_open_score), export/import (export_pdf, export_musicxml, import_musicxml), and analysis (analyze_harmony, check_voice_leading, etc.). Gaps exist, such as inability to set key/time signatures directly and no explicit undo/redo, but these are addressed through documented workarounds (e.g., using write_score with MusicXML, or run_command). The surface is largely complete for the stated purpose.
Maintenance
Related MCP Connectors
Compose, mix, master and export in Logic Pro, REAPER or Ableton Live by talking to an AI engineer.
ScoreStarling is an all-in-one AI sheet music assistant. Ask for anything to do with sheet music: transcribe a song from audio, video or a link, read a PDF or photo, add chords, change the key, make it easier, turn it into guitar TAB or jianpu, or arrange it for another instrument. Every change lands on a real score you can hear, fix and download (PDF, MIDI, MusicXML, guitar TAB, jianpu, audio). Hosted remote server with OAuth sign-in. Free accounts get monthly credits and free previews; prices are at scorestarling.com/pricing. Setup: https://scorestarling.com/mcp
Score video, make music as an agent: 100+ AI instruments play your MIDI, audio, stems, Logic/Ableton
Score video as an agent: cue sheet from the cut, 100+ AI instruments play your MIDI, mix to picture
Related MCP Servers
- AlicenseNot gradedqualityBmaintenanceA Model Context Protocol server that provides programmatic control over MuseScore through a WebSocket-based plugin system, allowing AI assistants to compose music, add lyrics, navigate scores, and control MuseScore directly.142MIT
- AlicenseAqualityBmaintenanceAI-powered music notation server that lets you create and edit scores using natural language, integrating with MuseScore for live manipulation.2353 PyPI31MIT
- AlicenseBqualityDmaintenanceAn MCP server that enables natural language control of Steinberg Dorico music notation software through Claude Desktop or ChatGPT, offering tools for score creation, note input, notation, harmony analysis, and orchestration.5415MIT
- AlicenseNot gradedqualityDmaintenanceEnables AI tools to interact with constraint theory tools for pitch snapping, music generation, diagnostic analysis, and audio rendering through the Model Context Protocol.2MIT