Skip to main content
Glama
ESP RainMaker MCP Server

ESP RainMaker MCP Server

Official
by espressif

ESP RainMaker MCP Server

This project provides a Model Context Protocol (MCP) server wrapper around the esp-rainmaker-cli Python library. It allows MCP-compatible clients (like LLMs or applications such as Cursor, Claude Desktop, and Windsurf) to interact with your ESP RainMaker devices using the official CLI.

Introduction to Model Context Protocol (MCP)

The Model Context Protocol (MCP) is a standardized framework that enables AI systems to interact with external tools, data sources, and services in a unified manner. Introduced by Anthropic and adopted by major AI organizations, MCP acts as a universal interface, much like USB-C for hardware, allowing seamless integration across different platforms.

Key Benefits of MCP in ESP RainMaker

  • Unified Interaction: MCP allows AI models to access and control IoT devices using natural language prompts, making interactions more intuitive and accessible.

  • Real-time Control: With MCP, users can execute actions such as turning devices on/off, adjusting settings, and managing schedules directly through AI interfaces.

  • Local Server, Cloud-Backed Control: The ESP RainMaker MCP server runs locally and stores credentials on your machine. However, device management actions are performed via the official ESP RainMaker cloud APIs through the esp-rainmaker-cli.

By integrating MCP, the ESP RainMaker platform enhances its capabilities, allowing tools like Claude, Cursor, Windsurf, and Gemini CLI to manage IoT devices efficiently and securely.

Related MCP server: WiseVision/mcp_server_ros_2

Prerequisites

  • Python: Version 3.10 or higher

  • uv: The uv Python package manager. Install from Astral's uv documentation.

  • ESP RainMaker CLI Login: You must have successfully logged into ESP RainMaker using the standard esp-rainmaker-cli login command in your terminal at least once. This server relies on the credentials saved by that process.

  • RainMaker Nodes added into your account since onboarding isn't supported by the MCP server.

Installation & Setup

  1. Clone the Repository:

    git clone https://github.com/espressif/esp-rainmaker-mcp.git
    cd esp-rainmaker-mcp
  2. Install Dependencies using uv: This command installs esp-rainmaker-cli, mcp[cli], and any other dependencies listed in pyproject.toml into a virtual environment managed by uv.

    uv sync

    (This assumes uv is installed)

  3. Login to ESP Rainmaker using esp-rainmaker-cli

    uv run esp-rainmaker-cli login
NOTE

Direct login via username/password within MCP is not supported for security reasons. Please use the standard CLI login flow first.

Client Configuration

To add this project as an MCP server in supported MCP clients (Cursor, Claude Desktop, Windsurf, and Gemini CLI), you'll need to add the same JSON configuration to each client's config file. The configuration is identical across all clients:

MCP Server Configuration (All Clients)

Use the following JSON configuration for all MCP clients:

{
   "mcpServers": {
      "ESP-RainMaker-MCP": {
         "command": "uv",
         "args": [
            "run",
            "--with",
            "esp-rainmaker-cli",
            "--with",
            "mcp[cli]",
            "mcp",
            "run",
            "<absolute_path_to_repo>/server.py"
         ]
      }
   }
}
IMPORTANT

Replace<absolute_path_to_repo>/server.py with the actual absolute path to the server.py file within the cloned esp-rainmaker-mcp directory on your system.

Cursor MCP Server Setup

  1. Open Cursor and click on the settings (gear icon) at the top right.

  2. Navigate to "Tools & Integrations" from the settings menu.

  3. Click on "MCP Tools" in the integrations section.

  4. Click on "New MCP Server" to add a new server.

  5. This will open the mcp.json file. Add the JSON configuration shown above.

Claude Desktop MCP Server Setup

  1. Open Claude Desktop and go to Settings -> Developer -> Edit Config.

  2. This will open the configuration file (claude_desktop_config.json). Add the JSON configuration shown above.

  3. Save the changes and restart Claude Desktop to apply the new settings.

