{
  "name": "rainmaker-mcp",
  "version": "1.0.0",
  "tools": [
    {
      "name": "list_devices",
      "description": "Find ESP RainMaker devices and read their current state. Every device belongs to exactly one group (the user's home) and may sit in subgroups within it (rooms); this tool returns that placement together with the device's live parameters, so one call answers both \"which devices are in the kitchen\" and \"is the kitchen light on\".\n\nCall this first whenever the user names a device, a type of device, or a room instead of giving ids. It is the only tool that turns names into ids, and every row carries both the node_id and the group_id that the other tools require — so one call is always enough. Never follow it with a second lookup to find the group.\n\nEach row carries two different things about parameters, and they are not interchangeable. params is what the device is reporting right now, which is what you read to answer a question about its state. spec is what the device will accept a write for — parameter name to type, range and meaning — and it is what set_params checks against, so it is the one to consult before changing anything. A device names its own parameters, so a colour light may call its hue \"H\"; spec is where you find that out.\n\nDo not call it before set_params or set_schedule when the user has already given you a node_id and group_id, and do not call it to inspect schedules — list_schedules does that.\n\nFilters combine: group_id or subgroup_id to narrow to a home or room, name or type for a kind of device. Node ids look like any other string, so when the user gives you an identifier you cannot classify, put it in name — that matches node ids as well as names, and never comes back empty just because the value turned out to be an id. Reading state fetches each device's shadow, so when you do not need the whole payload pass fields — for example \"node_id,group_id,connected\" or \"params.Light.Power\".\n\nThis server reads and controls devices that already exist, and shows only their state as of now. It cannot add, remove or rename a device, create or rename a home or room, move a device between rooms, or report history, trends or past usage. When the user asks for any of that, tell them it is not available here — there is no tool for it, and no combination of these tools does it.\n\nIn replies to the user prefer the human names in params.\u003cDevice\u003e.Name; use ids only in tool calls.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "fields": {
            "description": "Comma-separated fields to return instead of the whole record. Top level: node_id, group_id, group_name, subgroup_ids, subgroup_names, name, type, model, fw_version, connected, params, spec, config, error. A device whose read failed carries error and is always returned with it, so connected false on a record without error is a genuinely offline device. Dot paths reach inside: params.Light.Power. Keep spec whenever the next step is a write — it is what set_params validates against.",
            "type": "string"
          },
          "group_id": {
            "description": "Restrict to one group (home). Get it from this tool or from list_groups.",
            "type": "string"
          },
          "name": {
            "description": "Match whatever the user called the device, partially and ignoring case: the Name parameter they see in the app, the node's own name, or its node id. Safe to use for any identifier you were given but cannot classify.",
            "type": "string"
          },
          "node_id": {
            "description": "One node id, or several comma-separated, when you know the values are node ids. Omit to search every device the user can reach. If you are not sure whether a string is a node id or a name, pass it as name instead — that matches ids too.",
            "type": "string"
          },
          "subgroup_id": {
            "description": "Restrict to one subgroup (room) within the group. Get it from list_groups.",
            "type": "string"
          },
          "type": {
            "description": "Match a device type, partially and ignoring case. Accepts a bare word (light, switch, sensor) or a full type such as esp.device.lightbulb.",
            "type": "string"
          }
        }
      }
    },
    {
      "name": "list_groups",
      "description": "List the groups and subgroups that organise the user's devices — their homes and the rooms inside them — with the number of devices in each. Use it for questions about structure (\"what rooms do I have?\", \"which homes can I see?\") and to turn a group name into a group_id.\n\nThis tool describes placement only. It never returns parameters, connectivity or device configuration, and the node ids it can list carry no names or types. For anything about the devices themselves — including which devices are in a given room — call list_devices with a group_id or subgroup_id filter instead.\n\nSet include_devices when you need the node ids in each group and subgroup but not their state. subgroups is always present: an empty array means this home genuinely has no rooms, so take it at face value rather than looking again.\n\nThis tool only reads. Nothing here creates, renames or deletes a home or a room, moves a device between them, or reports history — if the user asks for that, say it is not available rather than reaching for another tool.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "group_id": {
            "description": "Return only this group. Omit both group_id and group_name to list them all.",
            "type": "string"
          },
          "group_name": {
            "description": "Return only the group with this name, ignoring case. Do not pass it together with group_id.",
            "type": "string"
          },
          "include_devices": {
            "description": "Add the node ids belonging to each group and subgroup. Defaults to false, which returns counts only.",
            "type": "boolean"
          }
        }
      }
    },
    {
      "name": "list_schedules",
      "description": "List the schedules stored on one device: each schedule's id, name, enabled flag, triggers and the parameters it applies. ESP RainMaker keeps schedules per device, so ask about one node at a time.\n\nCall this to turn a schedule the user described rather than named — \"the morning alarm\", \"the one that shuts the porch light off\" — into the schedule_id that set_schedule needs. Do not use list_devices for that; schedules are not part of a device's parameters.\n\nTriggers come back in the device's own form: m is minutes past midnight and d is a weekday bitmask with Monday as the lowest bit. Translate them when you answer the user — m 420 with d 31 means 07:00 on weekdays.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "group_id": {
            "description": "The group the device belongs to. list_devices returns it alongside the node_id.",
            "type": "string"
          },
          "node_id": {
            "description": "The device whose schedules to list.",
            "type": "string"
          }
        },
        "required": [
          "node_id",
          "group_id"
        ]
      }
    },
    {
      "name": "set_params",
      "description": "Change what a device is doing right now — switch it on or off, set brightness or temperature, or reboot or factory reset it where the device exposes a parameter for that. This is a direct action tool: call it as soon as the user asks for a change and you hold the ids.\n\nIf the user gave you node_id and group_id, act on them immediately. Only call list_devices first when the device is identified by name, type or room and you do not have the ids yet. To act on several devices in one go, pass their ids comma-separated in node_id; they must all belong to the group in group_id, and the same params are applied to every one of them, so only batch devices that share the same device names.\n\nparams is keyed by the device name inside the node and then by parameter: {\"Light\": {\"Power\": true, \"Brightness\": 80}}. Names are matched exactly and case-sensitively against the device's own configuration, and a write that names a device or parameter the device did not declare is rejected in full — nothing is sent. The rejection lists the parameters the device does have, so if you are unsure, send the call and read the error rather than asking the user. There is no {\"OTA\": {\"Trigger\": true}} that starts a firmware update and no key that adds a capability the device never reported; if nothing covers what the user asked for, tell them this device does not support it.\n\nThe spec field on each list_devices row is what a device accepts — parameter name to type, range and meaning, as in {\"Colour Light\": {\"H\": \"int 0-360, hue\", \"V\": \"int 0-100, brightness\"}}. Use it rather than the params field, which is only what the device currently reports: a light whose hue is named \"H\" will not accept \"Hue\". Values are checked too — send real booleans for on/off parameters, not \"true\" or \"on\", and keep numbers inside the stated range.\n\nDo not use this to read state — list_devices does that — and do not use it for anything that happens later. If the request carries a time, a delay or a repetition (\"at 7am\", \"every weekday\", \"in ten minutes\", \"every night\"), it belongs to set_schedule, even when it also names a device and the state to put it in.\n\nDelivery to the device is asynchronous: success means the change was accepted and published, not that the device has already applied it.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "group_id": {
            "description": "The group every listed device belongs to. list_devices returns it alongside the node_id.",
            "type": "string"
          },
          "node_id": {
            "description": "One node id, or several comma-separated to apply the same change to each.",
            "type": "string"
          },
          "params": {
            "description": "Device name to parameter name to value: {\"Light\": {\"Power\": true, \"Brightness\": 80}}. Case-sensitive. Both names must appear in the device's spec from list_devices; a write naming anything else, or a value of the wrong type or out of range, is rejected in full and the error names what the device does accept.",
            "type": "object"
          }
        },
        "required": [
          "node_id",
          "group_id",
          "params"
        ]
      }
    },
    {
      "name": "set_schedule",
      "description": "Create, change, remove, enable or disable a schedule on one device. This is a direct action tool: call it as soon as the user asks for something to happen at a time or on a repeating basis, including loosely specified requests like \"wake me up with the lights\" or \"turn the heater off overnight\" — choose sensible values rather than asking the user for the wire format.\n\noperation is one of add, edit, remove, enable, disable. add needs name, triggers and action; every other operation needs schedule_id. When the user refers to a schedule by description instead of by id, call list_schedules to resolve it — never list_devices.\n\nWrite triggers in human terms and this tool converts them: {\"time\": \"07:00\", \"days\": \"weekdays\"} or {\"time\": \"20:30\", \"days\": [\"sat\",\"sun\"]}. days also accepts \"daily\" and \"weekends\". action takes the same shape as the params argument of set_params: {\"Light\": {\"Power\": true}}.\n\nedit merges into the stored schedule, so send only the fields that change. The device is given its complete schedule set on every write, so avoid issuing concurrent edits to the same node.\n\nDo not use this for an immediate change (set_params) or to read schedules back (list_schedules).",
      "inputSchema": {
        "type": "object",
        "properties": {
          "action": {
            "description": "What it does, in the same shape as the params argument of set_params: {\"Light\": {\"Power\": true}}. Required when adding.",
            "type": "object"
          },
          "enabled": {
            "description": "Whether the schedule is active. New schedules are enabled unless you set this to false.",
            "type": "boolean"
          },
          "group_id": {
            "description": "The group the device belongs to. list_devices returns it alongside the node_id.",
            "type": "string"
          },
          "info": {
            "description": "Free-text note stored with the schedule.",
            "type": "string"
          },
          "name": {
            "description": "What to call the schedule, up to 32 characters. Required when adding.",
            "type": "string"
          },
          "node_id": {
            "description": "The device to schedule. One device per call.",
            "type": "string"
          },
          "operation": {
            "description": "add needs name, triggers and action. edit, remove, enable and disable need schedule_id.",
            "enum": [
              "add",
              "edit",
              "remove",
              "enable",
              "disable"
            ],
            "type": "string"
          },
          "schedule_id": {
            "description": "The schedule to act on, from list_schedules. Required for every operation except add, where supplying one pins the new schedule's id instead of generating it — 8 characters or fewer, since the device keys the schedule by it. Leave it out and a free one is generated.",
            "type": "string"
          },
          "triggers": {
            "description": "When it fires: [{\"time\": \"07:00\", \"days\": \"weekdays\"}]. days takes daily, weekdays, weekends, or a list like [\"mon\",\"tue\"]. Required when adding.",
            "items": {
              "type": "object"
            },
            "type": "array"
          }
        },
        "required": [
          "node_id",
          "group_id",
          "operation"
        ]
      }
    }
  ]
}
