Python library and MCP server for controlling Novation Circuit Tracks via MIDI
Circuit Tracks Tools exhibits significant inconsistencies in definition quality. Tool definitions are split across multiple files (src/circuit_mcp/server.py and webapp/js/agent/tools.js), with some tools appearing twice with different schemas. Descriptions are present but often generic (e.g., 'Disconnect from the Circuit Tracks' lacks context on consequences). Parameter descriptions exist but many lack constraints (e.g., 'port_name' has no validation hints). Schemas are partially visible but incomplete for several tools. The server lacks error handling guidance, recovery paths, and output schema documentation. No tool annotations (readOnlyHint/destructiveHint) are visible in the Python source, though some risk classifications are noted in the evaluation metadata.
Clear a pattern, removing all steps from all tracks.
Clear every track of a named pattern (the name keeps its slot).
Connect to the Circuit Tracks MIDI output port. Args: port_name: The exact MIDI port name from list_midi_ports output.
Check the current MIDI connection status.
Disconnect from the Circuit Tracks.
Reference for synth, drum, project and patch parameters, lookup tables, mod matrix, macros and the song format. Call with no section for the section list and best practices; call with a section before using complex tools so parameter names are exact. Sections: synth, patch, drums, project, lookup_tables, mod_matrix, macros, song_format, best_practices.
Duplicate tool definitions with inconsistent schemas. 'set_pattern' and 'set_track' appear twice (once in server.py, once in webapp/js/agent/tools.js) with different input schemas. This creates ambiguity for LLMs about which version to call and what parameters are valid.
Missing output schema documentation. No tool documents what fields it returns or their types. LLMs cannot plan downstream calls or extract required data (e.g., pattern IDs, connection status details) without seeing response structures.
Minimal error handling guidance. No tool description explains what happens on failure, what errors are retryable, or how to recover. E.g., 'connect' provides no guidance if the port name is invalid or the device is offline.
Inferred effective spec: <=2025-11-25.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-23 | D | 59 | <=2025-11-25 | v2 |
Read one named pattern in song format (length and per-track steps / automation).
List available MIDI input and output ports. Use this to find the Circuit Tracks port name before connecting. The Circuit Tracks typically appears as 'Circuit Tracks' or similar.
Named patterns with their slots, unnamed slots that hold data, the current song order and the pattern length in use.
Load a complete song (patterns, sounds, FX, mixer, song order) into the project, replacing it. The song format is the hardware server's: see get_parameter_reference("song_format"). CRITICAL: all patterns share one length (16 or 32); include "sounds" for both synths; synth p-locks use the step/track "macros" key, drum automation the track "params" key; mod matrix names are space separated ("filter frequency") while macro targets use snake_case parameter names ("filter_frequency"); gate max 16. Pattern names map to slots 1-8 and the "song" list to scenes. Then start_sequencer to listen and export_song_to_project to save.
Trigger a drum hit on the Circuit Tracks. Args: drum: Drum number (1-4). velocity: Hit velocity (0-127).
Play one or more MIDI notes on the Circuit Tracks. For a single note, pass a one-element list: [60]. For a chord, pass multiple notes: [60, 64, 67]. Channels: 0=Synth 1, 1=Synth 2, 9=Drums (notes 60,62,64,65 for drums 1-4). Args: channel: MIDI channel (0-indexed). 0=Synth1, 1=Synth2, 9=Drums. notes: List of MIDI note numbers (0-127). Middle C = 60. velocity: Note velocity (0-127). duration_ms: How long to hold the notes in milliseconds.
Read the live project back in the load_song song format (patterns by name where known, sounds, fx, mixer, scale, song order): what the user played or edited on the pads, ready to modify and load_song again.
Define a named pattern with tracks and steps for live sequencer preview. The sequencer mirrors the Circuit Tracks: 8 tracks, each with up to 32 steps. Only specify steps that have notes — absent step indices are rests. Drum tracks ignore note/gate fields (each drum has a fixed trigger note). Args: name: Pattern name (e.g., "intro", "verse", "drop"). tracks: Track data keyed by track name. Step fields are strictly validated. length: Pattern length in steps (default 16).
Define or replace one named pattern (a slot 1-8 on every track) with song-format track configs: {"synth1": {"steps": {"0": {"note": 60, "gate": 1}}}, "drum1": {"steps": {"0": {}, "4": {}}}}. Tracks not given are silent in this pattern. Length must match the other patterns (all 16 or all 32). When the sequencer is stopped the new pattern is selected on all tracks.
Set the song order: a list of pattern names becomes scenes 1..n and a scene chain that loops as a whole.
Replace (default) or merge the steps of one track inside a named pattern while everything else keeps playing. steps: {"0": {"note": 48, "velocity": 110}, "8": {"notes": [48, 55]}} for synths, {"0": {}, "4": {"velocity": 80}} for drums. Track-level automation lanes are kept.
Update a single track within a pattern. Use this to add/modify parts while the sequencer runs. Args: pattern_name: Name of the pattern to modify. track: Track name. steps: Step index (as string) -> step data. Step fields are strictly validated. clear_existing: If True (default), replace all steps. If False, merge with existing.
Generic descriptions lack context. 'Disconnect from the Circuit Tracks' (13 chars) and 'Check the current MIDI connection status' (40 chars) do not explain when to use them, what state changes occur, or consequences. Descriptions should be 50-200 chars with actionable context.
Parameter constraints missing or underspecified. 'port_name' in 'connect' has no validation hints (format, length, allowed characters). 'drum' in 'play_drum' specifies range 1-4 but no description of what each drum is. 'velocity' lacks guidance on typical values.