Skip to main content
Glama

GMA2 MCP

An MCP server that lets AI assistants control grandMA2 lighting consoles via Telnet. It exposes 101 high-level tools for cue management, fixture control, preset management, executor control, macro editing, appearance assignment, bulk operations, console state queries, show file management, read-back verification, music show workflows, and more through the Model Context Protocol.

Table of Contents

Related MCP server: Eos MCP

Overview

grandMA2 consoles accept commands over Telnet, but building correct command strings requires knowledge of the MA2 syntax rules -- keyword ordering, preset type mappings, option flags, and object hierarchies. This project handles that complexity in layered components:

  1. Command Builder (src/commands/) -- Python functions that construct valid grandMA2 command strings following the official syntax rules. Each function is a thin wrapper that returns a string; it never touches the network.

  2. MCP Server (src/server.py) -- A FastMCP server that exposes 101 tools covering cue management, fixture control, presets, executors, global state, labeling, appearance assignment, macro editing, bulk cue operations, sequence playback, console state queries, show file management, read-back verification, music show workflows, and raw commands over stdio transport. It manages a persistent Telnet connection to the console and delegates command construction to the builder layer.

  3. Response Parser (src/response_parser.py) -- Pure functions for parsing grandMA2 Telnet output: tabular List data into structured dicts, and inline Error #NN: REASON failures (with ANSI stripping) for the verified execution path.

  4. Execution Core (src/execution.py) -- ExecutionResult plus the pure build_result() that turns a raw Telnet reply into a structured {ok, echo, error_code, error_text, raw}. Mutating tools route through GMA2TelnetClient.execute() so they report the console's real outcome instead of fabricating success.

  5. GMA2Client (src/gma2_client.py) -- A high-level orchestration class that composes multiple command builder calls into workflow-level methods (e.g., build an entire cue list, set up a fixture group with a preset, create song objects for music shows).

  6. CommandSequence (src/command_sequence.py) -- A builder-pattern class for composing multiple commands into an ordered batch that can be previewed and executed as a unit.

The command builder covers all grandMA2 command-line keywords organized by category: object keywords (fixtures, channels, groups, presets, cues, sequences, executors), function keywords (store, delete, copy, move, goto, label, assign, and many more), and helping keywords (thru, at, +).