Windsurf MCP Server Setup

  1. Open Windsurf and look for the hammer-type icon under the chat text input area.

  2. Click on the hammer icon and select "Configure" from the options. This will open the plugins window.

  3. Click on "View raw config" which will show you the ~/.codium/windsurf/mcp_config.json file.

  4. Add the JSON configuration shown above to the file.

  5. Save the changes and click on "Refresh" under the chat text window to load the ESP RainMaker MCP tools.

Gemini CLI MCP Server Setup

  1. Locate your Gemini CLI settings file. On macOS, this is typically at ~/.gemini/settings.json.

  2. Open the settings.json file in your preferred text editor.

  3. Add the JSON configuration shown above to the mcpServers section of the file. If the section does not exist, create it as shown in the example.

  4. Save the file and restart Gemini CLI if it is running.

NOTE

The configuration for all four applications (Cursor, Claude Desktop, Windsurf, and Gemini CLI) is the same, so you can use the same JSON structure for all of them.

NOTE

The--with arguments ensure uv includes the necessary dependencies when running the mcp run command.

How it Works

This server acts as a bridge. It uses the mcp library to handle the Model Context Protocol communication. When a tool is called:

  1. It uses functions from the installed esp-rainmaker-cli library.

  2. The library functions read locally stored authentication tokens.

  3. It makes the necessary API calls to the ESP RainMaker cloud.

  4. It returns the results (or errors) back through the MCP protocol.

Available Tools

This MCP server exposes the following tools for interacting with ESP RainMaker:

Authentication & Configuration

  • login_instructions():

    • Provides instructions (formatted with Markdown) on how to log in using the standard esp-rainmaker-cli login command in your terminal. This server relies on the external CLI's browser-based login flow to securely store credentials. Rendering as Markdown depends on the MCP client's capabilities.

  • check_login_status():

    • Checks if a valid login session exists based on credentials stored locally by esp-rainmaker-cli. Confirms if the server can communicate with the ESP RainMaker backend.

Node Management

  • get_nodes():

    • Lists all node IDs associated with the logged-in user.

  • get_node_details(node_id: str = None, fields: str = None, name: str = None, type_: str = None):

    • Get detailed information for nodes including config, status, and params.

    • Supports filtering and field selection:

      • fields: comma-separated list of fields to include (e.g. "node_id,name,type,config,params,status.connectivity,fw_version,mapping_timestamp")

      • name: substring match (user-visible name from params)

      • type_: substring match (device type)

      • node_id: single node ID (for one node) or None (for all)

    • Returns a dict (single node) or list of dicts (all nodes).

    • Example:

      get_node_details(ctx, fields="node_id,name,type")
  • get_node_status(node_id: str):

    • Get the online/offline connectivity status for a specific node ID.

  • get_params(node_id: str):

    • Get current parameter values for a device.

  • set_params(node_id: str, params_dict: dict):

    • Set parameters for one or more devices.

    • node_id: Single ID or comma-separated list (e.g., "light1,light2")

    • params_dict: Parameters to set, e.g., {"Light": {"Power": true}}

Schedule Management

  • get_schedules(node_id: str):

    • Get schedules for a device.

  • set_schedule(node_id: str, operation: str, ...):

    • Manage device schedules.

    • operation: "add", "edit", "remove", "enable", or "disable"

    • For add/edit: Provide name, trigger, and action

    • Common triggers:

      • Daily 8 AM: {"m": 480, "d": 127}

      • Weekdays 6:30 PM: {"m": 1110, "d": 31}

    • Example action: {"Light": {"Power": true}}

Group Management (Home/Room Hierarchy)

  • create_group(name: str, group_type: str = None, ...):

    • Create a home or room.

    • Required: name, group_type ("home" or "room")

    • For rooms: parent_group_id required

    • Example: create_group("Living Room", "room", parent_group_id="home_id")

  • get_group_details(group_id: str = None, include_nodes: bool = False):

    • Get group information. For all groups, use group_id=None.

    • Set include_nodes=True to include device details.

    • Returns: Group hierarchy, members, and metadata.

  • update_group(group_id: str, ...):

    • Update group properties or manage devices.

    • Optional: name, description, add_nodes, remove_nodes

    • Examples:

      • Rename: update_group("group_id", name="New Name")

      • Add devices: update_group("group_id", add_nodes="light1,light2")

  • add_device_to_room(device_node_id: str, room_group_id: str):

    • Add device to room (handles parent group automatically).

    • Example: add_device_to_room("light1", "kitchen_id")

