MCP — installation et référence

La version 1.11 ajoute un compagnon local pour piloter l’éditeur et les leçons exportées depuis un client MCP. Le fichier index.html reste utilisable seul pour le travail musical habituel. MCP nécessite le compagnon Node.js et une page ouverte.

Démarrage

  1. Installez Node.js 22 ou ultérieur. Le compagnon n’exige aucun paquet npm supplémentaire.
  2. Sous Windows, exécutez Configure_MCP.cmd. Il crée mcp-config.json avec les chemins absolus de cette installation.
  3. Importez cette entrée dans les réglages des serveurs MCP de votre client. Le client démarre node server.cjs en transport stdio.
  4. Demandez au client d’appeler abc_launch, puis de lire abc_instances et abc_capabilities. La page affiche son bouton MCP de connexion.

Launch_MCP.cmd ouvre aussi une session locale indépendante. Pour piloter depuis un client MCP, laissez ce client lancer son propre compagnon : fermez d’abord le lanceur indépendant. Ne lancez pas deux compagnons sur le même port.

Sur macOS/Linux : node configure.cjs, puis même configuration stdio. Les tests livrés ont été effectués sous Windows ; les autres systèmes n’ont pas été exécutés.

codex mcp add abc-editor-plus -- node "ABSOLUTE_PATH/server.cjs"

Port, bibliothèque et fichiers

Le compagnon écoute sur 127.0.0.1:8766. ABC_MCP_PORT change le port ; la valeur 0 demande un port aléatoire. Le port fixe conserve l’origine du navigateur d’une session à l’autre. La bibliothèque d’une ancienne ouverture file:// ou d’un autre port n’est pas partagée automatiquement : exportez/importez la bibliothèque ou les projets.

Les fichiers reçus sont conservés en mémoire une heure : 192 Mio au maximum par fichier et 512 Mio au total. abc_artifact read renvoie au plus 1 Mio par appel. save exige un chemin absolu dans le dossier Exports ; ABC_MCP_OUTPUT_DIR change ce dossier. L’écrasement doit être demandé explicitement. Il n’existe pas de lecture arbitraire du disque ni d’exécution de commandes système via MCP.

Commandes disponibles

13 outils MCP regroupent 114 actions nommées, les ressources musicales et le catalogue dynamique des contrôles. Les 407 opérations de l’audit initial sont un inventaire fonctionnel, pas 407 outils distincts. Les fonctions fines accessibles par boutons, menus et sélecteurs passent par abc_ui. Certains contrôles n’apparaissent qu’une fois leur fenêtre ouverte.

abc_server_metricsCPU cumulé du serveur, mémoire et durée de fonctionnement.
abc_instancesInstances d’éditeur et de leçon ouvertes.
abc_launchURL locale avec jeton temporaire ; ouverture facultative du navigateur.
abc_capabilitiesActions, schémas de paramètres et contrôles présents.
abc_readSource, modèle musical, partition SVG, annotations, mixage, diagnostics, état.
abc_edit / abc_controlTransactions musicales et commandes de transport, son, vues et réglages.
abc_uiCatalogue des contrôles ; activation, valeur ou fichier transmis comme artefact.
abc_importABC, MIDI, MusicXML, projet, bibliothèque, audio, SF2.
abc_exportABC, MIDI, WAV, SVG, PDF, projet, bibliothèque, original MIDI, diagnostics, performance, XM, MOD, leçon.
abc_jobÉtat, progression, rapport, annulation, récupération de l’export.
abc_lessonConfiguration, lecture, pas à pas, position, erreurs, MIDI, aide et essais synthétiques.
abc_artifactFichiers temporaires : upload base64, liste, lecture par blocs, sauvegarde contrôlée, suppression.

Modifier avec la bonne révision

Lisez l’instance et son état. Une écriture exige instanceId, documentId, expectedRevision et un requestId unique. La révision protège contre une édition concurrente. Les identifiants de notes sont valables pour la révision lue ; relisez le modèle après modification. Un identifiant d’événement écrit permet de désigner les occurrences d’une même note répétée. Une transformation temporelle ambiguë est refusée.

{
  "name": "abc_edit",
  "arguments": {
    "instanceId": "INSTANCE_ID",
    "documentId": "DOCUMENT_ID",
    "expectedRevision": 12,
    "requestId": "unique-edit-001",
    "operation": "notes.edit",
    "params": {
      "changes": [
        {
          "action": "update",
          "noteId": "note:12:0",
          "semitones": 1
        }
      ]
    },
    "preview": true
  }
}

Les valeurs de cet exemple sont à remplacer par celles de l’instance courante. Pour appliquer, utilisez un nouveau requestId et preview:false. Les aperçus existent pour les opérations indiquées par les capacités ; la prévisualisation MIDI décrit la proposition, mais la conversion et sa confirmation restent à vérifier au moment de l’application. L’annulation musicale utilise l’historique existant ; toutes les préférences et actions externes ne constituent pas une transaction annulable.

La répétition exacte d’une commande déjà terminée restitue sa réponse tant que son identifiant reste dans le cache des 500 dernières écritures de la session. Réutiliser cet identifiant avec d’autres paramètres est refusé. Après une interruption réseau, relisez l’état : annuler l’attente d’une requête n’annule pas nécessairement une action déjà exécutée.

Dialogues et autorisations

Une confirmation renvoie needs_user_action avec la question exacte. Après accord de l’utilisateur, le client peut la fournir dans confirmations, ou répondre via promptResponses. Les permissions audio, MIDI et microphone et les restrictions des fenêtres/plein écran restent celles du navigateur. Cliquez une première fois sur Lecture pour autoriser le son si nécessaire. abc_ui renvoie « dispatched » après activation : relisez le résultat et les diagnostics.

Exports et leçons

abc_export ouvre la fenêtre existante, avec progression, bandeau et décompte. Consultez abc_job get, puis collect après disponibilité. Un rapport de pertes XM/MOD doit être lu et accepté avec approveLosses:true. Le fichier devient un artefact à sauvegarder. cancel arrête la livraison. jobId:"current" permet de suivre un export démarré dans l’interface.