Features

  • 100 MCP tools -- Cue management (store/update/delete/goto, CMD assignment, timing, bulk operations), fixture control (set values/attributes, clear programmer, advanced selection: next/previous/invert/locate/align/fix), preset management (store/apply), executor control (on/off/go/kill/toggle/release/top, fader, rate/speed, flash/swop/stomp/temp busking), global state (blackout, highlight), blind/preview modes, object labeling, appearance assignment, copy/move and extended delete (group/preset/fixture/show), park/unpark, macro editing, effect control (apply, speed, form, range, phase, width, envelope, seconds, speed group, sync), MAtricks fan effects, show/user variables, timecode (SMPTE), MIDI output (note/control/program), clone fixtures, console state queries, show file management, read-back verification, music show workflows, and raw command execution.

  • Verified command execution -- grandMA2 reports failures inline in the Telnet reply (Error #NN: REASON). Mutating tools route through a verified execution core (src/execution.py) that parses the reply and returns the console's real outcome, so the assistant is not given false success on a rejected command. Multi-command tools abort on the first error.

  • Range/batch selectors -- Object tools accept grandMA2 selection expressions (1 thru 10, 1 + 3 + 5, 1 thru 10 - 4) validated by src/commands/selector.py, so a whole range copies/moves in one call instead of looping per ID.

  • Show-aware name resolution -- src/introspection.py queries List Attribute (cached) and resolves friendly, show-specific attribute names (e.g. White -> COLORRGB5) before sending, rejecting unknown names with suggestions.

  • Fixture-profile resolver -- src/profile_resolver.py parses MA2 fixture-type / GDTF XML and resolves named functions (open/closed/strobe/random/iris/frost/prism) to the correct per-fixture-type value, so the same logical preset (e.g. "Strobe Fast") gets the right DMX value on every profile.

  • Palette builders -- build_color_palette and the generalized build_preset_palette program whole preset palettes in one call, with per-fixture-type values, Global/Selective scope, and per-type merge (extend a palette to a new fixture group non-destructively).

  • Destructive command safety warnings -- Delete tools include informational warnings about downstream effects (orphaned executor handles, lost cue programming) to help AI assistants understand impact before confirming.

  • Complete command builder -- Over 350 Python functions covering all grandMA2 command-line keywords across 30+ modules, each returning a correctly formatted command string.

  • High-level client -- GMA2Client provides workflow-level methods: build cue lists, set up fixture groups with presets, quick look programming, batch executor assignments. Uses grandMA2's inline naming syntax to minimize Telnet round-trips.

  • Command chaining -- CommandSequence lets you compose multiple commands, preview them, and execute them as a batch.

  • Resilient Telnet client -- Built on telnetlib3 with automatic login, persistent connections, connection health checking, auto-reconnection with bounded exponential backoff, command serialization via asyncio.Lock, and graceful shutdown. If the console restarts or the network drops, the client detects the failure and reconnects transparently before the next command.

  • Connection error surfacing -- MCP tools catch connection failures and return human-readable error messages instead of unhandled exceptions, so the AI assistant knows when commands are not reaching the console.

  • Configurable transport -- Supports stdio (default, single client) and streamable-http (multi-client, web-based access) transports. HTTP host and port are configurable via environment variables.

  • Configurable via environment -- Host, port, user, password, and transport settings set through .env or environment variables.

Getting Started

Prerequisites

  • Python >= 3.12

  • A grandMA2 console (or onPC) with Telnet access enabled

  • uv (recommended) or pip

On macOS, if you need a Telnet client for manual testing:

brew install telnet

Installation

git clone <repository-url>
cd gma2-mcp

Using uv:

uv sync

Using pip:

python -m venv .venv
source .venv/bin/activate
pip install -e .

Configuration

Copy the template and fill in your console details:

cp .env.template .env

Variable

Description

Default

GMA_HOST

IP address of the grandMA2 onPC

127.0.0.1

GMA_PORT

Telnet port

30000

GMA_USER

Login username

administrator

GMA_PASSWORD

Login password

admin

MCP_TRANSPORT

MCP transport protocol (stdio, streamable-http)

stdio

MCP_HOST

HTTP bind address (streamable-http only)

127.0.0.1

MCP_PORT

HTTP port (streamable-http only)

8000

GMA_HOST should be set to the IP address of the machine running grandMA2 onPC. You can find this in the onPC network settings or by checking the machine's network configuration.

Port 30000 is the standard command port. Port 30001 is read-only (log output).

To enable HTTP transport for web-based or multi-client access:

MCP_TRANSPORT=streamable-http
MCP_HOST=0.0.0.0    # bind to all interfaces (default: 127.0.0.1)
MCP_PORT=3000        # custom port (default: 8000)

When using streamable-http, concurrent commands from multiple clients are automatically serialized via an internal lock to prevent interleaved Telnet commands, since the grandMA2 console processes commands sequentially.

MCP Registration

Add the server to your MCP client configuration.

Claude Desktop (~/Library/Application Support/Claude/claude_desktop_config.json on macOS):

{
  "mcpServers": {
    "gma2": {
      "command": "uv",
      "args": [
        "--directory",
        "/path/to/gma2-mcp",
        "run",
        "python",
        "-m",
        "src.server"
      ],
      "env": {
        "GMA_HOST": "2.0.0.1",
        "GMA_USER": "administrator",
        "GMA_PASSWORD": "admin"
      }
    }
  }
}

Without uv -- point directly to the virtualenv Python:

{
  "mcpServers": {
    "gma2": {
      "command": "/path/to/gma2-mcp/.venv/bin/python",
      "args": ["-m", "src.server"],
      "cwd": "/path/to/gma2-mcp",
      "env": {
        "GMA_HOST": "2.0.0.1",
        "GMA_USER": "administrator",
        "GMA_PASSWORD": "admin"
      }
    }
  }
}

Claude Code (CLI / IDE extensions)

Claude Code manages MCP servers through its own configuration, separate from Claude Desktop. You can set it up using either the CLI command or by editing the settings file directly.

Option 1: Using the claude mcp add command (recommended)

Run the following command in your terminal:

claude mcp add gma2 \
  -e GMA_HOST=2.0.0.1 \
  -e GMA_USER=administrator \
  -e GMA_PASSWORD=admin \
  -- uv --directory /path/to/gma2-mcp run python -m src.server

This registers the MCP server with Claude Code. The -e flags set environment variables that the server reads at startup. Replace /path/to/gma2-mcp with the actual path to your cloned repository, and adjust the GMA_HOST, GMA_USER, and GMA_PASSWORD values to match your console.

To register the server for a specific project only (instead of globally), add the -s project flag:

claude mcp add gma2 -s project \
  -e GMA_HOST=2.0.0.1 \
  -e GMA_USER=administrator \
  -e GMA_PASSWORD=admin \
  -- uv --directory /path/to/gma2-mcp run python -m src.server

Option 2: Editing the settings file manually

Add the server entry to your Claude Code settings file. The file location depends on the scope:

  • User-level (available in all projects): ~/.claude/settings.json

  • Project-level (available only in the current project): .claude/settings.json in the project root

{
  "mcpServers": {
    "gma2": {
      "command": "uv",
      "args": [
        "--directory",
        "/path/to/gma2-mcp",
        "run",
        "python",
        "-m",
        "src.server"
      ],
      "env": {
        "GMA_HOST": "2.0.0.1",
        "GMA_USER": "administrator",
        "GMA_PASSWORD": "admin"
      }
    }
  }
}

Verifying the setup

After registering the server, verify it is working:

# List all registered MCP servers
claude mcp list

# Check the server details
claude mcp get gma2

When you start a new Claude Code session, the server starts automatically. You can confirm the tools are available by asking Claude to list its MCP tools or by directly requesting a grandMA2 operation (e.g., "toggle blackout on the console").

Managing the server

# Remove the server
claude mcp remove gma2

# Re-add with different settings
claude mcp add gma2 \
  -e GMA_HOST=192.168.1.100 \
  -- uv --directory /path/to/gma2-mcp run python -m src.server

Usage

MCP Tools

The server exposes 101 tools:

Tool

Description

capabilities

Report version, registered tools, and profile schema version (contract surface; no console connection)

create_fixture_group

Select fixtures and store as a named group (2 commands)

store_cue

Store current programmer state as a cue

delete_cue

Delete a cue (includes downstream safety warnings)

goto_cue_tool

Jump to a specific cue (executor or sequence)

set_fixture_value

Set fixture(s) to a dimmer value (0-100)

set_fixture_attribute

Set a specific attribute (Pan, Tilt, etc.) on fixture(s)

clear_programmer

Clear the programmer (all, selection, active, default)

store_preset

Store current values as a preset

apply_preset

Apply an existing preset to the current selection

control_executor

On/off/go/kill/toggle an executor

set_executor_fader

Set executor fader level (0-100)

assign_to_executor

Assign a sequence to an executor (supports named page paths)

toggle_blackout

Toggle grand blackout

toggle_highlight

Toggle highlight mode

label_object

Assign a name label to any MA2 object

label_sequence_cue

Label a cue within a specific sequence

assign_appearance

Set frame/background colors on pool objects and cues (RGB/HSB/hex)

set_macro_line

Set the command for a specific macro line

run_macro

Execute a macro by ID (Go+ Macro)

create_macro

Create a macro with command lines (store + assign lines + label)

label_macro_tool

Label a macro in the macro pool

list_macros

List macros in the macro pool

delete_macro_tool

Delete a macro (with destructive operation warnings)

apply_effect

Apply effect from pool to current fixture selection

set_effect_speed

Set effect speed in BPM or Hz

set_effect_form

Set effect waveform (sine, ramp, square, etc.)

set_effect_range

Set effect high and/or low values

set_effect_phase

Set effect phase offset in degrees

set_effect_width

Set effect width (percentage of cycle)

stop_effects

Stop running effects for current selection (Off Effect)

sync_effects_tool

Synchronize all running effects

set_cue_cmd

Assign a command to a cue's CMD field

store_cue_across_sequences

Store a cue across a range of sequences in one call

label_cue_across_sequences

Label a cue across a range of sequences in one call

appearance_cue_across_sequences

Set cue appearance across a range of sequences in one call

execute_sequence

Go, pause, or goto a cue in a sequence

list_groups

List all defined groups (returns raw console response)

list_cues

List cues in a sequence (returns raw console response)

list_presets

List presets by type with validation (returns raw console response)

get_cue_annotation

Read user-added annotation text on a cue

get_group_annotation

Read user-added annotation text on a group

list_variables

List show or user variables with optional filter

query_object

Generic query for any MA2 object type (list or annotation mode)

save_show_tool

Save the current show file (optional name)

load_show_tool

Load a show file (destructive -- warns about unsaved changes)

new_show_tool

Create a new empty show (destructive -- warns about unsaved changes)

list_shows_tool

List available show files on the console

read_macro_lines

Read macro line content (parsed into structured data)

read_cue_info

Read cue properties: label, CMD, fade (parsed)

read_object_label

Read the label of any pool object (sequence, page, macro, etc.)

create_song_objects

Create and label a Sequence + Page pair for a song

setup_song_macro

Create a macro with SetVar command for song variable assignment

build_set_list

Build a set-list sequence with cue-to-macro links

send_raw_command

Send any grandMA2 command-line instruction

Query / Introspection Tools

The query tools use send_command_with_response() to capture the grandMA2 Telnet output and return raw text. The grandMA2 manual does not document the exact Telnet wire format for List and Info responses, so these tools return unprocessed console output that the AI can interpret directly.

  • list_groups, list_cues, list_presets -- Use the grandMA2 List keyword to retrieve show data. Accept optional ID ranges (e.g., list_groups(group_id=1, end_group_id=10)). list_presets validates the preset type against the 9 known types before sending.

  • get_cue_annotation, get_group_annotation -- Use the grandMA2 Info keyword, which reads user-added descriptive text annotations on objects (set via Info Group 3 "some note" on the console). These do NOT return object properties like fade times or fixture composition -- use list_cues or list_groups for that.

  • list_variables -- Routes to ListVar (show variables) or ListUserVar (user variables) based on the variable_type parameter. Supports an optional filter pattern.

  • query_object -- Generic query for any grandMA2 object type not covered by the dedicated tools. Supports mode="list" (default) and mode="annotation".

All query tools return a descriptive message instead of an empty string when the console returns no data.

Show File Management Tools

  • save_show_tool -- Saves the current show file. Accepts an optional show_name to save as a specific name. Uses /noconfirm to suppress the overwrite confirmation popup that would block Telnet.

  • load_show_tool -- Loads a show file by name. This is a destructive operation: unsaved changes to the current show are lost. Accepts save_first=True to save the current show before loading. Uses /noconfirm to suppress the GUI popup.

  • new_show_tool -- Creates a new empty show. Same destructive behavior and save_first option as load_show_tool.

  • list_shows_tool -- Lists available show files on the console's selected drive. Accepts an optional filter pattern.

Read-Back Tools

The read-back tools use send_command_with_response() to capture grandMA2 List command output and parse it into structured dicts via src/response_parser.py. Each tool returns a dict with parsed fields plus a raw_response field for debugging. If the response format is unrecognized, the tools return parsed: False with the raw output.

  • read_macro_lines -- Sends List Macro {pool}.{id} and parses each line's number and CMD content. Returns {"macro_id": N, "parsed": True, "lines": [{"line_number": 1, "cmd": "..."}], "raw_response": "..."}.

  • read_cue_info -- Sends List Cue {id} Sequence {seq} and extracts label, CMD, and fade time. Returns {"sequence_id": N, "cue_id": N, "label": "...", "cmd": "...", "fade": "...", "raw_response": "..."}.

  • read_object_label -- Sends List {type} {id} and extracts the object's name/label. Works with any pool object type (sequence, page, group, etc.). For macros, the object_id must be pool-qualified (e.g., "1.5" for Macro 5 in Pool 1).

Music Show Workflow Tools

High-level tools that automate common multi-step patterns for music show programming. These delegate to GMA2Client workflow methods.

  • create_song_objects -- Creates and labels a Sequence + Page pair with the same ID and name. This is the standard pattern for per-song programming (e.g., create_song_objects(song_id=101, song_name="Opening+Childhood") sends 2 commands).

  • setup_song_macro -- Creates a labeled macro with a SetVar command on line 1 for tracking the current song. Accepts an optional var_name (default: $song). Sends 3 commands: store macro, label, assign CMD.

  • build_set_list -- Creates a set-list sequence with cues linked to song macros. For each song, stores a cue with the song name as label and assigns Macro {id} as the cue CMD. Accepts a list of {cue_id, macro_id, name} dicts.

delete_show is intentionally not exposed as an MCP tool -- it is irreversible and too dangerous for AI-initiated operations. The Backup command is also excluded because it opens a GUI menu on the console (per grandMA2 Manual p368), which is not functional over Telnet. Use save_show_tool with a specific name as the practical backup mechanism.

Command Builder

The src/commands/ module can be used independently to build command strings:

from src.commands import fixture, at_full, store_group, label_group

fixture(1, end=10)              # "fixture 1 thru 10"
at_full()                       # "at full"
store_group(5)                  # "store group 5"
store_group(5, name="Front Wash")  # 'store group 5 "Front Wash"' (inline naming)
label_group(5, "Front Wash")    # 'label group 5 "Front Wash"' (separate label command)

GMA2Client (Workflow Orchestration)

The GMA2Client class provides high-level workflow methods that compose multiple commands. These methods use grandMA2's inline naming syntax to minimize the number of Telnet round-trips:

from src.gma2_client import GMA2Client

async with GMA2Client.create("192.168.1.100") as client:
    # Build a cue list with names and fade times
    # Uses inline naming: 1 command per named cue (store cue N "Name"),
    # plus 1 command per fade time
    await client.build_cue_list(1, [
        {"id": 1, "name": "Preset", "fade": 0},
        {"id": 2, "name": "Look 1", "fade": 3.0},
        {"id": 3, "name": "Blackout", "fade": 2.0},
    ])

    # Select fixtures, store as named group, apply a preset (3 commands)
    # Uses inline naming: store group N "Name" in a single command
    await client.setup_group_with_preset(
        fixtures=(1, 10), group_id=1,
        group_name="Front Wash", preset_type="color", preset_id=3,
    )

    # Quick look: set fixtures to a value, optionally store as cue
    await client.quick_look(fixtures=(1, 20), value=75, store_as_cue=5)

    # Batch assign sequences to executors
    await client.assign_sequences_to_executors([(1, 1), (2, 2), (3, 3)])

    # Clone fixture programming (with /noconfirm for telnet)
    await client.clone_fixtures(source_fixture=1, target_fixture=11,
                                source_end=5, target_end=15, mode="overwrite")

    # Apply effect to a group with parameters
    await client.setup_effect_on_group(group_id=1, effect_id=5,
                                       bpm=120, form="sin", high=100, low=0)

    # Set up a full executor page
    await client.setup_executor_page(page=1, assignments=[
        {"executor_id": 1, "sequence_id": 1, "label": "Wash", "fader_level": 80},
        {"executor_id": 2, "sequence_id": 2, "label": "Spots"},
    ])

    # Label multiple objects at once
    await client.batch_label("group", {1: "Wash", 2: "Spots", 3: "Beams"})

    # Create and optionally run a macro
    await client.create_and_run_macro(
        macro_id=10, commands=["Go Sequence 1", "Go Sequence 2"],
        name="Start Show", run=True,
    )

    # Music show workflows: create song objects (sequence + page pair)
    await client.create_song_objects(song_id=101, song_name="Opening+Childhood")

    # Set up a song macro with SetVar on line 1
    await client.setup_song_macro(macro_id=101, song_name="Opening+Childhood")

    # Build a full set list with cue-to-macro links
    await client.build_set_list(
        sequence_id=100, sequence_name="Set List",
        songs=[
            {"cue_id": 1, "macro_id": 101, "name": "Opening+Childhood"},
            {"cue_id": 2, "macro_id": 102, "name": "Nostalgia"},
            {"cue_id": 3, "macro_id": 103, "name": "Finale"},
        ],
    )

CommandSequence (Command Chaining)

The CommandSequence class lets you compose multiple commands and execute them as a batch:

from src.command_sequence import CommandSequence
from src.commands import fixture_at, store_group, at_full

seq = CommandSequence()
seq.add(fixture_at(1, 50)).add(at_full()).add(store_group(1))

# Preview before sending
print(seq.preview())  # ['fixture 1 at 50', 'at full', 'store group 1']

# Execute all commands
result = await seq.execute(client)
# {'commands_sent': [...], 'count': 3, 'success': True}

Direct Telnet Access

For manual testing via the Makefile:

make server    # Connect to grandMA2 command port (30000)
make log       # Connect to log output port (30001)

To exit a Telnet session: press Ctrl + ], then type quit.

