Skip to main content
Glama
channels.py13.8 kB
"""Channel rack control tools for FL Studio.""" from __future__ import annotations from typing import TYPE_CHECKING if TYPE_CHECKING: from fastmcp import FastMCP def register_channel_tools(mcp: FastMCP) -> None: """Register channel rack tools with the MCP server.""" from fl_studio_mcp.utils.connection import get_connection @mcp.tool() def fl_get_channel_count(global_count: bool = True) -> int: """Get the number of channels in the channel rack. Args: global_count: If True, returns total channels ignoring groups. If False, returns channels in current group only. """ conn = get_connection() result = conn.send_command("channels.getCount", {"global_count": global_count}) if not result.get("success", False) and "error" in result: return -1 return result.get("count", 0) @mcp.tool() def fl_get_channel_info(index: int, use_global_index: bool = True) -> dict: """Get detailed information about a channel. Args: index: Channel index use_global_index: Whether to use global channel indexing """ conn = get_connection() result = conn.send_command("channels.getInfo", { "index": index, "use_global": use_global_index, }) if not result.get("success", False) and "error" in result: return {"error": result["error"]} return { "index": result.get("index", index), "name": result.get("name", ""), "color": result.get("color", "0x0"), "volume": result.get("volume", 0), "pan": result.get("pan", 0), "pitch": result.get("pitch", 0), "is_muted": result.get("is_muted", False), "is_solo": result.get("is_solo", False), "is_selected": result.get("is_selected", False), "target_fx_track": result.get("target_fx_track", 0), } @mcp.tool() def fl_get_all_channels() -> list[dict]: """Get information about all channels in the channel rack. Returns a list of all channels with their basic properties. """ conn = get_connection() result = conn.send_command("channels.getAll") if not result.get("success", False) and "error" in result: return [{"error": result["error"]}] return result.get("channels", []) @mcp.tool() def fl_get_selected_channel() -> dict | None: """Get information about the currently selected channel. Returns None if no channel is selected. """ conn = get_connection() result = conn.send_command("channels.getSelected") if not result.get("success", False) and "error" in result: return {"error": result["error"]} return result.get("channel") @mcp.tool() def fl_select_channel(index: int, select: bool = True) -> str: """Select or deselect a channel. Args: index: Channel index (global) select: True to select, False to deselect """ conn = get_connection() result = conn.send_command("channels.select", { "index": index, "select": select, }) if not result.get("success", False) and "error" in result: return f"Error: {result['error']}" channel_name = result.get("channel_name", f"Channel {index}") return f"Channel '{channel_name}' {'selected' if select else 'deselected'}" @mcp.tool() def fl_select_one_channel(index: int) -> str: """Select only one channel, deselecting all others. Args: index: Channel index (global) to select exclusively """ conn = get_connection() result = conn.send_command("channels.selectOne", {"index": index}) if not result.get("success", False) and "error" in result: return f"Error: {result['error']}" channel_name = result.get("channel_name", f"Channel {index}") return f"Channel '{channel_name}' selected exclusively" @mcp.tool() def fl_trigger_note( channel: int, note: int, velocity: int = 100, midi_channel: int = -1 ) -> str: """Trigger a MIDI note on a channel (real-time, does NOT persist to pattern). This triggers the note in real-time. To persist notes in a pattern, FL Studio must be in record mode, or use the step sequencer functions. Args: channel: Channel index (global) note: MIDI note number (0-127, where 60 = C5/Middle C) velocity: Note velocity (1-127, 0 = note off) midi_channel: MIDI channel (-1 for default) """ if not 0 <= note <= 127: return "Error: Note must be between 0 and 127" if not 0 <= velocity <= 127: return "Error: Velocity must be between 0 and 127" conn = get_connection() result = conn.send_command("channels.triggerNote", { "channel": channel, "note": note, "velocity": velocity, "midi_channel": midi_channel, }) if not result.get("success", False) and "error" in result: return f"Error: {result['error']}" note_names = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"] note_name = note_names[note % 12] octave = (note // 12) - 1 if velocity == 0: return f"Note {note_name}{octave} (MIDI {note}) released on channel {channel}" return f"Note {note_name}{octave} (MIDI {note}) triggered with velocity {velocity} on channel {channel}" @mcp.tool() def fl_set_channel_volume(index: int, volume: float) -> str: """Set the volume of a channel. Args: index: Channel index (global) volume: Volume level from 0.0 (silence) to 1.0 (full) """ if not 0.0 <= volume <= 1.0: return "Error: Volume must be between 0.0 and 1.0" conn = get_connection() result = conn.send_command("channels.setVolume", { "index": index, "volume": volume, }) if not result.get("success", False) and "error" in result: return f"Error: {result['error']}" channel_name = result.get("channel_name", f"Channel {index}") new_volume = result.get("volume", volume) return f"Channel '{channel_name}' volume set to {new_volume:.2f}" @mcp.tool() def fl_set_channel_pan(index: int, pan: float) -> str: """Set the pan position of a channel. Args: index: Channel index (global) pan: Pan from -1.0 (full left) to 1.0 (full right), 0.0 = center """ if not -1.0 <= pan <= 1.0: return "Error: Pan must be between -1.0 and 1.0" conn = get_connection() result = conn.send_command("channels.setPan", { "index": index, "pan": pan, }) if not result.get("success", False) and "error" in result: return f"Error: {result['error']}" channel_name = result.get("channel_name", f"Channel {index}") new_pan = result.get("pan", pan) return f"Channel '{channel_name}' pan set to {new_pan:.2f}" @mcp.tool() def fl_mute_channel(index: int, muted: bool | None = None) -> str: """Mute or unmute a channel. Args: index: Channel index (global) muted: True to mute, False to unmute, None to toggle """ conn = get_connection() result = conn.send_command("channels.mute", { "index": index, "muted": muted, }) if not result.get("success", False) and "error" in result: return f"Error: {result['error']}" is_muted = result.get("is_muted", False) channel_name = result.get("channel_name", f"Channel {index}") return f"Channel '{channel_name}' {'muted' if is_muted else 'unmuted'}" @mcp.tool() def fl_solo_channel(index: int, solo: bool | None = None) -> str: """Solo or unsolo a channel. Args: index: Channel index (global) solo: True to solo, False to unsolo, None to toggle """ conn = get_connection() result = conn.send_command("channels.solo", { "index": index, "solo": solo, }) if not result.get("success", False) and "error" in result: return f"Error: {result['error']}" is_solo = result.get("is_solo", False) channel_name = result.get("channel_name", f"Channel {index}") return f"Channel '{channel_name}' {'soloed' if is_solo else 'unsoloed'}" @mcp.tool() def fl_set_channel_name(index: int, name: str) -> str: """Set the name of a channel. Args: index: Channel index (global) name: New name for the channel """ conn = get_connection() result = conn.send_command("channels.setName", { "index": index, "name": name, }) if not result.get("success", False) and "error" in result: return f"Error: {result['error']}" return f"Channel {index} renamed to '{name}'" @mcp.tool() def fl_set_channel_color(index: int, red: int, green: int, blue: int) -> str: """Set the color of a channel. Args: index: Channel index (global) red: Red component (0-255) green: Green component (0-255) blue: Blue component (0-255) """ conn = get_connection() result = conn.send_command("channels.setColor", { "index": index, "r": red, "g": green, "b": blue, }) if not result.get("success", False) and "error" in result: return f"Error: {result['error']}" return f"Channel {index} color set to RGB({red}, {green}, {blue})" @mcp.tool() def fl_route_channel_to_mixer(channel_index: int, mixer_track: int) -> str: """Route a channel to a specific mixer track. Args: channel_index: Channel index (global) mixer_track: Mixer track index to route to """ conn = get_connection() result = conn.send_command("channels.routeToMixer", { "channel_index": channel_index, "mixer_track": mixer_track, }) if not result.get("success", False) and "error" in result: return f"Error: {result['error']}" channel_name = result.get("channel_name", f"Channel {channel_index}") return f"Channel '{channel_name}' routed to mixer track {mixer_track}" # Step Sequencer Tools @mcp.tool() def fl_get_grid_bit(channel: int, position: int) -> bool: """Get whether a step is active in the step sequencer. Args: channel: Channel index (global) position: Step position (0-based) """ conn = get_connection() result = conn.send_command("channels.getGridBit", { "channel": channel, "position": position, }) if not result.get("success", False) and "error" in result: return False return result.get("value", False) @mcp.tool() def fl_set_grid_bit(channel: int, position: int, value: bool) -> str: """Set a step in the step sequencer on or off. This allows basic pattern programming for drum-style instruments. Args: channel: Channel index (global) position: Step position (0-based) value: True to enable the step, False to disable """ conn = get_connection() result = conn.send_command("channels.setGridBit", { "channel": channel, "position": position, "value": value, }) if not result.get("success", False) and "error" in result: return f"Error: {result['error']}" channel_name = result.get("channel_name", f"Channel {channel}") return f"Channel '{channel_name}' step {position} {'enabled' if value else 'disabled'}" @mcp.tool() def fl_get_step_sequence(channel: int, steps: int = 16) -> list[bool]: """Get the step sequence pattern for a channel. Args: channel: Channel index (global) steps: Number of steps to retrieve (default 16) """ conn = get_connection() result = conn.send_command("channels.getStepSequence", { "channel": channel, "steps": steps, }) if not result.get("success", False) and "error" in result: return [] return result.get("sequence", []) @mcp.tool() def fl_set_step_sequence(channel: int, pattern: list[bool]) -> str: """Set a complete step sequence pattern for a channel. Args: channel: Channel index (global) pattern: List of boolean values for each step (True = on, False = off) """ conn = get_connection() result = conn.send_command("channels.setStepSequence", { "channel": channel, "pattern": pattern, }) if not result.get("success", False) and "error" in result: return f"Error: {result['error']}" channel_name = result.get("channel_name", f"Channel {channel}") active_steps = result.get("active_steps", sum(pattern)) total_steps = result.get("total_steps", len(pattern)) return f"Channel '{channel_name}' pattern set with {active_steps}/{total_steps} steps active"

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/karl-andres/fl-studio-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server