Les limitations musicales des formats demeurent : la commande n’autorise pas à supprimer silencieusement des voix. Une leçon se lance avec abc_launch et son artifactId. Elle conserve partition, sons et annotations. Les réponses simulate sont marquées « mcp-synthetic », sans avancer l’élève ni modifier ses erreurs réelles.

Confidentialité et transport

Le pont navigateur est limité à l’ordinateur, vérifie l’hôte, l’origine et un jeton aléatoire temporaire. Ne partagez pas l’URL contenant le jeton. Le client autorisé peut lire la musique et les annotations : ses propres règles de traitement des données s’appliquent. Les HTML exportés n’embarquent pas le jeton.

Le protocole MCP utilise stdio. L’HTTP local sert uniquement au pont avec le navigateur ; ce n’est pas un serveur MCP Streamable HTTP. Compatibilité testée : initialisation 2025-11-25 et découverte 2026-07-28. Les jobs sont exposés explicitement ; aucune extension Tasks ni abonnement push n’est annoncé.

CPU

abc_server_metrics expose les compteurs CPU en microsecondes, la mémoire et la durée de fonctionnement. Mesurez deux instants : 100 × ΔCPU / Δtemps donne le pourcentage d’un cœur. Les compteurs ne comprennent pas le navigateur. Le pont consulte les commandes toutes les 200 ms lorsqu’il est connecté ; déconnecté, cette boucle s’arrête. Consultez le rapport de mesures.

Aide et référence détaillée

Les nouvelles rubriques MCP et Leçon HTML de l’aide intégrée sont disponibles dans les vingt langues. Le guide illustré anglais conserve ses captures, thèmes et crédits. Les schémas ci-dessous sont ceux constatés lors de la vérification ; abc_capabilities reste la référence pour la page active.