Running the MCP Server Manually

uv run python -m src.server

Command Reference

The command builder follows the official grandMA2 command-line syntax. The basic pattern is [Function] [Object]. All objects have a default function, and most functions have a default object type. Objects are arranged in a hierarchical tree.

1. Helping Keywords (Prepositions/Conjunctions)

Used to create relations between functions and objects.

Keyword

Description

Example

Thru

Range selection

Fixture 1 Thru 10

+

Add to selection

Fixture 1 + 3 + 5

At

Set values

At 50

2. Object Keywords (Nouns)

Allocate objects in the show file. Usually combined with numbers, IDs, names, or labels.

Object

Function

Example

fixture()

Select fixtures by Fixture ID

fixture(34) -> fixture 34

channel()

Select fixtures by Channel ID

channel(11, sub_id=5) -> channel 11.5

group()

Select fixtures in a group

group(3) -> group 3

preset()

Apply a preset

preset("color", 5) -> preset 4.5

cue()

Reference a cue

cue(5) -> cue 5

sequence()

Reference a sequence

sequence(3) -> sequence 3

3. Function Keywords (Verbs)

Perform a task or function, often followed by the object they apply to.

Function

Description

Example

store()

Store objects in show file

store("macro", 5) -> store macro 5

store_cue()

Store cue with optional inline name

store_cue(1, name="Look") -> store cue 1 "Look"

store_preset()

Store preset with options

store_preset("dimmer", 3) -> store preset 1.3

store_group()

Store a group (with optional inline name)

store_group(1, name="Front") -> store group 1 "Front"

label_group()

Label a group

label_group(1, "Front") -> label group 1 "Front"

delete_group()

Delete a group

delete_group(1) -> delete group 1

select_fixture()

SelFix function

select_fixture(1, 10) -> selfix fixture 1 thru 10

clear()

Clear programmer

clear() -> clear

clear_selection()

Clear selection only

clear_selection() -> clearselection

clear_active()

Clear active values

clear_active() -> clearactive

clear_all()

Clear all

clear_all() -> clearall

go_sequence()

Start sequence playback

go_sequence(1) -> go+ sequence 1

pause_sequence()

Pause sequence

pause_sequence(1) -> pause sequence 1

goto_cue()

Jump to cue

goto_cue(1, 5) -> goto cue 5 sequence 1

4. At Keyword (Special)

At functions as both a Function Keyword and a Helping Keyword.

Function

Description

Example

at(75)

Set dimmer to value

at(75) -> at 75

at(cue=3)

Apply cue values

at(cue=3) -> at cue 3

at(fade=2)

Set fade time

at(fade=2) -> at fade 2

at_full()

Set to 100%

at_full() -> at full

at_zero()

Set to 0%

at_zero() -> at 0

attribute_at()

Set attribute value

attribute_at("Pan", 20) -> attribute "Pan" at 20

fixture_at()

Set fixture to value

fixture_at(2, 50) -> fixture 2 at 50

fixture_at()

Copy from fixture

fixture_at(2, source_fixture=3) -> fixture 2 at fixture 3

channel_at()

Set channel to value

channel_at(1, 75) -> channel 1 at 75

group_at()

Set group to value

group_at(3, 50) -> group 3 at 50

executor_at()

Set executor fader

executor_at(3, 50) -> executor 3 at 50

preset_type_at()

Set preset type values

preset_type_at(2, 50, end_type=9) -> presettype 2 thru 9 at 50

5. Copy and Move Keywords

Copy creates duplicates. Move relocates objects (swaps if the destination is occupied).

Function

Description

Example

copy("group", 1, 5)

Copy to target

copy group 1 at 5

copy("group", 1, end=3, target=11)

Copy range

copy group 1 thru 3 at 11

copy("group", 2, 6, target_end=8)

Copy to target range

copy group 2 at 6 thru 8

copy("cue", 5)

Copy to clipboard

copy cue 5

copy_cue(2, 6)

Copy cue

copy cue 2 at 6

move("group", 5, 9)

Move object

move group 5 at 9

move("group", 1, 10, end=3)

Move range

move group 1 thru 3 at 10

Copy options: overwrite, merge, status, cueonly, noconfirm

6. Assign Keyword

Defines relationships between objects, patching, and property assignment.

Function

Description

Example

assign("sequence", 1, "executor", 6)

Assign seq to executor

assign sequence 1 at executor 6

assign("dmx", "2.101", "channel", 5)

Patch DMX to channel

assign dmx 2.101 at channel 5

assign("group", 1, "layout", 1, x=5, y=2)

Assign to layout

assign group 1 at layout 1 /x=5 /y=2

assign_function("Toggle", "executor", 101)

Assign function to button

assign toggle at executor 101

assign_fade(3, 5)

Assign fade time to cue

assign fade 3 cue 5

assign_to_layout("group", 1, 1, x=5, y=2)

Assign to layout position

assign group 1 at layout 1 /x=5 /y=2

assign_macro_cmd(101, 1, "Go Seq 5")

Set macro line command

assign macro 1.101.1 /cmd="Go Seq 5"

assign_cue_cmd(1, 100, "Macro 101")

Set cue CMD field

assign cue 1 sequence 100 /cmd="Macro 101"

Assign options: break_, multipatch, reset, x, y, noconfirm, special, cue_mode, password

7. Label Keyword

Gives names to objects. Numbers in names auto-enumerate for ranges.

Function

Description

Example

label("group", 3, "All Studiocolors")

Label group

label group 3 "All Studiocolors"

label("fixture", 1, "Mac700 1", end=10)

Label range

label fixture 1 thru 10 "Mac700 1"

label("preset", '"color"."Red"', "Dark")

Label preset

label preset "color"."Red" "Dark"

label_sequence_cue("Set List", 1, "Opening")

Label cue in sequence

label sequence "Set List" cue 1 "Opening"

label_sequence_cue(100, 1, "Act 1", end_cue=5)

Label cue range

label sequence 100 cue 1 thru 5 "Act 1"

8. Appearance Keyword

Changes frame colors of pool objects and background colors of cues.

Function

Description

Example

appearance("preset", "0.1", red=100, green=0, blue=0)

Set RGB color

appearance preset 0.1 /r=100 /g=0 /b=0

appearance("preset", "0.1", hue=0, saturation=100)

Set HSB color

appearance preset 0.1 /h=0 /s=100

appearance("group", 1, end=5, color="FF0000")

Set hex color

appearance group 1 thru 5 /color=FF0000

appearance("macro", 2, source_type="macro", source_id=13)

Copy from source

appearance macro 2 at macro 13

appearance("preset", 1, reset=True)

Reset appearance

appearance preset 1 /reset

9. Macro Placeholder (@ Character)

The @ character (distinct from the At keyword) is used as a placeholder for user input in macros.