License

This project is licensed under the terms specified in the LICENSE file.

Available Tools

13 tools
add_device_to_roomA

Add a device to a room with automatic parent group handling.

This function automatically handles the ESP RainMaker hierarchy requirement:

  1. First adds the device to the parent group (e.g., "My Home")

  2. Then adds the device to the target room group (e.g., "Kitchen")

Parameters:

  • device_node_id: The node ID of the device to add

  • room_group_id: The group ID of the room to add the device to

This is a convenience function that makes the hierarchical behavior explicit.

Inputs: device_node_id (required), room_group_id (required). Returns: result

check_login_statusA

Checks if a valid login session exists based on stored credentials.

Inputs: no named parameters. Returns: result

create_groupA

Create a new group (home, room, or custom group) using Python library API.

Parameters:

  • name: Name of the group (required)

  • group_type: Type of group ('home', 'room', or custom type)

  • description: Description of the group (optional)

  • mutually_exclusive: Set mutually exclusive flag (recommended for homes and rooms)

  • parent_group_id: Parent group ID (required for rooms under a home)

  • nodes: Comma-separated list of node IDs to add to the group (optional)

  • custom_data: Custom data as JSON string (optional)

Examples:

  • Create home: create_group("My Home", "home", mutually_exclusive=True)

  • Create room: create_group("Living Room", "room", mutually_exclusive=True, parent_group_id="home_id")

Inputs: name (required), group_type, description, mutually_exclusive, +3 more. Returns: result

get_group_detailsA

Get comprehensive group information using Python library API.

Parameters:

  • group_id: ID of specific group to show (optional - if None, lists all groups)

  • include_nodes: Include detailed node information for groups

  • fields: List of group/sub-group keys to include (if None, includes all fields)

  • node_fields: List of node keys to include (if None, includes all node fields)

When group_id is None: Lists all groups with hierarchy When group_id is provided: Shows detailed information for that specific group When include_nodes is True: Includes comprehensive node details within groups


IMPORTANT USAGE GUIDELINES:

  1. For home/room structure overview (recommended for most queries):

    {
        "include_nodes": true,
        "fields": ["group_id", "group_name", "type", "total", "sub_groups", "node_details"],
        "node_fields": ["node_id", "name", "type"]
    }
  2. For home/room structure with basic device info:

    {
        "include_nodes": true,
        "fields": ["group_id", "group_name", "type", "total", "sub_groups", "node_details"],
        "node_fields": ["node_id", "name", "type", "model", "fw_version"]
    }
  3. For detailed technical information:

    {
        "include_nodes": true,
        "fields": ["group_id", "group_name", "type", "total", "sub_groups", "node_details",
                  "is_matter", "fabric_id", "primary", "mutually_exclusive"],
        "node_fields": ["node_id", "name", "type", "model", "fw_version", "status", "params"]
    }

Common Use Cases:

Query Type

Recommended Fields

"Show my home"

Basic fields (option 1) to avoid large responses

"List my devices"

Basic fields (option 1) for clear device listing

"Check my rooms"

Basic fields (option 1) for room structure

"Device details"

Detailed fields (option 3) for full information

Note: When fields/node_fields are not provided, defaults to summary mode (basic fields) to avoid overwhelming responses.

Inputs: group_id, include_nodes, fields, node_fields. Returns: result

get_node_detailsA

PREFERRED TOOL for getting comprehensive node information efficiently. Gets config, status, and params in a single API call instead of multiple separate calls.

USAGE:

  • To get all nodes: set node_id=None (default).

  • To get a single node: set node_id to a single node ID string.

    • Only a single node ID is supported. Do NOT use a comma-separated list or array.

    • If the node is not found or not accessible, the result will be an empty dict or contain empty/unknown fields.

FILTERING & FIELD SELECTION:

  • fields: comma-separated list of fields to include in the output (e.g. "node_id,name,type,config,params,status.connectivity,fw_version,mapping_timestamp").

  • name: substring match (matches user-visible name, extracted from params.{DeviceType}.Name).

  • type_: substring match (matches device type, from config.devices[].type).

  • node_id: single node ID string (for a single node), or None (for all nodes).