source.replace
{
  "operation": "source.replace",
  "inputSchema": {
    "type": "object",
    "properties": {
      "text": {
        "type": "string"
      }
    },
    "required": [
      "text"
    ],
    "additionalProperties": false
  },
  "readOnly": false
}
source.patch
{
  "operation": "source.patch",
  "inputSchema": {
    "type": "object",
    "properties": {
      "patches": {
        "type": "array",
        "items": {
          "type": "object",
          "properties": {
            "start": {
              "type": "integer"
            },
            "end": {
              "type": "integer"
            },
            "text": {
              "type": "string"
            }
          },
          "required": [
            "start",
            "end",
            "text"
          ],
          "additionalProperties": false
        }
      }
    },
    "required": [
      "patches"
    ],
    "additionalProperties": false
  },
  "readOnly": false
}
source.selection
{
  "operation": "source.selection",
  "inputSchema": {
    "type": "object",
    "properties": {
      "start": {
        "type": "integer",
        "minimum": 0,
        "maximum": 100000000
      },
      "end": {
        "type": "integer",
        "minimum": 0,
        "maximum": 100000000
      }
    },
    "required": [
      "start",
      "end"
    ],
    "additionalProperties": false
  },
  "readOnly": false
}
project.open
{
  "operation": "project.open",
  "inputSchema": {
    "type": "object",
    "properties": {
      "project": {
        "type": "object"
      }
    },
    "required": [
      "project"
    ],
    "additionalProperties": false
  },
  "readOnly": false
}
project.new
{
  "operation": "project.new",
  "inputSchema": {
    "type": "object",
    "properties": {
      "template": {
        "type": "string"
      }
    },
    "required": [],
    "additionalProperties": false
  },
  "readOnly": false
}
project.tune
{
  "operation": "project.tune",
  "inputSchema": {
    "type": "object",
    "properties": {
      "index": {
        "type": "integer",
        "minimum": 0,
        "maximum": 10000
      }
    },
    "required": [
      "index"
    ],
    "additionalProperties": false
  },
  "readOnly": false
}
history.undo
{
  "operation": "history.undo",
  "inputSchema": {
    "type": "object",
    "properties": {},
    "required": [],
    "additionalProperties": false
  },
  "readOnly": false
}
history.redo
{
  "operation": "history.redo",
  "inputSchema": {
    "type": "object",
    "properties": {},
    "required": [],
    "additionalProperties": false
  },
  "readOnly": false
}
source.cleanup
{
  "operation": "source.cleanup",
  "inputSchema": {
    "type": "object",
    "properties": {},
    "required": [],
    "additionalProperties": false
  },
  "readOnly": false
}
notes.edit
{
  "operation": "notes.edit",
  "inputSchema": {
    "type": "object",
    "properties": {
      "changes": {
        "type": "array",
        "items": {
          "type": "object",
          "properties": {
            "action": {
              "enum": [
                "update",
                "delete",
                "add",
                "duplicate"
              ]
            },
            "noteId": {
              "type": "string"
            },
            "writtenEventId": {
              "type": "string"
            },
            "midi": {
              "type": "number",
              "minimum": 0,
              "maximum": 127
            },
            "time": {
              "type": "number",
              "minimum": 0,
              "maximum": 86400
            },
            "duration": {
              "type": "number",
              "minimum": 1e-06,
              "maximum": 86400
            },
            "velocity": {
              "type": "number",
              "minimum": 0,
              "maximum": 1
            },
            "track": {
              "type": "integer",
              "minimum": 0,
              "maximum": 1023
            },
            "semitones": {
              "type": "integer",
              "minimum": -127,
              "maximum": 127
            }
          },
          "required": [
            "action"
          ],
          "additionalProperties": false
        }
      }
    },
    "required": [
      "changes"
    ],
    "additionalProperties": false
  },
  "readOnly": false
}
notes.select
{
  "operation": "notes.select",
  "inputSchema": {
    "type": "object",
    "properties": {
      "noteIds": {
        "type": "array",
        "items": {
          "type": "string"
        }
      }
    },
    "required": [
      "noteIds"
    ],
    "additionalProperties": false
  },
  "readOnly": false
}
notes.copy
{
  "operation": "notes.copy",
  "inputSchema": {
    "type": "object",
    "properties": {},
    "required": [],
    "additionalProperties": false
  },
  "readOnly": false
}
notes.cut
{
  "operation": "notes.cut",
  "inputSchema": {
    "type": "object",
    "properties": {},
    "required": [],
    "additionalProperties": false
  },
  "readOnly": false
}
notes.paste
{
  "operation": "notes.paste",
  "inputSchema": {
    "type": "object",
    "properties": {},
    "required": [],
    "additionalProperties": false
  },
  "readOnly": false
}
notes.transpose
{
  "operation": "notes.transpose",
  "inputSchema": {
    "type": "object",
    "properties": {
      "semitones": {
        "type": "integer",
        "minimum": -24,
        "maximum": 24
      },
      "scope": {
        "enum": [
          "selection",
          "all"
        ]
      }
    },
    "required": [
      "semitones"
    ],
    "additionalProperties": false
  },
  "readOnly": false
}
voice.add
{
  "operation": "voice.add",
  "inputSchema": {
    "type": "object",
    "properties": {},
    "required": [],
    "additionalProperties": false
  },
  "readOnly": false
}
voice.delete
{
  "operation": "voice.delete",
  "inputSchema": {
    "type": "object",
    "properties": {
      "track": {
        "type": "integer"
      }
    },
    "required": [
      "track"
    ],
    "additionalProperties": false
  },
  "readOnly": false
}
voice.program
{
  "operation": "voice.program",
  "inputSchema": {
    "type": "object",
    "properties": {
      "track": {
        "type": "integer"
      },
      "program": {
        "type": "integer",
        "minimum": 0,
        "maximum": 127
      }
    },
    "required": [
      "track",
      "program"
    ],
    "additionalProperties": false
  },
  "readOnly": false
}
annotations.replace
{
  "operation": "annotations.replace",
  "inputSchema": {
    "type": "object",
    "properties": {
      "annotations": {
        "type": "array"
      }
    },
    "required": [
      "annotations"
    ],
    "additionalProperties": false
  },
  "readOnly": false
}
annotations.upsert
{
  "operation": "annotations.upsert",
  "inputSchema": {
    "type": "object",
    "properties": {
      "annotations": {
        "type": "array"
      }
    },
    "required": [
      "annotations"
    ],
    "additionalProperties": false
  },
  "readOnly": false
}
annotations.delete
{
  "operation": "annotations.delete",
  "inputSchema": {
    "type": "object",
    "properties": {
      "ids": {
        "type": "array",
        "items": {
          "type": "integer"
        }
      },
      "automatic": {
        "type": "boolean"
      },
      "all": {
        "type": "boolean"
      }
    },
    "required": [],
    "additionalProperties": false
  },
  "readOnly": false
}
annotations.auto
{
  "operation": "annotations.auto",
  "inputSchema": {
    "type": "object",
    "properties": {},
    "required": [],
    "additionalProperties": false
  },
  "readOnly": false
}
transport.play
{
  "operation": "transport.play",
  "inputSchema": {
    "type": "object",
    "properties": {
      "from": {
        "type": "number"
      }
    },
    "required": [],
    "additionalProperties": false
  },
  "readOnly": false
}
transport.pause
{
  "operation": "transport.pause",
  "inputSchema": {
    "type": "object",
    "properties": {},
    "required": [],
    "additionalProperties": false
  },
  "readOnly": false
}
transport.stop
{
  "operation": "transport.stop",
  "inputSchema": {
    "type": "object",
    "properties": {},
    "required": [],
    "additionalProperties": false
  },
  "readOnly": false
}
transport.seek
{
  "operation": "transport.seek",
  "inputSchema": {
    "type": "object",
    "properties": {
      "seconds": {
        "type": "number",
        "minimum": 0,
        "maximum": 86400
      }
    },
    "required": [
      "seconds"
    ],
    "additionalProperties": false
  },
  "readOnly": false
}
transport.source
{
  "operation": "transport.source",
  "inputSchema": {
    "type": "object",
    "properties": {
      "source": {
        "enum": [
          "abc",
          "midi"
        ]
      }
    },
    "required": [
      "source"
    ],
    "additionalProperties": false
  },
  "readOnly": false
}
transport.tempo
{
  "operation": "transport.tempo",
  "inputSchema": {
    "type": "object",
    "properties": {
      "bpm": {
        "type": "number",
        "minimum": 10,
        "maximum": 1000
      },
      "writeABC": {
        "type": "boolean"
      }
    },
    "required": [
      "bpm"
    ],
    "additionalProperties": false
  },
  "readOnly": false
}
transport.panic
{
  "operation": "transport.panic",
  "inputSchema": {
    "type": "object",
    "properties": {},
    "required": [],
    "additionalProperties": false
  },
  "readOnly": false
}
transport.reset
{
  "operation": "transport.reset",
  "inputSchema": {
    "type": "object",
    "properties": {},
    "required": [],
    "additionalProperties": false
  },
  "readOnly": false
}
loop.set
{
  "operation": "loop.set",
  "inputSchema": {
    "type": "object",
    "properties": {
      "startBeat": {
        "type": "number",
        "minimum": 0,
        "maximum": 1000000
      },
      "endBeat": {
        "type": "number",
        "minimum": 0,
        "maximum": 1000000
      },
      "enabled": {
        "type": "boolean"
      },
      "count": {
        "type": "integer",
        "minimum": 0,
        "maximum": 10000
      }
    },
    "required": [
      "startBeat",
      "endBeat"
    ],
    "additionalProperties": false
  },
  "readOnly": false
}
loop.clear
{
  "operation": "loop.clear",
  "inputSchema": {
    "type": "object",
    "properties": {},
    "required": [],
    "additionalProperties": false
  },
  "readOnly": false
}
loop.enabled
{
  "operation": "loop.enabled",
  "inputSchema": {
    "type": "object",
    "properties": {
      "enabled": {
        "type": "boolean"
      }
    },
    "required": [
      "enabled"
    ],
    "additionalProperties": false
  },
  "readOnly": false
}
loop.play
{
  "operation": "loop.play",
  "inputSchema": {
    "type": "object",
    "properties": {},
    "required": [],
    "additionalProperties": false
  },
  "readOnly": false
}
mix.set
{
  "operation": "mix.set",
  "inputSchema": {
    "type": "object",
    "properties": {
      "track": {
        "type": "integer"
      },
      "volume": {
        "type": "number",
        "minimum": 0,
        "maximum": 100
      },
      "muted": {
        "type": "boolean"
      },
      "solo": {
        "type": "boolean"
      },
      "visible": {
        "type": "boolean"
      }
    },
    "required": [
      "track"
    ],
    "additionalProperties": false
  },
  "readOnly": false
}
sound.set
{
  "operation": "sound.set",
  "inputSchema": {
    "type": "object",
    "properties": {
      "volume": {
        "type": "number",
        "minimum": 0,
        "maximum": 1
      },
      "reverb": {
        "type": "number",
        "minimum": 0,
        "maximum": 1
      },
      "chorus": {
        "type": "number",
        "minimum": 0,
        "maximum": 1
      }
    },
    "required": [],
    "additionalProperties": false
  },
  "readOnly": false
}
sound.restore
{
  "operation": "sound.restore",
  "inputSchema": {
    "type": "object",
    "properties": {},
    "required": [],
    "additionalProperties": false
  },
  "readOnly": false
}
sound.prepare
{
  "operation": "sound.prepare",
  "inputSchema": {
    "type": "object",
    "properties": {},
    "required": [],
    "additionalProperties": false
  },
  "readOnly": false
}
workspace.layout
{
  "operation": "workspace.layout",
  "inputSchema": {
    "type": "object",
    "properties": {
      "layout": {
        "enum": [
          "classic",
          "studio"
        ]
      }
    },
    "required": [
      "layout"
    ],
    "additionalProperties": false
  },
  "readOnly": false
}
workspace.language
{
  "operation": "workspace.language",
  "inputSchema": {
    "type": "object",
    "properties": {
      "language": {
        "type": "string"
      }
    },
    "required": [
      "language"
    ],
    "additionalProperties": false
  },
  "readOnly": false
}
workspace.theme
{
  "operation": "workspace.theme",
  "inputSchema": {
    "type": "object",
    "properties": {
      "theme": {
        "type": "string"
      }
    },
    "required": [
      "theme"
    ],
    "additionalProperties": false
  },
  "readOnly": false
}
workspace.zoom
{
  "operation": "workspace.zoom",
  "inputSchema": {
    "type": "object",
    "properties": {
      "percent": {
        "type": "number",
        "minimum": 50,
        "maximum": 120
      }
    },
    "required": [
      "percent"
    ],
    "additionalProperties": false
  },
  "readOnly": false
}
workspace.view
{
  "operation": "workspace.view",
  "inputSchema": {
    "type": "object",
    "properties": {
      "view": {
        "enum": [
          "score",
          "pianoroll",
          "tracker"
        ]
      }
    },
    "required": [
      "view"
    ],
    "additionalProperties": false
  },
  "readOnly": false
}
workspace.detach
{
  "operation": "workspace.detach",
  "inputSchema": {
    "type": "object",
    "properties": {
      "view": {
        "enum": [
          "score",
          "pianoroll",
          "tracker"
        ]
      }
    },
    "required": [
      "view"
    ],
    "additionalProperties": false
  },
  "readOnly": false
}
workspace.dock
{
  "operation": "workspace.dock",
  "inputSchema": {
    "type": "object",
    "properties": {
      "view": {
        "enum": [
          "score",
          "pianoroll",
          "tracker"
        ]
      }
    },
    "required": [
      "view"
    ],
    "additionalProperties": false
  },
  "readOnly": false
}
workspace.dockAll
{
  "operation": "workspace.dockAll",
  "inputSchema": {
    "type": "object",
    "properties": {},
    "required": [],
    "additionalProperties": false
  },
  "readOnly": false
}
workspace.exitFullscreen
{
  "operation": "workspace.exitFullscreen",
  "inputSchema": {
    "type": "object",
    "properties": {},
    "required": [],
    "additionalProperties": false
  },
  "readOnly": false
}
workspace.fullscreen
{
  "operation": "workspace.fullscreen",
  "inputSchema": {
    "type": "object",
    "properties": {},
    "required": [],
    "additionalProperties": false
  },
  "readOnly": false
}
workspace.economy
{
  "operation": "workspace.economy",
  "inputSchema": {
    "type": "object",
    "properties": {
      "enabled": {
        "type": "boolean"
      }
    },
    "required": [
      "enabled"
    ],
    "additionalProperties": false
  },
  "readOnly": false
}
score.options
{
  "operation": "score.options",
  "inputSchema": {
    "type": "object",
    "properties": {
      "options": {
        "type": "object"
      }
    },
    "required": [
      "options"
    ],
    "additionalProperties": false
  },
  "readOnly": false
}
score.follow
{
  "operation": "score.follow",
  "inputSchema": {
    "type": "object",
    "properties": {
      "voiceId": {
        "type": "string"
      },
      "enabled": {
        "type": "boolean"
      },
      "locked": {
        "type": "boolean"
      }
    },
    "required": [],
    "additionalProperties": false
  },
  "readOnly": false
}
tracker.set
{
  "operation": "tracker.set",
  "inputSchema": {
    "type": "object",
    "properties": {
      "row": {
        "type": "integer",
        "minimum": 0,
        "maximum": 1000000
      },
      "track": {
        "type": "integer"
      },
      "step": {
        "enum": [
          4,
          8,
          12,
          16,
          32
        ]
      },
      "mode": {
        "enum": [
          "off",
          "audition",
          "write"
        ]
      },
      "octave": {
        "type": "integer",
        "minimum": 0,
        "maximum": 8
      },
      "velocity": {
        "type": "integer",
        "minimum": 1,
        "maximum": 127
      },
      "advance": {
        "type": "integer",
        "minimum": 0,
        "maximum": 32
      },
      "stepPlayback": {
        "type": "boolean"
      }
    },
    "required": [],
    "additionalProperties": false
  },
  "readOnly": false
}
tracker.audition
{
  "operation": "tracker.audition",
  "inputSchema": {
    "type": "object",
    "properties": {
      "row": {
        "type": "integer",
        "minimum": 0,
        "maximum": 1000000
      }
    },
    "required": [],
    "additionalProperties": false
  },
  "readOnly": false
}
tracker.note
{
  "operation": "tracker.note",
  "inputSchema": {
    "type": "object",
    "properties": {
      "pitch": {
        "type": "integer",
        "minimum": 0,
        "maximum": 127
      }
    },
    "required": [
      "pitch"
    ],
    "additionalProperties": false
  },
  "readOnly": false
}
midi.connect
{
  "operation": "midi.connect",
  "inputSchema": {
    "type": "object",
    "properties": {},
    "required": [],
    "additionalProperties": false
  },
  "readOnly": false
}
midi.disconnect
{
  "operation": "midi.disconnect",
  "inputSchema": {
    "type": "object",
    "properties": {},
    "required": [],
    "additionalProperties": false
  },
  "readOnly": false
}
midi.panic
{
  "operation": "midi.panic",
  "inputSchema": {
    "type": "object",
    "properties": {},
    "required": [],
    "additionalProperties": false
  },
  "readOnly": false
}
microphone.start
{
  "operation": "microphone.start",
  "inputSchema": {
    "type": "object",
    "properties": {},
    "required": [],
    "additionalProperties": false
  },
  "readOnly": false
}
microphone.stop
{
  "operation": "microphone.stop",
  "inputSchema": {
    "type": "object",
    "properties": {},
    "required": [],
    "additionalProperties": false
  },
  "readOnly": false
}
audio.analyze
{
  "operation": "audio.analyze",
  "inputSchema": {
    "type": "object",
    "properties": {},
    "required": [],
    "additionalProperties": false
  },
  "readOnly": false
}
audio.apply
{
  "operation": "audio.apply",
  "inputSchema": {
    "type": "object",
    "properties": {},
    "required": [],
    "additionalProperties": false
  },
  "readOnly": false
}
audio.cancel
{
  "operation": "audio.cancel",
  "inputSchema": {
    "type": "object",
    "properties": {},
    "required": [],
    "additionalProperties": false
  },
  "readOnly": false
}
midi.cancelImport
{
  "operation": "midi.cancelImport",
  "inputSchema": {
    "type": "object",
    "properties": {},
    "required": [],
    "additionalProperties": false
  },
  "readOnly": false
}
midi.confirmImport
{
  "operation": "midi.confirmImport",
  "inputSchema": {
    "type": "object",
    "properties": {},
    "required": [],
    "additionalProperties": false
  },
  "readOnly": false
}
xml.confirmImport
{
  "operation": "xml.confirmImport",
  "inputSchema": {
    "type": "object",
    "properties": {},
    "required": [],
    "additionalProperties": false
  },
  "readOnly": false
}
library.list
{
  "operation": "library.list",
  "inputSchema": {
    "type": "object",
    "properties": {},
    "required": [],
    "additionalProperties": false
  },
  "readOnly": true
}
library.open
{
  "operation": "library.open",
  "inputSchema": {
    "type": "object",
    "properties": {
      "id": {
        "type": "integer"
      }
    },
    "required": [
      "id"
    ],
    "additionalProperties": false
  },
  "readOnly": false
}
library.save
{
  "operation": "library.save",
  "inputSchema": {
    "type": "object",
    "properties": {},
    "required": [],
    "additionalProperties": false
  },
  "readOnly": false
}
library.rename
{
  "operation": "library.rename",
  "inputSchema": {
    "type": "object",
    "properties": {
      "id": {
        "type": "integer"
      },
      "title": {
        "type": "string"
      }
    },
    "required": [
      "id",
      "title"
    ],
    "additionalProperties": false
  },
  "readOnly": false
}
library.duplicate
{
  "operation": "library.duplicate",
  "inputSchema": {
    "type": "object",
    "properties": {
      "id": {
        "type": "integer"
      },
      "title": {
        "type": "string"
      }
    },
    "required": [
      "id"
    ],
    "additionalProperties": false
  },
  "readOnly": false
}
library.delete
{
  "operation": "library.delete",
  "inputSchema": {
    "type": "object",
    "properties": {
      "id": {
        "type": "integer"
      }
    },
    "required": [
      "id"
    ],
    "additionalProperties": false
  },
  "readOnly": false
}
startup.run
{
  "operation": "startup.run",
  "inputSchema": {
    "type": "object",
    "properties": {
      "options": {
        "type": "object"
      }
    },
    "required": [
      "options"
    ],
    "additionalProperties": false
  },
  "readOnly": false
}
startup.cancel
{
  "operation": "startup.cancel",
  "inputSchema": {
    "type": "object",
    "properties": {},
    "required": [],
    "additionalProperties": false
  },
  "readOnly": false
}
startup.console
{
  "operation": "startup.console",
  "inputSchema": {
    "type": "object",
    "properties": {},
    "required": [],
    "additionalProperties": false
  },
  "readOnly": false
}
performance.start
{
  "operation": "performance.start",
  "inputSchema": {
    "type": "object",
    "properties": {
      "duration": {
        "type": "number",
        "minimum": 1,
        "maximum": 300
      }
    },
    "required": [
      "duration"
    ],
    "additionalProperties": false
  },
  "readOnly": false
}
performance.stop
{
  "operation": "performance.stop",
  "inputSchema": {
    "type": "object",
    "properties": {},
    "required": [],
    "additionalProperties": false
  },
  "readOnly": false
}
performance.reset
{
  "operation": "performance.reset",
  "inputSchema": {
    "type": "object",
    "properties": {},
    "required": [],
    "additionalProperties": false
  },
  "readOnly": false
}
lyrics.save
{
  "operation": "lyrics.save",
  "inputSchema": {
    "type": "object",
    "properties": {},
    "required": [],
    "additionalProperties": false
  },
  "readOnly": false
}
lyrics.addVerse
{
  "operation": "lyrics.addVerse",
  "inputSchema": {
    "type": "object",
    "properties": {},
    "required": [],
    "additionalProperties": false
  },
  "readOnly": false
}
lyrics.template
{
  "operation": "lyrics.template",
  "inputSchema": {
    "type": "object",
    "properties": {},
    "required": [],
    "additionalProperties": false
  },
  "readOnly": false
}
harmony.apply
{
  "operation": "harmony.apply",
  "inputSchema": {
    "type": "object",
    "properties": {},
    "required": [],
    "additionalProperties": false
  },
  "readOnly": false
}
source.fields
{
  "operation": "source.fields",
  "inputSchema": {
    "type": "object",
    "properties": {},
    "required": [],
    "additionalProperties": false
  },
  "readOnly": true
}
source.field
{
  "operation": "source.field",
  "inputSchema": {
    "type": "object",
    "properties": {
      "tag": {
        "type": "string"
      },
      "value": {
        "type": "string"
      },
      "occurrence": {
        "type": "integer",
        "minimum": 0,
        "maximum": 10000
      }
    },
    "required": [
      "tag",
      "value"
    ],
    "additionalProperties": false
  },
  "readOnly": false
}
source.share
{
  "operation": "source.share",
  "inputSchema": {
    "type": "object",
    "properties": {},
    "required": [],
    "additionalProperties": false
  },
  "readOnly": true
}
music.analyze
{
  "operation": "music.analyze",
  "inputSchema": {
    "type": "object",
    "properties": {},
    "required": [],
    "additionalProperties": false
  },
  "readOnly": true
}
music.convertTime
{
  "operation": "music.convertTime",
  "inputSchema": {
    "type": "object",
    "properties": {
      "seconds": {
        "type": "number"
      },
      "beat": {
        "type": "number"
      }
    },
    "required": [],
    "additionalProperties": false
  },
  "readOnly": true
}
music.writtenSlots
{
  "operation": "music.writtenSlots",
  "inputSchema": {
    "type": "object",
    "properties": {
      "track": {
        "type": "integer"
      }
    },
    "required": [],
    "additionalProperties": false
  },
  "readOnly": true
}
annotations.duplicate
{
  "operation": "annotations.duplicate",
  "inputSchema": {
    "type": "object",
    "properties": {
      "id": {
        "type": "integer"
      },
      "dx": {
        "type": "number",
        "minimum": -100,
        "maximum": 100
      },
      "dy": {
        "type": "number",
        "minimum": -100,
        "maximum": 100
      }
    },
    "required": [
      "id"
    ],
    "additionalProperties": false
  },
  "readOnly": false
}
annotations.select
{
  "operation": "annotations.select",
  "inputSchema": {
    "type": "object",
    "properties": {
      "id": {
        "type": "integer"
      }
    },
    "required": [
      "id"
    ],
    "additionalProperties": false
  },
  "readOnly": false
}
annotations.center
{
  "operation": "annotations.center",
  "inputSchema": {
    "type": "object",
    "properties": {
      "id": {
        "type": "integer"
      }
    },
    "required": [
      "id"
    ],
    "additionalProperties": false
  },
  "readOnly": false
}
lyrics.read
{
  "operation": "lyrics.read",
  "inputSchema": {
    "type": "object",
    "properties": {},
    "required": [],
    "additionalProperties": false
  },
  "readOnly": true
}
lyrics.set
{
  "operation": "lyrics.set",
  "inputSchema": {
    "type": "object",
    "properties": {
      "index": {
        "type": "integer",
        "minimum": 0,
        "maximum": 10000
      },
      "text": {
        "type": "string"
      }
    },
    "required": [
      "index",
      "text"
    ],
    "additionalProperties": false
  },
  "readOnly": false
}
pianoroll.zoom
{
  "operation": "pianoroll.zoom",
  "inputSchema": {
    "type": "object",
    "properties": {
      "factor": {
        "type": "number",
        "minimum": 0.1,
        "maximum": 10
      }
    },
    "required": [
      "factor"
    ],
    "additionalProperties": false
  },
  "readOnly": false
}
pianoroll.resetZoom
{
  "operation": "pianoroll.resetZoom",
  "inputSchema": {
    "type": "object",
    "properties": {},
    "required": [],
    "additionalProperties": false
  },
  "readOnly": false
}
pianoroll.selectVoice
{
  "operation": "pianoroll.selectVoice",
  "inputSchema": {
    "type": "object",
    "properties": {
      "track": {
        "type": "integer"
      }
    },
    "required": [
      "track"
    ],
    "additionalProperties": false
  },
  "readOnly": false
}
pianoroll.scroll
{
  "operation": "pianoroll.scroll",
  "inputSchema": {
    "type": "object",
    "properties": {
      "x": {
        "type": "number",
        "minimum": 0,
        "maximum": 10000000
      },
      "y": {
        "type": "number",
        "minimum": 0,
        "maximum": 10000000
      }
    },
    "required": [],
    "additionalProperties": false
  },
  "readOnly": false
}
pianoroll.follow
{
  "operation": "pianoroll.follow",
  "inputSchema": {
    "type": "object",
    "properties": {
      "enabled": {
        "type": "boolean"
      }
    },
    "required": [
      "enabled"
    ],
    "additionalProperties": false
  },
  "readOnly": false
}
pianoroll.legend
{
  "operation": "pianoroll.legend",
  "inputSchema": {
    "type": "object",
    "properties": {
      "enabled": {
        "type": "boolean"
      }
    },
    "required": [
      "enabled"
    ],
    "additionalProperties": false
  },
  "readOnly": false
}
transport.metronome
{
  "operation": "transport.metronome",
  "inputSchema": {
    "type": "object",
    "properties": {
      "enabled": {
        "type": "boolean"
      }
    },
    "required": [
      "enabled"
    ],
    "additionalProperties": false
  },
  "readOnly": false
}
sound.audition
{
  "operation": "sound.audition",
  "inputSchema": {
    "type": "object",
    "properties": {
      "pitch": {
        "type": "integer",
        "minimum": 0,
        "maximum": 127
      },
      "program": {
        "type": "integer",
        "minimum": 0,
        "maximum": 127
      }
    },
    "required": [
      "pitch"
    ],
    "additionalProperties": false
  },
  "readOnly": false
}
bank.load
{
  "operation": "bank.load",
  "inputSchema": {
    "type": "object",
    "properties": {
      "artifactId": {
        "type": "string"
      }
    },
    "required": [
      "artifactId"
    ],
    "additionalProperties": false
  },
  "readOnly": false
}
bank.inspectNote
{
  "operation": "bank.inspectNote",
  "inputSchema": {
    "type": "object",
    "properties": {
      "pitch": {
        "type": "integer",
        "minimum": 0,
        "maximum": 127
      },
      "program": {
        "type": "integer",
        "minimum": 0,
        "maximum": 127
      },
      "velocity": {
        "type": "number",
        "minimum": 0,
        "maximum": 1
      },
      "duration": {
        "type": "number",
        "minimum": 0.001,
        "maximum": 120
      },
      "isDrums": {
        "type": "boolean"
      }
    },
    "required": [
      "pitch"
    ],
    "additionalProperties": false
  },
  "readOnly": true
}
recovery.save
{
  "operation": "recovery.save",
  "inputSchema": {
    "type": "object",
    "properties": {},
    "required": [],
    "additionalProperties": false
  },
  "readOnly": false
}
recovery.clear
{
  "operation": "recovery.clear",
  "inputSchema": {
    "type": "object",
    "properties": {},
    "required": [],
    "additionalProperties": false
  },
  "readOnly": false
}
recovery.restore
{
  "operation": "recovery.restore",
  "inputSchema": {
    "type": "object",
    "properties": {},
    "required": [],
    "additionalProperties": false
  },
  "readOnly": false
}
modules.validate
{
  "operation": "modules.validate",
  "inputSchema": {
    "type": "object",
    "properties": {
      "format": {
        "enum": [
          "xm",
          "mod"
        ]
      },
      "artifactId": {
        "type": "string"
      }
    },
    "required": [
      "format",
      "artifactId"
    ],
    "additionalProperties": false
  },
  "readOnly": true
}
midi.importState
{
  "operation": "midi.importState",
  "inputSchema": {
    "type": "object",
    "properties": {},
    "required": [],
    "additionalProperties": false
  },
  "readOnly": true
}
audio.analysis
{
  "operation": "audio.analysis",
  "inputSchema": {
    "type": "object",
    "properties": {},
    "required": [],
    "additionalProperties": false
  },
  "readOnly": true
}
audio.close
{
  "operation": "audio.close",
  "inputSchema": {
    "type": "object",
    "properties": {},
    "required": [],
    "additionalProperties": false
  },
  "readOnly": false
}
help.open
{
  "operation": "help.open",
  "inputSchema": {
    "type": "object",
    "properties": {},
    "required": [],
    "additionalProperties": false
  },
  "readOnly": false
}