Function

Description

Example

macro_with_input_after()

@ at end of macro line

macro_with_input_after("Load") -> Load @

macro_with_input_before()

@ at start of macro line

macro_with_input_before("Fade 20") -> @ Fade 20

Project Structure

gma2-mcp/
├── src/
│   ├── commands/               # Command builder module
│   │   ├── __init__.py         # Public API exports
│   │   ├── constants.py        # PRESET_TYPES, store option sets
│   │   ├── helpers.py          # Internal helper functions
│   │   └── functions/          # Function keywords by category
│   │       ├── advanced_edit.py    # Flip, CircularCopy, Import, Export
│   │       ├── assignment.py       # Assign keyword functions
│   │       ├── blackout.py         # Blackout, Freeze, Highlight, Solo
│   │       ├── blind.py            # Blind, Preview modes
│   │       ├── call.py             # Call keyword
│   │       ├── conditionals.py     # EndIf, IfActive, IfOutput, Or, With
│   │       ├── crossfade.py        # Crossfade, ManualXFade
│   │       ├── cue_timing.py       # Delay, Fade timing
│   │       ├── edit.py             # Copy, Move, Delete, Remove
│   │       ├── effect.py           # Effect, EffectBPM, EffectForm
│   │       ├── executor_control.py # Off, On, Kill, Flash, Swop
│   │       ├── fixture_control.py  # Align, Fix, Locate, Next, Prev
│   │       ├── flash_swop_ext.py   # FlashGo, SwopGo, StoreLook
│   │       ├── helping.py          # Helping keywords (Thru, +)
│   │       ├── info.py             # List and Info queries
│   │       ├── intensity.py        # Full, Zero, Load, Learn
│   │       ├── labeling.py         # Label and Appearance
│   │       ├── list_ext.py         # ListShows, ListOops, ListVar
│   │       ├── macro.py            # Macro placeholder functions
│   │       ├── matricks.py         # MAtricks keywords
│   │       ├── midi.py             # MIDI control functions
│   │       ├── navigation.py       # Down, Up, NextRow, Search
│   │       ├── network.py          # Remote, Telnet, JoinSession
│   │       ├── park.py             # Park/Unpark functions
│   │       ├── playback.py         # Go, GoBack, Goto, DefGo
│   │       ├── programmer.py       # Block, Clone, Default, Update
│   │       ├── rate_speed.py       # Rate, Speed control
│   │       ├── rdm.py              # RDM functions
│   │       ├── selection.py        # SelFix and Clear functions
│   │       ├── show_data.py        # CrashLog, Lua, PSR, Thru
│   │       ├── step_timing.py      # SnapPercent, StepFade, FadePath
│   │       ├── store.py            # Store functions
│   │       ├── system.py           # Backup, Setup, Shutdown, Login
│   │       ├── values.py           # At and value setting
│   │       └── variables.py        # Variable functions
│   ├── command_sequence.py     # CommandSequence for multi-command batching
│   ├── gma2_client.py          # High-level workflow orchestration client (15 methods)
│   ├── response_parser.py      # Parse grandMA2 Telnet output into structured data
│   ├── execution.py            # Verified execution core (ExecutionResult, build_result)
│   ├── server.py               # MCP server (FastMCP, 101 tools, configurable stdio/streamable-http transport)
│   └── telnet_client.py        # Async Telnet client with health check, auto-reconnect, command lock, state tracking
├── tests/                      # Pytest test suite (one file per module)
├── doc/                        # grandMA2 user manual (PDF)
├── connect.sh                  # Telnet connection script with auto-login
├── Makefile                    # Utility commands (server, log, test)
├── pyproject.toml              # Project metadata and dependencies
└── .env.template               # Environment variable template

Development

Running Tests

make test

Or directly:

uv run pytest -v

Run a specific test file:

uv run pytest tests/test_playback.py -v

Dependencies

Package

Purpose

mcp>=1.21.0

Model Context Protocol library

python-dotenv

Environment variable loading

telnetlib3

Async Telnet client

pytest

Testing framework (dev)

pytest-asyncio

Async test support (dev)

Architecture

The project has six layers:

  1. Telnet Client (src/telnet_client.py) -- Low-level async Telnet communication with the console. Includes ConnectionState tracking (DISCONNECTED/CONNECTING/CONNECTED/RECONNECTING), health check probing, auto-reconnection with bounded exponential backoff (configurable retries and delay), health check TTL to skip redundant probes during rapid command sequences, asyncio.Lock-based command serialization for concurrent access safety, graceful shutdown via the server lifespan hook, and a verified execute() that reads and checks the console reply.

  2. Command Builder (src/commands/) -- Pure functions that construct command strings. No network access.

  3. Response Parser (src/response_parser.py) -- Pure functions that parse grandMA2 Telnet output: List tables into structured dicts (parsed: False on unrecognized formats), and inline Error #NN failures via detect_error()/strip_ansi().

  4. Execution Core (src/execution.py) -- Pure build_result() + ExecutionResult; turns a raw reply into {ok, echo, error_code, error_text, raw} so mutating tools report real outcomes.

  5. Orchestration (src/gma2_client.py, src/command_sequence.py) -- High-level workflow methods (15 methods including music show workflows) and command batching that compose builders + telnet.

  6. MCP Server (src/server.py) -- FastMCP server that exposes 101 tools over configurable transport (stdio or streamable-http), connecting the builder to the Telnet client.

All console communication goes through the Telnet client layer.

Troubleshooting

Connection fails -- Verify the console IP and port. Ensure Telnet is enabled on the console. Test manually with make server.

Connection drops mid-session -- The Telnet client auto-reconnects with up to 3 retries (exponential backoff: 1s, 2s, 4s). If all retries fail, tools return a clear error message. Check that the console is running and reachable.

Authentication errors -- Check username/password in .env. Confirm the user account exists on the console. Login is case-sensitive.

Fixture setup locked by stale connection -- If a previous session crashed without calling disconnect(), the console may still hold a Telnet lock. Restart the console or wait for its session timeout. The MCP server now calls disconnect() on shutdown to prevent this.

Command not working -- Verify the syntax against the grandMA2 manual. Ensure referenced objects (fixtures, groups, presets) exist in the current show file.

License

This project is licensed under the Apache License 2.0. See LICENSE for details.

Contributing

  1. Fork the repository

  2. Create a feature branch (git checkout -b feature/name)

  3. Commit your changes

  4. Push to the branch and open a Pull Request

Run tests before submitting: make test

Available Tools

101 tools
add_user_variableA
Add to / extend a user-profile-specific variable (AddUserVar).

Numeric values are summed; text values are concatenated.

Args:
    var_name: Variable name (should start with $)
    value: Value to add (numeric) or append (text)

Returns:
    str: Operation result message

Inputs: var_name (required), value (required). Returns: result

add_variableA
Add to / extend a global show variable (AddVar).

Numeric values are summed; text values are concatenated.

Args:
    var_name: Variable name (should start with $)
    value: Value to add (numeric) or append (text)

Returns:
    str: Operation result message

Inputs: var_name (required), value (required). Returns: result

align_selectionC

Align attribute values across the selected fixtures (fan first-to-last).

Inputs: target. Returns: result

appearance_cue_across_sequencesA
Set appearance (color) on a cue across a range of sequences.

Iterates over every sequence in the range and applies the color to the
specified cue. Supports RGB values (0-100) or hex color codes.

Args:
    cue_id: Cue number (e.g., 1, 0.5)
    sequence_start: First sequence number in the range
    sequence_end: Last sequence number in the range (inclusive)
    red: (Optional) Red component (0-100)
    green: (Optional) Green component (0-100)
    blue: (Optional) Blue component (0-100)
    color: (Optional) Hex color code (e.g., "FF0000")

Returns:
    dict: Result with commands_sent, count, and summary

Examples:
    - Set cue 0.5 to black across sequences 101-125 (red=0, green=0, blue=0)
    - Set cue 1 to red across sequences 101-103 (color="FF0000")

Inputs: cue_id (required), sequence_start (required), sequence_end (required), red, +3 more.

apply_effectB
Apply a predefined effect from the effect pool to the current fixture selection.

Fixtures must be selected first using set_fixture_value or create_fixture_group.

Args:
    effect_id: Effect number from the effect pool

Returns:
    str: Operation result message

Examples:
    - Apply effect 5 to selected fixtures

Inputs: effect_id (required). Returns: result

apply_presetA
Apply an existing preset to the current selection.

Args:
    preset_type: Preset type (dimmer, position, gobo, color, beam, focus, control, shapers, video)
    preset_id: Preset number

Returns:
    str: Operation result message

Examples:
    - Apply color preset 3
    - Apply position preset 1

Inputs: preset_type (required), preset_id (required). Returns: result

assign_appearanceA
Assign an appearance (frame/background color) to a grandMA2 pool object or cue.

Colors can be specified via RGB (0-100), HSB (hue 0-360, sat/bright 0-100),
hex color string, or by copying from a source object. Use reset to clear.

Args:
    object_type: Object type (e.g., "group", "cue", "preset", "macro")
    object_id: Object number or compound ID (e.g., 1 or "0.1")
    end: (Optional) End ID for applying to a range of objects
    source_type: (Optional) Source object type to copy appearance from
    source_id: (Optional) Source object ID to copy appearance from
    reset: Reset appearance to default
    color: Hex color code (e.g., "FF0000") or gel name
    red: Red component (0-100)
    green: Green component (0-100)
    blue: Blue component (0-100)
    hue: Hue (0-360)
    saturation: Saturation (0-100)
    brightness: Brightness (0-100)