MULTIPLE NODE FILTERING:

  • To filter for multiple node IDs, set node_id=None and use filtering (e.g. by name or type) in post-processing.

  • Passing a comma-separated list of node IDs is NOT supported and will result in an error.

RETURN VALUE:

  • If a single node is requested, returns a dict (or empty dict if not found).

  • If all nodes are requested, returns a list of dicts (one per node).

EXAMPLES:

  • Get all nodes, only node_id and name: get_node_details(ctx, fields="node_id,name")

  • Get a single node by ID: get_node_details(ctx, node_id="30EDA0E0AF48", fields="node_id,name,config")

  • Get all nodes of type 'lightbulb': get_node_details(ctx, type_="lightbulb", fields="node_id,name,type")

Inputs: node_id, fields, name, type_. Returns: result

get_nodesA

Get ONLY the list of node IDs (names) without detailed information.

Use this tool only when:

  • User specifically asks for "node IDs", "device names", or "list of devices"

  • You need just the names/IDs for reference

For comprehensive device information, use get_node_details instead.

Inputs: no named parameters. Returns: result

get_node_statusA

Get ONLY the online/offline status for a specific node.

Use this tool only when:

  • User specifically asks about "status", "online", "offline" of a particular device

  • You already have other info and need just the status

For comprehensive device information, use get_node_details instead.

Inputs: node_id (required). Returns: result

get_paramsA

Get ONLY the current parameters (state) for a specific node.

Use this tool only when:

  • User specifically asks for "parameters", "state", or "current values" of a particular device

  • You already have other info and need just the current state

For comprehensive device information, use get_node_details instead.

Inputs: node_id (required). Returns: result

get_schedulesA

Get schedule information for a specific node. Returns the schedules configured for the node if any exist, along with support status.

Response includes:

  • schedules_supported: Whether the node supports scheduling

  • schedules: Array of schedule objects with trigger and action details

  • schedule_count: Number of configured schedules

Each schedule object contains:

  • id: Unique schedule identifier

  • name: Human-readable schedule name

  • enabled: Whether the schedule is active

  • triggers: Array of trigger conditions (format explained below)

  • action: What the schedule will do when triggered

TRIGGER FORMAT GUIDE (for understanding schedule responses):

  • "m": Minutes since midnight (0-1439). Example: 480 = 8:00 AM, 1110 = 6:30 PM

  • "d": Day bitmap for which days to trigger:

    • 31 = Weekdays (Mon-Fri)

    • 96 = Weekends (Sat-Sun)

    • 127 = Every day

    • 0 = One-time only

    • Individual days: 1=Mon, 2=Tue, 4=Wed, 8=Thu, 16=Fri, 32=Sat, 64=Sun

  • "dd": Day of month (1-31)

  • "mm": Month bitmap (4095 = all months)

  • "rsec": Relative seconds from creation time

  • "ts": Exact Unix timestamp when schedule was created or will trigger

Example: {"m": 1110, "d": 31} means "6:30 PM on weekdays"

NOTE: Schedule actions use device type names (like "Light") not display names. See get_node_details for the mapping between device types and their display names.

Inputs: node_id (required). Returns: result

login_instructionsA

Provides instructions (formatted with Markdown) on how to log in using the standard ESP RainMaker CLI. This server relies on credentials saved locally by that process. Rendering as Markdown depends on the MCP client capabilities.

Inputs: no named parameters. Returns: result

set_paramsA

Set parameters for one or more nodes using a JSON object (dictionary).

Parameters:

  • node_id: Single node ID or comma-separated list of node IDs (e.g., "node1" or "node1,node2,node3")

  • params_dict: Dictionary containing the parameters to set

Example params_dict value: {'Thermostat': {'Power': False}}

When multiple node IDs are provided, the same parameters will be applied to all specified nodes. This is useful for bulk operations like turning off multiple devices or setting common configurations.

Inputs: node_id (required), params_dict (required). Returns: result

set_scheduleA

Manage schedules for one or more nodes.