· ·

1.11 · Instrument lessons, startup options & MCP · 20 languages

Instrument guide · 1.11 Final

The 88-key piano shows selected voices: red keys sound during playback; amber keys are the next exercise target. Select several voices in the right panel; the primary voice anchors scrolling. Guitar and bass show suggested string/fret positions, not prescribed finger numbers. Frets rise toward the body at the bottom, with markers at 3, 5, 7, 9, 12 (double), 15, 17, 19, 21 and 24 (double). Twelve strings form six paired courses; lower four pairs sound octaves. Custom tuning uses MIDI pitches from low to high; blank restores the model. Winds have the mouthpiece at the bottom and separate thumb holes. One wind note at a time; unavailable fingerings are reported rather than guessed. Recorder: Baroque soprano C5–D6; whistle D5–C♯7; three-hole profiles include register and partial bell closure. Profiles vary by instrument: verify with your teacher. Display transposition changes diagrams, not sound or exercise answers. MIDI guitars and winds use note-on/off on the selected port and channels; all channels accepts separate-string channels. Audio-to-note exercise grading, breath controllers and pitch bends are not implemented. The microphone tuner measures one string, gives cents and an up/down direction; ±5 cents is in tune. A4 can be set from 430 to 450 Hz. It needs microphone permission, headphones and a clear sustained tone; close it to stop capture. Startup: voices=1,2; piano=true; instrument=guitar6/guitar12/guitar7/guitar8/guitar9/bass4/bass5/bass6/bass7/recorder/whistle/tabor/galoubet/txistu/off; instrument-transpose=0; instrument-tuning=40,45,50,55,59,64; reference-a=440. No microphone is started automatically. Automatic mode shows a percussion grid for a followed drum voice. Guitar tablature proposes string/fret positions; time labels are in seconds. Annotations remain on the standard score. Restore Classic layout, dock windows, reset panels and set interface size to 90%. The header holds the error count, reset and interface size. Transport times stay beside Stop and Set B; other settings occupy the second row. Instructions, MIDI connection, keyboard map and credits are on the right (☰). Click a piano key, score note or tablature note to audition; QWERTY/AZERTY and MIDI also work outside exercises. Release keys to stop. Clicks never answer an exercise. Keyboard/MIDI exercises still grade answers. Audio: the standalone lesson embeds the SF2 playback engine and shared samples for the instruments used, including their key and velocity zones, loops and envelopes. Notes are synthesized during playback, rather than exported as separate WAV files. No sound-bank download is needed. The 128 MiB limit applies to shared sample data, not song duration. Audition uses the selected instrument and stops after at most 2.5 seconds. The lesson embeds the editor’s ABC score renderer and SF2 player, not a pre-rendered score. Notation and shared instrument samples are compressed inside the single HTML file. Two musical lines are shown: the current line and the next one. Score zoom (50–300%) is independent of interface size; Follow keeps the playhead visible. The arrows browse lines with Follow disabled. The audio scheduler runs separately from the display, and only a few lines are kept in memory. The playback status is shown once beside the ? Help button, in both the editor and lesson. Startup and MCP options: score-zoom=150, score-view=focus or full. Score zoom and voice selection are in the right sidebar; ☰ opens it on narrow screens.

