GMA2
FreeNot checkedAn MCP server that lets AI assistants control grandMA2 lighting consoles via Telnet, exposing 41 high-level tools for cue management, fixture control, preset ma
About
An MCP server that lets AI assistants control grandMA2 lighting consoles via Telnet, exposing 41 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, and music show workflows.
README
An MCP server that lets AI assistants control grandMA2 lighting consoles via Telnet. It exposes 17 high-level tools for cue management, fixture control, preset management, executor control, and more through the Model Context Protocol.
Table of Contents
- Overview
- Features
- Getting Started
- Usage
- Command Reference
- Project Structure
- Development
- Troubleshooting
- License
- Contributing
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 two layers:
- 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. - MCP Server (
src/server.py) -- A FastMCP server that exposes 17 tools covering cue management, fixture control, presets, executors, global state, labeling, sequence playback, and raw commands over stdio transport. It manages a persistent Telnet connection to the console and delegates command construction to the builder layer. - 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). - 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
- 17 MCP tools -- Cue management (store/delete/goto), fixture control (set values, set attributes, clear programmer), preset management (store/apply), executor control (on/off/go/kill/toggle, fader level, sequence assignment), global state (blackout, highlight), object labeling, sequence playback, and raw command execution.
- Complete command builder -- Over 200 Python functions covering all grandMA2 command-line keywords across 30+ modules, each returning a correctly formatted command string.
- High-level client --
GMA2Clientprovides workflow-level methods: build cue lists, set up fixture groups with presets, quick look programming, batch executor assignments. - Command chaining --
CommandSequencelets you compose multiple commands, preview them, and execute them as a batch. - Async Telnet client -- Built on
telnetlib3with automatic login handling and persistent connections. - Configurable via environment -- Host, port, user, and password set through
.envor 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 console | 127.0.0.1 |
GMA_PORT |
Telnet port | 30000 |
GMA_USER |
Login username | administrator |
GMA_PASSWORD |
Login password | admin |
Port 30000 is the standard command port. Port 30001 is read-only (log output).
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"
}
}
}
}
Usage
MCP Tools
The server exposes 17 tools:
| Tool | Description |
|---|---|
create_fixture_group |
Select a range of fixtures and store as a group |
store_cue |
Store current programmer state as a cue |
delete_cue |
Delete a cue |
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 |
toggle_blackout |
Toggle grand blackout |
toggle_highlight |
Toggle highlight mode |
label_object |
Assign a name label to any MA2 object |
execute_sequence |
Go, pause, or goto a cue in a sequence |
send_raw_command |
Send any grandMA2 command-line instruction |
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"
label_group(5, "Front Wash") # 'label group 5 "Front Wash"'
GMA2Client (Workflow Orchestration)
The GMA2Client class provides high-level workflow methods that compose multiple commands:
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
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 group, apply a preset
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)])
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 2.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 options | store_cue(1, merge=True) -> store cue 1 /merge |
store_preset() |
Store preset with options | store_preset("dimmer", 3) -> store preset 1.3 |
store_group() |
Store a group | store_group(1) -> store group 1 |
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 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" |
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
│ ├── server.py # MCP server (FastMCP, 17 tools, stdio transport)
│ ├── telnet_client.py # Async Telnet connection management
│ └── tools.py # Legacy tool implementations (used by tests)
├── tests/ # Pytest test suite (one file per module)
├── doc/ # grandMA2 user manual (PDF)
├── main.py # Standalone login test script
├── 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 four layers:
- Telnet Client (
src/telnet_client.py) -- Low-level async Telnet communication with the console. - Command Builder (
src/commands/) -- Pure functions that construct command strings. No network access. - Orchestration (
src/gma2_client.py,src/command_sequence.py) -- High-level workflow methods and command batching that compose builders + telnet. - MCP Server (
src/server.py) -- FastMCP server that exposes 17 tools over stdio, 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.
Authentication errors -- Check username/password in .env. Confirm the user account exists on the console.
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 does not currently include a license file.
Contributing
- Fork the repository
- Create a feature branch (
git checkout -b feature/name) - Commit your changes
- Push to the branch and open a Pull Request
Run tests before submitting: make test
Installing GMA2
This server has no published package — it is built from source. Open the repository and follow its README.
▸ github.com/chienchuanw/gma2-mcpFAQ
Is GMA2 MCP free?
Yes, GMA2 MCP is free — one-click install via Unyly at no cost.
Does GMA2 need an API key?
No, GMA2 runs without API keys or environment variables.
Is GMA2 hosted or self-hosted?
A hosted option is available: Unyly runs the server in the cloud, no local setup required.
How do I install GMA2 in Claude Desktop, Claude Code or Cursor?
Open GMA2 on unyly.org, pick your client tab (Claude Desktop, Claude Code, Cursor) and press Install — the config is generated automatically, no JSON editing.
Related MCPs
Omni Video
An MCP server that transforms LLM-enabled IDEs into professional video editors by pre-processing footage into text proxies, generating motion graphics via HTML/
by buildwithtazaARA
Generate images, video and audio from any AI agent — one connector.
by ARAYouTube
Transcripts, channel stats, search
by YouTubeEverArt
AI image generation using various models.
by modelcontextprotocolCompare GMA2 with
Not sure what to pick?
Find your stack in 60 seconds
Author?
Embed badge for your README
Browse similar
All media MCPs