Parameters:

  • node_id: Single node ID or comma-separated list of node IDs (e.g., "node1" or "node1,node2,node3")

  • operation: Operation to perform (add, edit, remove, enable, disable)

  • schedule_id: Schedule ID (required for edit, remove, enable, disable operations)

  • name: Schedule name (required for add operation, optional for edit)

  • trigger: Dictionary defining when to trigger (required for add, optional for edit)

  • action: Dictionary defining what to do (required for add, optional for edit)

  • info: Additional information for the schedule (optional)

  • flags: General purpose flags for the schedule (optional)

When multiple node IDs are provided:

  • For 'add' operations: Creates the same schedule on all specified nodes with a common schedule ID

  • For 'edit/remove/enable/disable' operations: Applies the operation to the specified schedule on all nodes

  • This is useful for bulk schedule management across multiple devices

TRIGGER FORMAT GUIDE: Time-based triggers use these fields:

  • "m": Minutes since midnight (0-1439). Example: 480 = 8:00 AM, 1110 = 6:30 PM

  • "d": Day bitmap for which days to trigger:

    • 31 = Weekdays (Mon-Fri)

    • 96 = Weekends (Sat-Sun)

    • 127 = Every day

    • 0 = One-time only

    • Individual days: 1=Mon, 2=Tue, 4=Wed, 8=Thu, 16=Fri, 32=Sat, 64=Sun

  • "dd": Day of month (1-31)

  • "mm": Month bitmap (4095 = all months)

  • "rsec": Relative seconds from now

  • "ts": Exact Unix timestamp

COMMON TRIGGER EXAMPLES:

  • Daily 8:00 AM: {"m": 480, "d": 127}

  • Weekdays 6:30 PM: {"m": 1110, "d": 31}

  • Weekends 10:00 AM: {"m": 600, "d": 96}

  • One-time 7:00 PM: {"m": 1140, "d": 0}

  • 15th of every month at noon: {"m": 720, "dd": 15, "mm": 4095}

  • In 1 hour: {"rsec": 3600}

ACTION EXAMPLES:

  • Turn on light: {"Light": {"Power": true}}

  • Set brightness: {"Light": {"Power": true, "Brightness": 80}}

  • Control thermostat: {"Thermostat": {"Power": true, "Temperature": 22}}

NOTE: Action keys use device type names (like "Light") not display names. Use get_node_details to see device types in config.devices[].name.

Inputs: node_id (required), operation (required), schedule_id, name, +4 more. Returns: result

update_groupA

Edit an existing group's properties and manage nodes using Python library API.

Parameters:

  • group_id: ID of the group to edit (required)

  • name: New name for the group (optional)

  • description: New description for the group (optional)

  • custom_data: New custom data as JSON string (optional)

  • add_nodes: Comma-separated list of node IDs to add to the group (optional)

  • remove_nodes: Comma-separated list of node IDs to remove from the group (optional)

At least one parameter must be provided.

Inputs: group_id (required), name, description, custom_data, +2 more. Returns: result

Tool Schema Changelog

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

Loading tool schema changelog…

    TDQS

    A4.3/5.0

    Scored across 13 tools

    Disambiguation5/5

    Each tool has a clearly distinct purpose, even those that seem similar (e.g., get_nodes vs get_node_details vs get_node_status vs get_params) are well-differentiated with explicit usage guidance in descriptions. The convenience function add_device_to_room is distinct from create_group and update_group.

    Naming Consistency5/5

    All tool names follow a consistent verb_noun pattern in snake_case (e.g., create_group, get_node_details, set_params). Even login_instructures, while noun_verb, is a clear action. No mixing of conventions.

    Tool Count5/5

    With 13 tools, the server is well-scoped for the ESP RainMaker domain. The count is within the recommended range, covering login, groups, nodes, and schedules without unnecessary duplication.

    Completeness4/5

    The tool surface covers core CRUD for groups and nodes (create, read, update) and schedule management (add, edit, remove, enable, disable). However, missing operations like delete_group and delete_node represent minor gaps that agents might encounter.

    Maintenance

    ActivityInactive
    ResponsivenessOutdated data

    Related MCP Connectors

    Related MCP Servers