score-zoom50 … 300 (100)Score zoom
score-viewfocus | fullScore view
notationauto | score | tabScore notation: automatic, score or tablature.
voices1,2Voices to follow
pianotrue | falsePiano
instrumentoff | guitar6 | guitar12 | guitar7 | guitar8 | guitar9 | bass4 | bass5 | bass6 | bass7 | recorder | whistle | tabor | galoubet | txistuInstrument
instrument-transpose−24 … 24 (0)Display transposition (semitones)
instrument-tuning40,45,50,55,59,64Open strings (MIDI, low → high)
reference-a430 … 450 (440 Hz)Play reference
lesson.html?mode=step&voice=1&voices=1,2&instrument=guitar6&reference-a=440&score-zoom=150&score-view=focus&lang=en
lesson.html?mode=midi&instrument=whistle&notation=score&lang=en
lesson.html?help=true&lang=en

ABC Editor Plus

notationauto | score | tabScore notation: automatic, score or tablature.
tab-instrumentguitar6 … guitar9 / bass4 … bass7Guitar or bass model used for tablature.
tab-tuning40,45,50,55,59,64Open-string MIDI pitches, low to high, separated by commas.
reset-layouttrue | falseRestore Classic layout, dock windows, reset panels and set interface size to 90%.
index.html?notation=tab&tab-instrument=guitar6&lang=en
index.html?reset-layout=true&layout=studio&zoom=100&lang=en
index.html?help=true&lang=en