Returns:
    str: Operation result message

Examples:
    - Set group 1 to red: object_type="group", object_id=1, red=100, green=0, blue=0
    - Set preset 0.1 to hex color: object_type="preset", object_id="0.1", color="FF0000"
    - Copy appearance from macro 13 to macro 2: object_type="macro", object_id=2, source_type="macro", source_id=13
    - Reset group 1 appearance: object_type="group", object_id=1, reset=True

Inputs: object_type (required), object_id (required), end, source_type, +9 more. Returns: result

assign_timecode_slotA
Assign a timecode show to a timecode slot for cue triggering.

Args:
    tc_id: Timecode show ID
    slot: Timecode slot number

Returns:
    str: Operation result message

Inputs: tc_id (required), slot (required). Returns: result

assign_to_executorC
Assign a sequence to an executor.

Args:
    sequence_id: Sequence number to assign
    executor_id: Target executor number

Returns:
    str: Operation result message

Examples:
    - Assign sequence 1 to executor 6

Inputs: sequence_id (required), executor_id (required). Returns: result

build_color_paletteA
Build (or extend) a color preset palette for a fixture selection in one call.

For each color: selects ``target``, sets R/G/B/W, stores the preset (Global by
default), labels it, and sets its pool appearance swatch.

To extend an existing palette to a new fixture group without disturbing
already-stored values, pass that group as ``target`` and ``merge=True``.

Args:
    target: A grandMA2 selection command (e.g. "Group 3", "Group 1 Thru 5").
    colors: List of {id, name?, r, g, b, w?} with r/g/b/w in 0-100.
    scope: "global" (default), "selective", or "universal".
    merge: Merge into existing presets (non-destructive extend).
    label: Apply each color's name as the preset label.
    appearance: Set each preset's pool swatch from its color.

Returns:
    str: Operation result message

Examples:
    - Build a palette on LED Par: target="Group 3", colors=[{"id":7,"name":"Red","r":100,"g":0,"b":0}]
    - Extend it to Wash: target="Group 4", merge=True, same colors

Inputs: target (required), colors (required), scope, merge, +2 more. Returns: result

build_preset_paletteA
Build a preset palette of any type from per-fixture-type value sets.

Generalizes color/beam/focus palette building: each preset carries one or
more targets (fixture selections), each with its own attribute values. The
first target uses ``scope``; subsequent targets of the same preset merge
(per-fixture-type accumulation into one preset).

Args:
    preset_type: "color", "beam", "focus", "dimmer", "position", ...
    presets: list of {id, name?, by_target: [{target, attrs}]} where target
        is a selection command (e.g. "Fixture 101 Thru 128") and attrs is a
        list of [attribute, value] pairs.
    scope: "global" (default), "selective", or "universal".
    merge: if True the first target also merges (extend an existing preset).
    label: apply each preset's name as its label.

Returns:
    str: Operation result message

Examples:
    - Beam strobe across types: preset_type="beam", presets=[{"id":3,"name":"Strobe","by_target":[{"target":"Fixture 401 Thru 428","attrs":[["SHUTTER",50]]},{"target":"Fixture 201 Thru 215","attrs":[["MASTERSHUTTERSTROBE",21]]}]}]

Inputs: preset_type (required), presets (required), scope, merge, +1 more. Returns: result

build_set_listA
Create a set-list sequence with cue-to-macro links.

Stores a sequence, then for each song creates a cue and assigns a
macro trigger to its CMD field. Each song dict must contain:
    cue_id (int): Cue number in the set-list sequence
    macro_id (int): Macro to trigger via cue CMD
    name (str): Name for the cue

Args:
    sequence_id: Sequence number for the set list
    sequence_name: Label for the set-list sequence
    songs: List of song definition dicts

Returns:
    str: Operation result message

Examples:
    - Build a set list with 3 songs linking cues to macros

Inputs: sequence_id (required), sequence_name (required), songs (required). Returns: result

capabilitiesA

Report this server's version, registered tools, and profile schema version.