Reset default layout: Restore Classic layout, dock windows, reset panels and set interface size to 90%.

MCP

abc_edit: operation="score.notation"
params={"mode":"tab","instrument":"guitar6","tuning":"40,45,50,55,59,64"}
abc_edit: operation="workspace.resetLayout", params={}
abc_lesson: operation="configure"
params={"notation":"auto","instrument":"guitar6","voices":"1,2","piano":true,"reference-a":440,"score-zoom":150,"score-view":"focus"}
abc_read: resource="startup"
abc_capabilities

Ornament playback

Select ABC as the playback source; raw imported MIDI has no ornament signs. Play ornaments enables the same interpretation in the editor and HTML Lesson. ~ and !roll! play main–upper–main–lower–main, with short cuts. T / !trill! and extended trills alternate with the upper neighbour; mordents and turns use the appropriate neighbours. Key signatures, current accidentals and transposition determine auxiliary pitches. Arpeggios spread chords upwards. Ornaments fit inside the written duration, including tuplets and ties: repeats, A/B loops and score following keep their timing. Exercises require the written notes, not generated auxiliaries. Disable the option to hear the plain melody. Fermatas and bowing marks remain engraved without automatic tempo holds. Interpretation varies by instrument and tradition; write explicit notes for a different realization. Re-export older lessons. Startup: ornaments=true|false; MCP: ornaments.set, enabled=true|false.

~G3  !roll!G3  TG3  MG3  PG3  !turn!G3
!trill(!G3 A3 | !trill)!B3 c3 |
index.html?ornaments=true
lesson.html?ornaments=false
ornaments.set: {"enabled":true}

ABC-YuE2 export

Export → ABC-YuE2 prepares the selected tune for YuE2. Select the source of Vocal and Ins; unused parts become rests. Two independent monophonic lines are required, never a multi-track MIDI arrangement. Strict polyphony mode refuses chords and overlaps. Highest/lowest mode chooses a note at each attack and shortens an overlapping held note. Repeats and first/second endings are expanded using the editor’s playing order; ambiguous navigation is refused. Dotted durations are preserved; tuplets or other non-native rhythms require explicit quantization on a power-of-two grid. Short or irregular bars need matching meters, without adding silence. Native plans use one integer tempo. MIDI instructions are removed and sounding pitches are retained with explicit accidentals in K:C. Optional supported chord symbols belong in Vocal; without harmony use cot=melody, otherwise cot=full. Read the preview and adaptation counts before downloading through the progress window. The A/B option exports only that passage. Structural and note-event checks do not guarantee generated sound or model context capacity. MCP: yue2.prepare and abc_export format=yue2. Quantization keeps each selected note for at least one grid step; merged attacks are refused.