Machine-readable contract surface for the gma2-workflow startup self-check
(issue #85). Pure metadata: it does NOT open a console connection, so it is
intentionally not wrapped with ``@handle_connection_error``.

Returns:
    dict: ``{version, tools (sorted), tool_count, profile_schema_version}``.

Inputs: no named parameters.

clear_programmerB
Clear the programmer.

Args:
    mode: Clear mode - "all" (clear everything), "selection" (clear selection only),
          "active" (clear active values only), or "default" (standard clear)

Returns:
    str: Operation result message

Examples:
    - Clear all programmer data
    - Clear selection only

Inputs: mode. Returns: result

clone_fixturesA
Clone all programming from source fixture(s) to target fixture(s).

WARNING: mode="overwrite" replaces existing target programming.

Args:
    source_fixture: Source fixture number (or start of range)
    target_fixture: Target fixture number (or start of range)
    source_end: Optional end of source range
    target_end: Optional end of target range
    mode: "default", "overwrite", or "merge"

Returns:
    str: Operation result message

Inputs: source_fixture (required), target_fixture (required), source_end, target_end, +1 more. Returns: result

control_executorA
Control an executor (on, off, go, kill, toggle).

Args:
    executor_id: Executor number
    action: Action to perform: "on", "off", "go", "kill", or "toggle"

Returns:
    str: Operation result message

Examples:
    - Turn on executor 1
    - Kill executor 3
    - Go on executor 2

Inputs: executor_id (required), action (required). Returns: result

control_timecodeA
Control timecode playback/recording.

Args:
    tc_id: Timecode show ID
    action: One of "go" (play), "pause", "off" (stop), "top" (rewind),
            or "record" (arm real-time event recording)

Returns:
    str: Operation result message

Examples:
    - Start timecode 1: action="go"
    - Record events into timecode 1: action="record"

Inputs: tc_id (required), action (required). Returns: result

copy_objectA
Copy an object (or range) to a new location.

``source`` and ``target`` accept grandMA2 selection expressions: a single ID
or a range/list using thru / + / - (e.g. "1 thru 10", "1 + 3 + 5"), so a
whole range copies in one call.

Args:
    object_type: Object type ("cue", "preset", "group", "sequence", "macro", ...)
    source: Source selection expression
    target: Target selection expression
    mode: "default", "overwrite", or "merge"

Returns:
    str: Operation result message

Examples:
    - Copy cue 1 to cue 10: object_type="cue", source="1", target="10"
    - Copy groups 1 thru 5 to 11: object_type="group", source="1 thru 5", target="11"

Inputs: object_type (required), source (required), target (required), mode. Returns: result

create_fixture_groupA
Create a group containing a specified range of fixtures.

This tool selects the specified range of fixtures and saves them as a group.
Optionally, a name can be assigned to the group.

Args:
    start_fixture: Starting fixture number
    end_fixture: Ending fixture number
    group_id: Group number to save
    group_name: (Optional) Group name, e.g., "Front Wash"

Returns:
    str: Operation result message

Examples:
    - Save fixtures 1 to 10 as group 1
    - Save fixtures 1 to 10 as group 1 with name "Front Wash"

Inputs: start_fixture (required), end_fixture (required), group_id (required), group_name. Returns: result

create_macroA
Create a macro with command lines.

Stores an empty macro, then assigns each command to sequential lines.
Optionally labels the macro.

Args:
    macro_id: Macro number to create
    commands: List of command strings for each macro line
    name: Optional label for the macro
    pool: Macro pool number (default: 1)

Returns:
    str: Operation result message

Examples:
    - Create macro 10 with commands ["Go Sequence 1", "Go Sequence 2"]
    - Create macro 10 named "Start Show" with one command

Inputs: macro_id (required), commands (required), name, pool. Returns: result

create_song_objectsA
Create and label a Sequence + Page pair for a song.

Stores a sequence and a page with the same ID and name, which is the
standard pattern for per-song programming in music show workflows.

Args:
    song_id: ID for both the sequence and page
    song_name: Name to assign to both objects

Returns:
    str: Operation result message

Examples:
    - Create sequence 101 and page 101 named "Opening+Childhood"

Inputs: song_id (required), song_name (required). Returns: result

create_timecode_poolA
Create a timecode show, optionally naming it and assigning a slot.

Standard SMPTE setup: store the timecode pool object, label it, and bind it
to a timecode slot for cue triggering. Timecode events themselves are
recorded in real time via control_timecode(action="record").

Args:
    tc_id: Timecode show ID
    name: Optional display name
    slot: Optional timecode slot to assign

Returns:
    str: Operation result message

Examples:
    - Create timecode 1 named "Act 1" on slot 1

Inputs: tc_id (required), name, slot. Returns: result

delete_cueB
Delete a cue from the current sequence.

Args:
    cue_id: Cue number to delete

Returns:
    str: Operation result message

Examples:
    - Delete cue 3

Inputs: cue_id (required). Returns: result

delete_fixtureC

Delete a fixture from the patch (destructive).

Inputs: fixture_id (required). Returns: result

delete_groupC

Delete a fixture group (destructive).

Inputs: group_id (required). Returns: result

delete_macro_toolA
Delete a macro from the macro pool.

WARNING: This is a destructive operation that permanently removes the macro.

Args:
    macro_id: Macro number to delete
    pool: Macro pool number (default: 1)

Returns:
    str: Operation result message with warnings

Inputs: macro_id (required), pool. Returns: result

delete_presetB

Delete a preset (destructive). preset_type e.g. "color", "dimmer".

Inputs: preset_type (required), preset_id (required). Returns: result

delete_showB

Delete a show file from the console drive (destructive).

Inputs: show_name (required). Returns: result

execute_sequenceB
Execute sequence-related operations.

Args:
    sequence_id: Sequence number
    action: Operation type: "go" (execute), "pause" (pause), or "goto" (jump to cue)
    cue_id: (Required for goto) Target cue number

Returns:
    str: Operation result message

Examples:
    - Execute sequence 1
    - Pause sequence 2
    - Jump to cue 5 of sequence 1

Inputs: sequence_id (required), action (required), cue_id. Returns: result

fix_selectionC

Fix (lock) the current fixture selection.

Inputs: target. Returns: result

flash_executorA
Flash an executor (momentary intensity bump) — a live busking control.

Args:
    executor_id: Executor number
    page: Optional page number for page-qualified addressing
    mode: "flash" (momentary), "flash_on" (latching), or "flash_go" (flash and go)

Returns:
    str: Operation result message

Inputs: executor_id (required), page, mode. Returns: result

get_cue_annotationA
read the user-added annotation text on a cue.

in grandMA2, the Info keyword reads or writes user-added descriptive text
annotations on objects. this tool reads the annotation. if no annotation has
been set on the cue, the response will be empty.

note: this does NOT return cue properties (fade time, values, etc.).
to see cue properties, use list_cues with a specific cue_id instead.

Args:
    cue_id: cue ID to read annotation from
    sequence_id: sequence containing the cue (optional)

Returns:
    str: the user annotation text, or empty response message if none set

Inputs: cue_id (required), sequence_id. Returns: result

get_group_annotationA
read the user-added annotation text on a group.

in grandMA2, the Info keyword reads or writes user-added descriptive text
annotations on objects. this tool reads the annotation. if no annotation has
been set on the group, the response will be empty.

note: this does NOT return group composition or fixture details.
to see group details, use list_groups with a specific group_id instead.

Args:
    group_id: group ID to read annotation from

Returns:
    str: the user annotation text, or empty response message if none set

Inputs: group_id (required). Returns: result

goto_cue_toolA
Jump to a specific cue.

Targets either an executor or a sequence. If neither is specified,
the command applies to the selected executor.

Args:
    cue_id: Target cue number
    executor: (Optional) Executor number
    sequence: (Optional) Sequence number

Returns:
    str: Operation result message

Examples:
    - Goto cue 5 on executor 4
    - Goto cue 3 in sequence 1

Inputs: cue_id (required), executor, sequence. Returns: result

invert_selectionB

Invert the current fixture selection.

Inputs: no named parameters. Returns: result

label_cue_across_sequencesA
Label a cue across a range of sequences.

Iterates over every sequence in the range and labels the specified cue.

Args:
    cue_id: Cue number to label (e.g., 1, 0.5)
    sequence_start: First sequence number in the range
    sequence_end: Last sequence number in the range (inclusive)
    label: Label text to assign

Returns:
    dict: Result with commands_sent, count, and summary

Examples:
    - Label cue 0.5 across sequences 101-125 as "((LOADING SONG))"

Inputs: cue_id (required), sequence_start (required), sequence_end (required), label (required).

label_macro_toolB
Label a macro in the macro pool.

Args:
    macro_id: Macro number to label
    name: Label text for the macro

Returns:
    str: Operation result message

Examples:
    - Label macro 5 as "Blackout All"

Inputs: macro_id (required), name (required). Returns: result

label_objectA
Assign a name label to a grandMA2 object.

Args:
    object_type: Object type (e.g., "group", "cue", "sequence", "macro", "preset")
    object_id: Object number
    name: Name to assign

Returns:
    str: Operation result message

Examples:
    - Label group 1 as "Front Wash"
    - Label cue 5 as "Intro"

Inputs: object_type (required), object_id (required), name (required). Returns: result

label_sequence_cueA
Label a cue within a specific sequence.

This tool addresses cues through the sequence context, which is required
for labeling cues inside named sequences.

Args:
    sequence: Sequence ID or name (e.g., "100" or "Set List")
    cue_id: Cue number within the sequence
    name: Label to assign
    end_cue: (Optional) End cue for range labeling

Returns:
    str: Operation result message

Examples:
    - Label cue 1 in "Set List" as "Opening+Childhood"
    - Label cue 1 in sequence 100 as "Opening+Childhood"
    - Label cues 1 thru 5 in "Set List" as "Act 1"

Inputs: sequence (required), cue_id (required), name (required), end_cue. Returns: result

list_cuesB
list cues on the grandMA2 console.

lists cues of the selected executor, or a specific sequence if sequence_id is provided.

Args:
    cue_id: specific cue ID or start of range (optional)
    end_cue_id: end cue ID for range query (optional)
    sequence_id: sequence to list cues from (optional)

Returns:
    str: raw console response with cue listing

Inputs: cue_id, end_cue_id, sequence_id. Returns: result

list_groupsA
list all defined groups on the grandMA2 console.

returns raw console output from the List Group command.

Args:
    group_id: specific group ID or start of range (optional)
    end_group_id: end group ID for range query (optional, requires group_id)

Returns:
    str: raw console response with group listing

Inputs: group_id, end_group_id. Returns: result

list_macrosA
List macros in the macro pool.

Returns raw console output from the List Macro command.

Returns:
    str: Raw console response with macro listing

Inputs: no named parameters. Returns: result

list_presetsA
list presets of a given type on the grandMA2 console.

valid preset types: dimmer, position, gobo, color, beam, focus, control, shapers, video.

Args:
    preset_type: type of preset (e.g. "color", "position", "dimmer")
    preset_id: specific preset ID (optional)

Returns:
    str: raw console response with preset listing

Inputs: preset_type (required), preset_id. Returns: result

list_shows_toolA
list available show files on the grandMA2 console.

Args:
    filter: optional filter pattern (e.g. "Mac*" to list shows starting with Mac)

Returns:
    str: raw console response with show file listing

Inputs: filter. Returns: result

list_variablesA
list show variables or user variables on the grandMA2 console.

Args:
    variable_type: "show" for show variables (ListVar), "user" for user variables (ListUserVar)
    filter: optional filter pattern (e.g. "f*" to list variables starting with f)

Returns:
    str: raw console response with variable listing

Inputs: variable_type (required), filter. Returns: result

load_show_toolA
load a show file on the grandMA2 console.

WARNING: this is a DESTRUCTIVE operation. any unsaved changes to the current
show will be lost. set save_first=True to save the current show before loading.

Args:
    show_name: name of the show file to load
    save_first: if True, saves the current show before loading the new one

Returns:
    str: confirmation message with warning about unsaved changes

Inputs: show_name (required), save_first. Returns: result

locate_fixturesB

Send the selected fixtures to home/default position (Locate).

Inputs: no named parameters. Returns: result

move_objectA
Move an object (or range) to a new location. WARNING: removes from source.

``source`` and ``target`` accept selection expressions (thru / + / -), so a
whole range moves in one call (e.g. move groups 1 thru 10 to 21).

Args:
    object_type: Object type ("cue", "preset", "group", "sequence", "macro", ...)
    source: Source selection expression
    target: Target selection expression

Returns:
    str: Operation result message

Inputs: object_type (required), source (required), target (required). Returns: result

new_show_toolA
create a new empty show on the grandMA2 console.

WARNING: this is a DESTRUCTIVE operation. any unsaved changes to the current
show will be lost. set save_first=True to save the current show before creating a new one.

note: per the grandMA2 manual, NewShow requires a show name. omitting the
name may work on some console versions but this behavior is undocumented.
providing a name is recommended.

Args:
    show_name: name for the new show (recommended — required per official manual)
    save_first: if True, saves the current show before creating a new one

Returns:
    str: confirmation message with warning about unsaved changes

Inputs: show_name, save_first. Returns: result

next_fixtureA

Step to the next fixture in the current selection.

Inputs: no named parameters. Returns: result

park_fixtureA
Park DMX output (lock a value regardless of playback).

Args:
    target: What to park (e.g. "fixture 1", "channel 5", "fixture 1 attribute dim").
            Omit to park the current selection.
    at_value: Optional value to park at (0-100); omit to park at current value.

Returns:
    str: Operation result message

Inputs: target, at_value. Returns: result

previous_fixtureB

Step to the previous fixture in the current selection.

Inputs: no named parameters. Returns: result

query_objectA
generic query for any grandMA2 object type.

use this for object types without a dedicated tool (e.g. executors, sequences, effects).
for groups, cues, and presets, prefer the specific tools.

Args:
    object_type: MA2 object type (e.g. "executor", "sequence", "effect", "macro")
    object_id: object ID to query (optional)
    mode: "list" to list objects, "annotation" to read user-added text annotation (default: "list").
          note: "annotation" mode reads user-added descriptive text, NOT object properties.

Returns:
    str: raw console response

Inputs: object_type (required), object_id, mode. Returns: result

query_timecodeA
Read timecode pool status / configuration.

Args:
    tc_id: Specific timecode ID (optional; lists the whole pool if omitted)

Returns:
    str: raw console response with timecode listing

Inputs: tc_id. Returns: result

read_cue_infoA

Read cue information for a specific cue in a sequence.

Retrieves and parses cue data including label, fade time, and CMD field.

Args:
    sequence_id: Sequence ID containing the cue
    cue_id: Cue ID (int or str for decimal cue numbers like "2.5")

Returns:
    dict with parsed cue info and raw response

Inputs: sequence_id (required), cue_id (required).

read_macro_linesA

Read macro line content for a given macro.

Retrieves and parses all lines of a macro, returning each line's
number and command string.

Args:
    macro_id: Macro ID to read
    pool: Macro pool number (default 1)

Returns:
    dict with parsed macro lines and raw response

Inputs: macro_id (required), pool.

read_object_labelA

Read the label/name of any grandMA2 show object.

Uses the generic list command to retrieve an object's name field.

Note: For macros, object_id must be pool-qualified (e.g., "1.5" for
Macro 5 in Pool 1) since grandMA2 addresses macros as pool.id.
For most other object types, a plain integer ID is sufficient.

Args:
    object_type: Object type (e.g., "group", "sequence", "macro", "page")
    object_id: Object ID (int for most types, or "pool.id" string for macros)

Returns:
    dict with parsed label and raw response

Inputs: object_type (required), object_id (required).

release_executorC
Release an executor (clean removal from output, respecting tracking).

Args:
    executor_id: Executor number
    page: Optional page for page-qualified addressing

Returns:
    str: Operation result message

Inputs: executor_id (required), page. Returns: result

reset_matricksA

Reset all MAtricks settings to defaults.

Inputs: no named parameters. Returns: result

run_macroA
Execute a macro by ID.

Starts the specified macro using the Go+ command.

Args:
    macro_id: Macro number to execute
    pool: Macro pool number (default: 1)

Returns:
    str: Operation result message

Examples:
    - Run macro 5 from default pool
    - Run macro 10 from pool 2

Inputs: macro_id (required), pool. Returns: result

save_show_toolA
save the current show file on the grandMA2 console.

if no name is provided, saves under the current show name.
uses /noconfirm to suppress the overwrite confirmation popup
that would otherwise block Telnet when a show with the same name exists.

Args:
    show_name: name to save the show as (optional)

Returns:
    str: confirmation message

Inputs: show_name. Returns: result

send_midi_controlA
Send a MIDI control-change (CC) message via the MIDI Out port.

Args:
    controller: Controller number (0-127)
    value: Control value (0-127)
    channel: MIDI channel; uses the Setup MSC channel if omitted

Returns:
    str: Operation result message

Inputs: controller (required), value (required), channel. Returns: result

send_midi_noteA
Send a MIDI note message via the console's MIDI Out port.

Used to trigger external devices (video servers, audio, pyro, fog).

Args:
    note: MIDI note number (0-127)
    velocity: Note velocity (0-127); defaults to full (127) if omitted
    channel: MIDI channel; uses the Setup MSC channel if omitted
    off: If True, send note-off instead of note-on

Returns:
    str: Operation result message

Inputs: note (required), velocity, channel, off. Returns: result

send_midi_programA
Send a MIDI program-change message via the MIDI Out port.

Args:
    program: Program number (0-127)
    channel: MIDI channel; uses the Setup MSC channel if omitted

Returns:
    str: Operation result message

Inputs: program (required), channel. Returns: result

send_raw_commandA
Send a raw MA command to grandMA2.

This is a low-level tool that allows sending any grandMA2 command-line instruction.
It is recommended to use other high-level tools first; use this tool only when
special commands are needed.

Args:
    command: Raw MA command to send

Returns:
    str: Operation result message

Examples:
    - blackout
    - go+ executor 1.1
    - store sequence 1 cue 1

Inputs: command (required). Returns: result

set_cue_cmdA
Assign a command to a cue's CMD field.

When the cue fires, the assigned command will execute automatically.
Common use: trigger a macro when a cue runs.

Args:
    cue_id: Cue number
    sequence_id: Sequence number
    command: Command to execute when cue fires (e.g., "Macro 101")

Returns:
    str: Operation result message

Examples:
    - Set cue 1 in sequence 100 to trigger "Macro 101"
    - Set cue 5 in sequence 200 to run "Go Sequence 10"

Inputs: cue_id (required), sequence_id (required), command (required). Returns: result

set_cue_timingA
Set fade/delay timing on a cue or the current selection.

Provide any subset (at least one required). ``target`` applies the timing to
a specific object (e.g. "cue 5" or "cue 5 sequence 2"); omit to apply to the
current programmer selection.

Args:
    fade: Fade time (seconds)
    delay: Delay time (seconds)
    out_fade: Out-fade time (seconds)
    out_delay: Out-delay time (seconds)
    target: Optional target object string

Returns:
    str: Operation result message

Inputs: fade, delay, out_fade, out_delay, +1 more. Returns: result

set_effect_envelopeB
Set effect envelope parameters (attack, decay, delay, fade) on the selection.

Provide any subset (at least one required).

Returns:
    str: Operation result message

Inputs: attack, decay, delay, fade. Returns: result

set_effect_formA
Set the effect waveform for the current selection.

Args:
    form: Waveform type — name (e.g., "sin", "ramp", "square") or number

Returns:
    str: Operation result message

Examples:
    - Set effect form to "sin"
    - Set effect form to 6

Inputs: form (required). Returns: result

set_effect_phaseA
Set the effect phase offset for the current selection.

Args:
    phase: Phase value in degrees (e.g., 0, 90, 180, 270)

Returns:
    str: Operation result message

Inputs: phase (required). Returns: result

set_effect_rangeA
Set effect high and/or low values for the current selection.

At least one of high or low must be provided.

Args:
    high: Effect high value (optional)
    low: Effect low value (optional)

Returns:
    str: Operation result message

Examples:
    - Set effect range high=100, low=0
    - Set only effect high to 80

Inputs: high, low. Returns: result

set_effect_secondsB

Set effect speed in seconds (alternative to BPM/Hz).

Inputs: value (required). Returns: result

set_effect_speedB
Set the effect speed for the current selection.

Args:
    value: Speed value
    unit: Speed unit — "bpm" (beats per minute) or "hz" (hertz)

Returns:
    str: Operation result message

Examples:
    - Set effect speed to 120 BPM
    - Set effect speed to 2.5 Hz

Inputs: value (required), unit. Returns: result

set_effect_speed_groupC

Assign the effect to a speed group for synchronized control.

Inputs: group_id (required). Returns: result

set_effect_widthB
Set the effect width for the current selection.

Args:
    width: Width value (percentage of cycle)

Returns:
    str: Operation result message

Inputs: width (required). Returns: result

set_executor_faderB
Set an executor's fader level.

Args:
    executor_id: Executor number
    value: Fader value (0-100)

Returns:
    str: Operation result message

Examples:
    - Set executor 1 fader to 75%

Inputs: executor_id (required), value (required). Returns: result

set_executor_rateB
Control an executor's playback rate (live tempo matching).

Args:
    executor_id: Executor number
    mode: "set" (rate), "half", "double", or "reset" (1:1)
    page: Optional page for page-qualified addressing

Returns:
    str: Operation result message

Inputs: executor_id (required), mode, page. Returns: result

set_executor_speedB
Control an executor's playback speed (live tempo matching).

Args:
    executor_id: Executor number
    mode: "set" (speed), "half", or "double"
    page: Optional page for page-qualified addressing

Returns:
    str: Operation result message

Inputs: executor_id (required), mode, page. Returns: result

set_fixture_attributeB
Set a specific attribute on fixture(s).

First selects the fixture(s), then applies the attribute value.

Args:
    fixture_id: Fixture number (or start of range)
    attribute: Attribute name (e.g., "Pan", "Tilt", "Dimmer")
    value: Value to set
    end_fixture: (Optional) End fixture for range

Returns:
    str: Operation result message

Examples:
    - Set Pan to 128 on fixture 1
    - Set Tilt to 50 on fixtures 1 thru 10

Inputs: fixture_id (required), attribute (required), value (required), end_fixture. Returns: result

set_fixture_valueA
Set fixture(s) to a dimmer value.

Args:
    fixture_id: Fixture number (or start of range)
    value: Dimmer percentage (0-100)
    end_fixture: (Optional) End fixture for range

Returns:
    str: Operation result message

Examples:
    - Set fixture 1 to 75%
    - Set fixtures 1 thru 10 to 50%

Inputs: fixture_id (required), value (required), end_fixture. Returns: result

set_macro_lineA
Set the command for a specific line within a macro.

Args:
    macro_id: Macro number
    line: Line number within the macro
    command: The command string for that line (e.g., "SetVar $song='Opening+Childhood'")
    pool: Macro pool number (default: 1)

Returns:
    str: Operation result message

Examples:
    - Set macro 101 line 1 to "SetVar $song='Opening+Childhood'"
    - Set macro 50 line 3 in pool 2 to "Go Sequence 5"

Inputs: macro_id (required), line (required), command (required), pool. Returns: result

set_matricksA
Configure MAtricks for fan effects / selection-pattern manipulation.

Applies to the current fixture selection. Provide any subset of parameters;
they are sent together. At least one parameter is required.

Args:
    blocks: Block size grouping
    wings: Number of wings (symmetry)
    groups: Number of groups
    interleave: Interleave step
    filter: MAtricks filter value

Returns:
    str: Operation result message

Inputs: blocks, wings, groups, interleave, +1 more. Returns: result

set_step_timingB
Set step/chase timing parameters for the current selection.

Provide any subset (at least one required).

Args:
    snap_percent: Snap percentage (hard-cut portion)
    step_fade: Step fade time
    step_in_fade: Step in-fade time
    step_out_fade: Step out-fade time
    fade_path: Fade path / curve value

Returns:
    str: Operation result message

Inputs: snap_percent, step_fade, step_in_fade, step_out_fade, +1 more. Returns: result

setup_song_macroA
Create a macro that sets a user variable to the song name.

Stores a macro, labels it with the song name, and assigns a SetVar
command on line 1. This is used to track the current song in
music show workflows.

Args:
    macro_id: Macro number to create
    song_name: Song name used for label and variable value
    var_name: Variable name for the SetVar command (default: "$song")

Returns:
    str: Operation result message

Examples:
    - Create macro 101 that sets $song to "Opening+Childhood"

Inputs: macro_id (required), song_name (required), var_name. Returns: result

set_user_variableB
Set a user-profile-specific variable (SetUserVar).

Args:
    var_name: Variable name (should start with $)
    value: Numeric, text, or None to delete the variable
    input_dialog: If True (string values only), prompt with an input dialog

Returns:
    str: Operation result message

Inputs: var_name (required), value, input_dialog. Returns: result

set_variableB
Set a global show variable (SetVar).

Args:
    var_name: Variable name (should start with $, e.g. "$song")
    value: Numeric, text, or None to delete the variable
    input_dialog: If True (string values only), prompt the operator with an
                  input dialog instead of setting a fixed value

Returns:
    str: Operation result message

Examples:
    - Set $count to 5
    - Set $song to "Opening"
    - Delete $count (value=None)

Inputs: var_name (required), value, input_dialog. Returns: result

stomp_executorA
Stomp an executor (assertive playback, overrides lower priorities).

Args:
    executor_id: Executor number
    page: Optional page number for page-qualified addressing

Returns:
    str: Operation result message

Inputs: executor_id (required), page. Returns: result

stop_effectsA
Stop all running effects for the current selection.

Removes effect values from the programmer using the Off command.

Returns:
    str: Operation result message

Inputs: no named parameters. Returns: result

store_cueB
Store the current programmer state as a cue.

Args:
    cue_id: Cue number to store
    name: (Optional) Name for the cue
    merge: Merge new values into existing cue
    overwrite: Overwrite existing cue entirely
    noconfirm: Suppress store confirmation pop-up

Returns:
    str: Operation result message

Examples:
    - Store cue 1
    - Store cue 5 with name "Blackout" and merge enabled

Inputs: cue_id (required), name, merge, overwrite, +1 more. Returns: result

store_cue_across_sequencesA
Store a cue across a range of sequences.

Iterates over every sequence in the range and stores the specified cue.
Optionally assigns a name to each stored cue.

Args:
    cue_id: Cue number to store (e.g., 1, 0.5)
    sequence_start: First sequence number in the range
    sequence_end: Last sequence number in the range (inclusive)
    cue_name: (Optional) Name for the cue

Returns:
    dict: Result with commands_sent, count, and summary

Examples:
    - Store cue 0.5 across sequences 101-125 with name "((LOADING SONG))"
    - Store cue 1 in sequence 101

Inputs: cue_id (required), sequence_start (required), sequence_end (required), cue_name.

store_presetA
Store current programmer values as a preset.

Use merge to ADD the current selection's values into an existing preset
without disturbing values already stored for other fixture types (the
non-destructive way to extend a global palette to a new fixture group).

Args:
    preset_type: Preset type (dimmer, position, gobo, color, beam, focus, control, shapers, video)
    preset_id: Preset number
    scope: (Optional) Scope: "global", "selective", or "universal"
    merge: Merge into the existing preset (keeps other fixtures' stored values)
    overwrite: Overwrite the existing preset entirely

Returns:
    str: Operation result message

Examples:
    - Store color preset 1
    - Store global dimmer preset 5
    - Extend global color preset 7 to the current selection: merge=True

Inputs: preset_type (required), preset_id (required), scope, merge, +1 more. Returns: result

swop_executorA
Swop an executor (solo with blackout on others) — a live busking control.

Args:
    executor_id: Executor number
    page: Optional page number for page-qualified addressing
    mode: "swop" (momentary), "swop_on" (latching), or "swop_go" (swop and go)

Returns:
    str: Operation result message

Inputs: executor_id (required), page, mode. Returns: result

sync_effects_toolA
Synchronize all running effects.

Resets effect timing so all running effects align their phase.

Returns:
    str: Operation result message

Inputs: no named parameters. Returns: result

temp_executorC
Temporarily activate an executor (Temp).

Args:
    executor_id: Executor number
    page: Optional page number for page-qualified addressing

Returns:
    str: Operation result message

Inputs: executor_id (required), page. Returns: result

toggle_blackoutB
Toggle the grand blackout state.

Returns:
    str: Operation result message

Inputs: no named parameters. Returns: result

toggle_blindA
Toggle blind editing mode (edit cues without affecting stage output).

Args:
    executor_id: Optional executor to toggle blind for (global if omitted)
    page: Optional page for page-qualified executor addressing
    edit_mode: If True, toggle blind-edit mode (global) instead

Returns:
    str: Operation result message

Inputs: executor_id, page, edit_mode. Returns: result

toggle_highlightA
Toggle highlight mode for fixture programming.

Returns:
    str: Operation result message

Inputs: no named parameters. Returns: result

toggle_previewB
Toggle preview mode (visualize output without affecting DMX).

Args:
    executor_id: Optional executor to toggle preview for (global if omitted)
    page: Optional page for page-qualified executor addressing
    edit_mode: If True, toggle preview-edit mode (global) instead

Returns:
    str: Operation result message

Inputs: executor_id, page, edit_mode. Returns: result

top_executorC
Set an executor to top priority (Top).

Args:
    executor_id: Executor number
    page: Optional page for page-qualified addressing

Returns:
    str: Operation result message

Inputs: executor_id (required), page. Returns: result

unpark_fixtureA
Unpark previously parked DMX output.

Args:
    target: What to unpark (e.g. "fixture 1"); omit for the current selection.

Returns:
    str: Operation result message

Inputs: target. Returns: result

update_cueA
Update an existing cue with current programmer values (Update).

Unlike store, Update merges programmer changes into an existing cue without
re-storing. WARNING: this modifies existing programming.

Args:
    cue_id: Cue number to update
    sequence_id: Optional sequence the cue belongs to
    merge: Add /merge flag
    cueonly: Add /cueonly flag (do not track changes forward)
    tracking: Add /tracking flag

Returns:
    str: Operation result message

Inputs: cue_id (required), sequence_id, merge, cueonly, +1 more. Returns: result

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    B3/5.0

    Scored across 101 tools

    Disambiguation3/5

    Most tools target distinct object/action pairs with detailed descriptions, but there are several overlapping boundaries: label_object vs label_macro_tool vs label_sequence_cue, set_effect_speed vs set_effect_seconds, goto_cue_tool vs execute_sequence(goto), and multiple store/update cue variants. An agent would often need to read full descriptions to avoid picking the wrong tool.

    Naming Consistency2/5

    A loose verb_noun pattern exists, but it is inconsistently applied: some tools have a random _tool suffix (save_show_tool, delete_macro_tool, sync_effects_tool), some use non-verb forms (temp_executor, next_fixture, appearance_cue_across_sequences), and similar operations use different verbs (list_groups vs list_shows_tool, read_cue_info vs get_cue_annotation). The naming feels ad hoc rather than systematic.

    Tool Count1/5

    101 tools is far beyond any reasonable scope for a focused MCP server. Even for a broad grandMA2 console integration, this count is excessive and creates high selection and maintenance overhead.

    Completeness4/5

    The tool surface covers core workflows comprehensively: cues, presets, macros, effects, executors, variables, timecode, MIDI, show files, and palette building. Notable gaps exist around patch/fixture creation (only delete_fixture is provided) and generic sequence lifecycle, but send_raw_command provides a workaround for these edge cases.

    Maintenance

    ActivityInactive
    ResponsivenessOutdated data

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      Not graded
      quality
      B
      maintenance
      Transforms an ETC Eos lighting console into a service controllable by AI assistants and automation tools via MCP and OSC, enabling cue management, preset recall, and channel level control.
      4
      AGPL 3.0
    • F
      license
      B
      quality
      D
      maintenance
      MCP server for controlling Ableton Live, enabling AI assistants to interact with Live sessions through tools for track/clip/scene management, playback control, and device parameter adjustments.
      48
      -
    • A
      license
      A
      quality
      B
      maintenance
      Enables MCP clients such as Claude Code or Claude Desktop to control grandMA3 onPC by executing command-line commands, running Lua, reading show data like sequences, cues, executors, patch and pools, driving playback and faders, and looking things up in the manual. It works through a Lua bridge plugin inside onPC, falling back to OSC for write-only commands when the bridge is unavailable.
      75
      4
      MIT