Notation and selected musical events are checked. Generated audio and the complete model input budget are not checked.

yue2.prepare: options={"vocal":0,"ins":null,"polyphony":"strict","rhythm":"exact"}
abc_export: format="yue2", options={"vocal":0,"ins":null}
python tools/yue2/abc_tools.py inspect exported-yue2.abc
yue2 generate --request song.json --abc-file exported-yue2.abc --cot melody --output output-song

Windows desktop edition

Windows 64-bit: use Setup for a per-user installation, or Portable without installation. Keep the portable executable in a writable folder. Its ABC-Editor-Plus-Data folder stores preferences, exports and MCP settings beside the executable; move both together. The installed edition stores its data in the Windows user profile. Uninstalling preserves that data.

File → Open imports supported music, audio and sound-bank files through the editor’s existing checks. Export from the editor as usual, then choose a destination in the Windows save dialog. Help → Application data opens the settings folder. Save project backups before closing. The HTML Lesson export remains an independent browser file.

MCP starts with the application and stops when it closes. No separate Node.js installation is required. Keep the desktop editor open; choose Help → Create MCP client configuration, then import the generated mcp-config.json entry into your client. The mcp-stdio.cjs argument connects that client to this desktop instance. It does not start a second editor. The browser companion and desktop companion remain separate.

Allow MIDI or microphone access only when needed. Ordinary editing and playback use the bundled resources locally; online links and optional transcription services require a connection. Detached windows share the editor’s session. The application remembers its window size. F11 switches full screen; the existing export window exits full screen automatically.

The desktop menus currently use English; the editor and this desktop help offer all 20 languages. No automatic updater is included. These executables are not digitally signed. Hardware MIDI, microphone input and audio latency depend on your devices and should be checked on the target computer.