[APP][Pro] MCP AI Bridge - Control Homey with any AI assistant (Claude, ChatGPT, Gemini, Copilot, Cursor or Mistral) via the Model Context Protocol

:robot: MCP AI Bridge

Control your Homey Pro with any AI assistant. Claude, ChatGPT, Gemini, Copilot, Cursor or Mistral. Just ask in plain language.

:speech_balloon: Talk to your Homey like you talk to a person. “Turn off the lights.” “Is the door still open?” “Set the heating to 21.” “Build me a flow for the porch light at sunset.” MCP AI Bridge lets your favorite AI assistant control your Homey Pro directly, in any language you speak. It runs locally on your Homey, works with every major assistant through the open MCP standard, and you stay in full control. Over 150 actions, your own custom AI commands in the flow editor, no extra cloud. :high_voltage:


:link: Links


:sparkles: What it is

Your Homey already runs your home. Now you can just talk to it.

MCP AI Bridge turns your Homey Pro into something any modern AI assistant can talk to directly. No middleman cloud, no separate integration for each assistant. The app runs a local Model Context Protocol (MCP) server on your Homey, and any MCP-compatible AI connects straight to it over your own network.

Once it is connected you stop digging through menus and just say what you want. The assistant reads the state of your home and changes it for you, in plain language, in any language you speak to it.

MCP is the open standard that Anthropic, OpenAI and Google are building their assistants around. Because the bridge speaks that standard, it is not tied to one vendor. The same Homey endpoint works in Claude, ChatGPT, Gemini, Cursor, Windsurf and more. :globe_showing_europe_africa:

:rocket: Why this MCP app

There is more than one way to reach Homey from the outside. Here is why this one is the one to install.

  • :unlocked: Works with every assistant, not one. It speaks the open Model Context Protocol, so the same endpoint works in Claude, ChatGPT, Gemini, Cursor, Windsurf and any other MCP client. No lock-in.
  • :toolbox: Covers the whole Homey, not just switches. Over 150 actions across devices, zones, flows, advanced flows, logic, energy, insights, alarms, notifications, apps, dashboards and system.
  • :puzzle_piece: You build your own AI commands. Expose custom tools to the assistant in the flow editor and send real answers back using live device values. The AI asks, your flow replies.
  • :house: Local first. The AI connects to your Homey’s own address on your network. Nothing is routed through an extra cloud, and your home data is not handed to a third party.
  • :locked: Real security, not an afterthought. Four permission levels per key, an access switch to pause or limit everything in one click, a tool blocklist, guardrail rules, rate limits, and a security log with source IPs.
  • :see_no_evil_monkey: Each assistant only sees what it may use. The tool list is cut down to the permission of the key it connected with, so a read-only assistant is not even shown the tools it would be refused.
  • :eyes: You can see what it does. Every action is auditable, with a diagnostics tab, broken flow detection and a live connections view. Argument values are never logged.

Open standard, full coverage, your own commands, local, and locked down by choice.

:speech_balloon: Just ask

:light_bulb: Lights and climate

  • “Turn off all the lights downstairs.”
  • “Dim the living room to 30 percent.”
  • “Set the bedroom to 19 degrees tonight.”

:magnifying_glass_tilted_left: Check on things

  • “Is the garage door still open?”
  • “Which devices have a low battery?”
  • “Anything offline right now?”

:high_voltage: Energy and history

  • “What used the most power this week?”
  • “Average power use over the last 7 days?”

:hammer_and_wrench: Automations

  • “Make a flow that turns on the porch light at sunset.”
  • “Set the heating to 21 tomorrow at 8 in the morning.”
  • “Watch the front door and tell me when it opens.”

:broom: Housekeeping

  • “Did anything fail in the last hour?”
  • “Restart the Hue app.”
  • “Are any of my flows broken?”

You talk normally. The assistant works out which devices and capabilities you mean and does it.

:toolbox: What it can reach

The bridge exposes your whole Homey through one clean interface:

  • :electric_plug: Devices and every capability they have
  • :house: Zones and rooms
  • :shuffle_tracks_button: Basic flows and Advanced Flows, including creating and editing them
  • :clapper_board: Moods
  • :1234: Logic variables
  • :alarm_clock: Alarms and the alarm clock
  • :high_voltage: Energy, live and historical
  • :chart_increasing: Insights, with one-number summaries
  • :bell: Notifications
  • :bar_chart: Homey’s own dashboards, including building them
  • :package: Installed apps, including enable, disable, restart and settings
  • :busts_in_silhouette: Users and presence
  • :desktop_computer: System info, memory, storage and reboot

Over 150 actions in total, so the assistant can both understand the state of your home and act on it.

:gear: How it works

Under the hood it is a standard MCP server speaking JSON-RPC over local HTTP, using the StreamableHTTP transport from the MCP spec. Because it follows the open standard, the same endpoint works across clients.

Prefer to script against it? There is a plain REST shortcut at POST /tools/{name} and a full OpenAPI 3.1 spec at GET /openapi.json, so it drops right into tools like n8n, Make, or your own code.

Everything stays on your network. The AI connects to your Homey’s local address. No extra cloud in the path. :house:

:electric_plug: Connect your AI

Open the app settings and you get a connect wizard with a tab per client. Pick your assistant, copy the generated config, paste it in, done. The wizard fills in your Homey’s own address, adds the Authorization header when you have set an API key, and can merge the entry into a config file you already have instead of replacing it.

Every method it offers is below, so you can see what you are in for before installing.

Local clients, straight over your network

These connect from your own machine to your Homey’s address. No tunnel, no public URL, no subscription tier.

Claude Desktop: claude_desktop_config.json, via Settings, Developer, Edit Config

{
  "mcpServers": {
    "homey": {
      "type": "http",
      "url": "http://<homey-ip>:52199/mcp"
    }
  }
}

Claude Code: one command in your terminal

claude mcp add homey --transport http "http://<homey-ip>:52199/mcp"

Cursor and Windsurf: the same mcpServers block in mcp.json, then restart the editor.

Gemini: the regular Gemini app cannot add an MCP server yet, so use the CLI. Google replaced gemini-cli with antigravity-cli, and the two want a different key, which is why the wizard generates both.

Antigravity CLI, in ~/.gemini/antigravity-cli/mcp_config.json:

{
  "mcpServers": {
    "homey": {
      "serverUrl": "http://<homey-ip>:52199/mcp"
    }
  }
}

Gemini CLI (older), in ~/.gemini/settings.json:

{
  "mcpServers": {
    "homey": {
      "httpUrl": "http://<homey-ip>:52199/mcp"
    }
  }
}

GitHub Copilot in VS Code (agent mode), in .vscode/mcp.json. Note it uses servers, not mcpServers:

{
  "servers": {
    "homey": {
      "type": "http",
      "url": "http://<homey-ip>:52199/mcp"
    }
  }
}

Mistral Vibe, the CLI coding agent. Its own command writes the config for you:

vibe mcp add homey --url "http://<homey-ip>:52199/mcp" --transport streamable-http

Or put it in config.toml yourself, per project in .vibe/config.toml or for everything in ~/.vibe/config.toml. Note that this one is TOML with an array of tables, not a JSON map like the others:

[[mcp_servers]]
name = "homey"
transport = "streamable-http"
url = "http://<homey-ip>:52199/mcp"

Vibe cannot do OAuth yet, so if you reach your Homey from outside, give it your API key in a header rather than the consent page.

Older clients without native HTTP. Every local tab also has an mcp-remote variant, which needs Node.js on your computer:

{
  "mcpServers": {
    "homey": {
      "command": "npx",
      "args": ["-y", "mcp-remote", "http://<homey-ip>:52199/mcp", "--allow-http"]
    }
  }
}

With an API key set, every client sends it as a header. The wizard fills in a YOUR_API_KEY placeholder for you to replace:

"headers": { "Authorization": "Bearer YOUR_API_KEY" }

Cloud clients, which need a public address

ChatGPT and the Claude web app run on the internet, so they cannot reach a local address. For these you need your bridge publicly reachable over HTTPS, through a reverse proxy or a tunnel, plus an API key set on the Security tab. Then add a custom connector pointing at your public /mcp URL, with the key as the Authorization header.

Two things worth knowing before you go down that road:

  • ChatGPT: write-capable custom MCP connectors are limited to Business, Enterprise and Edu. Plus and Pro get read-only, which still covers the roughly 80 read tools here but no control.
  • Claude web or app: custom connectors are available on Free, Pro, Max, Team and Enterprise. Free is limited to one custom connector, which is exactly what you need here, and there is no read-only restriction.

If you do expose it, use the app’s own remote access (see below) rather than forwarding a port.

:puzzle_piece: Build your own AI commands with flow cards

This is where it gets fun. The app ships flow cards so your AI and your flows work together both ways, not just the AI reaching into Homey.

:studio_microphone: Give the AI its own command

  • Trigger: “An AI agent calls a custom tool.” The assistant calls a tool you define, your flow runs whatever logic you want, and you reply with “Return a response to the AI agent” using live device values and flow tokens. The AI asks, your flow answers.

:eyes: See and react to what the AI does

  • Trigger: “The AI controls a device.” Fires whenever the assistant changes a device, with the name, capability and value as tokens. Perfect for a quick “AI just set the thermostat to 19” notification.

:police_car_light: Catch problems early

  • Trigger: “A flow becomes broken.” Know the moment an automation breaks, for example after you removed a device.
  • Trigger: “An AI tool call fails.” Know when the assistant hits an error.
  • Trigger: “A security event occurs.” Get the source IP and reason when someone is denied or tries to connect without a key.
  • Condition: “There is a broken flow.” Use it in a daily check flow.

:brain: Give the AI memory from your flows

  • Action: “Remember a value for the AI.” A flow writes context the assistant can recall later.
  • Condition: “A remembered value is.” A flow reads that memory back.

You are not limited to the built in tools. You design your own AI commands in the flow editor.

:bullseye: Real world examples

:bar_chart: A custom AI tool that answers with live data

  1. Trigger: An AI agent calls a custom tool.
  2. Action: Return a response to the AI agent, using a flow token like the current power usage.

Now you can ask “how much power am I using right now” and the assistant gets the real number back from your flow.

:person_running: Let the AI know nobody is home

  1. When everyone leaves, your presence flow runs “Remember a value for the AI”, key nobody_home, value true.
  2. Later you ask the assistant to do something, and it can check that context first.

:wrench: Get told when a flow breaks

  1. Trigger: A flow becomes broken.
  2. Action: send yourself a notification with the flow name.

:locked: You stay in control

The bridge is local first and locked down by choice.

:key: Four permission levels, per key. Give each assistant its own API key at exactly the level it needs:

Level May do May not
Read only See everything Change anything
Control only Switch and set devices, start a flow, activate a mood, set a variable, send a notification Rename, move between zones, change settings, create, overwrite or delete anything
Control and change All of the above, plus editing flows, dashboards, moods and settings Delete devices, uninstall apps, reboot, wipe stored data
Full Everything

Control only is the one to hand a voice assistant. It operates your house and leaves the way your house is set up alone.

:green_circle: Access mode switch. One control over the whole bridge, regardless of which keys exist: full, control and change, control only, read only, or off. Pause the AI or keep it read only while you are away, in one click.

:see_no_evil_monkey: The tool list follows the key. An assistant is only shown the tools its permission actually allows, and is told which level it is on. It says “this connection is not permitted to do that” instead of pretending your Homey cannot do it, and it does not waste a turn discovering each refusal.

:shield: And the rest:

  • :prohibited: Tool blocklist. Disable specific risky tools entirely, like deleting devices or rebooting Homey, even for a full key.
  • :clipboard: Guardrail rules. Block a tool or a capability outright, optionally only in a time window or on certain days. Enforced regardless of the key.
  • :raised_hand: Confirmation on irreversible actions, on clients that can show a prompt.
  • :globe_with_meridians: IP whitelist and rate limits, with a separate tighter limit for destructive actions.
  • :memo: Security log. Every denied or unauthorized attempt is recorded with its source IP, right in the settings.
  • :satellite_antenna: Active connections view. See who is connected, from which IP, and for how long.
  • :house: The dashboard never leaves your network. It has no login of its own, so it is refused on the HTTPS port even with remote access on. Not a setting you can get wrong, it is fixed in the app. You can also switch it off on your own network entirely, which keeps everything you built.

By default, with nothing configured, it stays open on your trusted local network, the same as before. The controls are there when you want them.

:globe_showing_europe_africa: Reaching it from outside your network (optional)

Off by default, and it stays off unless you turn it on. When you do, the bridge speaks OAuth 2.1 with PKCE, so a remote client goes through a consent page where you pick that connection’s permission and enter your API key. There is a global ceiling that caps every remote connection, whatever it was granted, and connectors can be revoked from the settings. Remote access refuses to switch on at all until you have set an API key.

The Remote tab carries the full step by step: how the chain fits together, three routes from your router to the connector in Claude or ChatGPT, which port goes where, how to test it before you connect anything, and what to check when it does not work.

If you do not need it, leave it alone. Everything above works on your own network without it.

:locked_with_key: Personal Access Token (optional)

Most tools work without one. A Personal Access Token is needed for two things the app’s own API token is not allowed to do:

  • Creating, editing, deleting and starting flows. Tick the homey.flow scope when you create the token at my.homey.app, Settings, API.
  • Homey’s own dashboards. These need the Dashboard scope, and here is the catch: that scope is not offered as a checkbox on the API key screen. It is not a firmware or version thing, it simply is not in the list. The only token that carries it is one created with the top entry Homey, which grants full access to everything. Worth deciding on purpose: take it if you want your assistant to build dashboards, and leave it if you do not.

The Status tab shows what your saved token can do and which scope it is still missing.

:bar_chart: See what is happening

Twelve tabs, and each one says what it is for rather than assuming you built the thing.

Status lists every address the bridge answers on in one place, with its real state: your local address, HTTPS, the public address when remote access is on, and the dashboard. Underneath it, how many tools, connected assistants, schedules, watched devices, webhooks and keys you have.

Connection is step one, all of it about your own network: where your Homey is, the optional token for writing flows, and a tab per assistant with the config to copy.

Security has the four permission levels, the access switch, the blocklist, the guardrails and the security log, with a line on each saying what it does and how the layers stack.

Commands is seventy-eight example sentences in thirteen groups, tap to copy. Deliberately weighted towards the things people do not think to ask: have a flow explained back in plain language, find apps used in no flow at all, find devices that drew power all night.

Dashboard builds one for you in a click, lists the ones you have with which is served at /dashboard, and holds the prompt to hand your assistant if you want a custom one.

Memory, Planning and Events show what the assistant remembered, scheduled and is watching, each with example sentences to fill it, because on a fresh install they are empty and that used to look broken.

Diagnostics is every action with its result and how long it took, plus broken flow detection. Bug report makes a document you can paste into this topic, with everything needed to help and never a key or a token.

Argument values are never logged, so nothing sensitive leaks into the history.

:white_check_mark: Works with

Claude (Desktop, Code, mobile), ChatGPT, Google Gemini, GitHub Copilot, Cursor, Windsurf, Mistral Vibe, and any other MCP client. The text to speech and LED ring tools work on Homey Pro (Early 2019) and older models.

:inbox_tray: Get it

  1. :mobile_phone_with_arrow: Install from the App Store: MCP AI Bridge | Homey
  2. :test_tube: Or try the latest test build: MCP AI Bridge | Homey
  3. :gear: Open the app settings and run the connect wizard.
  4. :speech_balloon: Paste the config into your assistant and start talking to your home.

Questions, ideas and bug reports are very welcome in this topic. If the app makes your home a little easier to live with, a reply or a thumbs up helps other people find it. :raising_hands:


:scroll: Changelog

v4.10.1 Mistral Vibe in the connect wizard, asked for in the app store.
        Its config is the odd one out, TOML with an array of tables
        where every other client takes a JSON map, so the tab saves you
        the evening. Vibe cannot do OAuth yet, so from outside your
        network you give it your API key in a header.

v4.10.0 Five things the dashboard was missing. Camera tiles, proxied by
        the app so the page never holds a Homey token. Conditional cards
        and rows that appear only when a condition holds. A list of your
        dashboards in the settings, with which one is served and buttons
        to open, promote or delete. A button that creates three moods,
        all off, evening and leaving, per room that has lamps. And a
        switch to stop serving the dashboard on your network while
        keeping everything you built; the refusal from outside your
        house is not a setting and has not changed.

v4.9.2  The settings page, gone through tab by tab, because most of it
        assumed you had built the thing. Status lists every address the
        bridge answers on with its real state. Security explains the
        three settings that had none. The example commands went from
        seventeen to seventy-eight. Memory, Planning and Events say why
        they are empty and how to fill them. The remote guide grew from
        six sections to fifteen. Two fixes: checking an Advanced Flow
        reported it valid without looking at its references at all, and
        a registry the app could not read counted as an empty one, which
        reported perfectly good cards as missing.

v4.9.1  One bug report instead of two. The page and your assistant built
        different documents, each missing what the other had. Also: a
        stray POST to the dashboard is a plain not-found rather than a
        failed login in your security log, the check interval really has
        its five second minimum, the session list no longer hands out
        ids that can close somebody else's connection, a flow named
        after a built-in tool no longer replaces it, and a guardrail
        naming a capability with a capital letter now fires.

v4.9.0  A full security audit. Four ways to get more access than you
        were given are closed, including naming a script card under a
        different field name and sending a structured argument as text,
        which also walked past rules like never unlock the door. The
        dashboard is local-network-only again: 4.8.0's HTTPS port would
        have published it, and the token that operates your devices with
        it. Remote access refuses to start without an API key. A
        read-only key no longer receives a flow's card arguments or a
        device's settings, which is where scripts and passwords live.

v4.8.0  The bridge can serve HTTPS itself, so the last hop from your
        proxy stops being plain HTTP with your API key in the clear. A
        certificate that does not load costs you HTTPS, never the
        bridge. Plus a Connected apps list: every cloud assistant you
        approved, with its permission, and a way to revoke it. Replacing
        your API key revokes nothing, which is why that list exists.

v4.7.0  Your assistant is only shown the tools its key may use, and is
        told which permission it is on, so it says this connection is
        not allowed instead of pretending your Homey cannot do it. Four
        tools that answered a bare failure now name what they could not
        find. And a correction to 4.6.0: the Dashboard scope is not
        offered when you create a token, only the top-level Homey scope
        carries it.

v4.6.0  A fourth permission level, Control only. It switches devices,
        starts flows, activates moods and sends notifications, and
        changes nothing about how your Homey is set up. Meant for a
        voice assistant you want on a short leash. Pick it per key, as
        the access mode, or as the ceiling for remote connections.

v4.5.1  A refused action explains itself in a form your assistant can
        read: a code, the tool, the permission that stopped it, and
        whether retrying is pointless. And set_default false is honoured
        instead of promoting the dashboard anyway.

v4.5.0  The permission model stopped guessing categories from tool
        names, which refused a read-only key the trigger card list
        because of the word "trigger" and treated tools that erase data
        as harmless reads. Reading a HomeyScript now needs control
        access, since script source can hold your API keys.

v4.4.1  Two remote-access fixes from a Cloudflare Tunnel report. The
        consent page failed with "Origin not allowed", because the check
        that protects against hostile web pages was also applied to the
        OAuth pages, which are reached by redirect and can never match.
        And a crash when a hostname resolved to IPv6 first.

v4.4.0  The Bridge device gets its own settings: refresh interval, and
        an optional alarm for a burst of refused attempts that counts
        the last 15 minutes so it clears by itself. Off by default.

v4.3.2  A new image for the Bridge device after App Store review
        feedback, so it fits the store page it appears on.

v4.3.1  On a Homey this app cannot run on, the settings page kept
        showing "server active" and 154 tools, left behind by an older
        version. Refusing to start now clears them.

v4.3.0  Flows the AI builds run their actions at the same time instead
        of one after another, since a chained action never runs at all
        when the one before it fails. Deliberate sequences are left
        alone and the reason is shown. The Bridge device gained four
        values, and the daily counters are real: they used to be
        estimated from recent log entries and flattened out around 200.

v4.2.1  A blocked tool is no longer advertised to your assistant and
        then refused when called. Plus eight missing field labels in the
        flow editor.

v4.2.0  Confirmation for irreversible actions is ON by default. It only
        ever worked on clients that can show a prompt, and most cannot,
        so a reboot or a delete simply went through while the app
        presented confirmation as a headline feature. Access mode "off"
        also really blocks everything now, where it used to keep handing
        out device names and variable values. New: a Bridge device with
        Insights graphs and flow triggers for the bridge itself.

v4.1.1  Fixes the version on the About tab, which read 3.1.3 whatever
        you were running, and travelled into people's bug reports.

v4.1.0  A hardening pass, most of it found by attacking the app rather
        than reading it. Confirmations could be skipped by wrapping a
        call in a list or mixing protocol eras. A typo in the access
        mode, or a key with an unrecognised permission, used to grant
        FULL access. The browser-origin check compared two values the
        attacker controls. And the settings page announced success
        whether or not anything happened, including the button that
        revokes an API key.

v4.0.2  Fixes a crash loop on older Homeys: the startup error escaped
        initialisation, so Homey restarted the app over and over and
        mailed a crash report each time. It now stops once and says
        which Homey is needed.

v4.0.1  Dashboard widget URIs are checked before anything is written, so
        a typo or an uninstalled app is refused with the reason instead
        of showing up as a blank tile. Homey's own built-in widgets are
        not exposed through any API, and the tools now say so and
        describe the workaround.

v4.0.0  Rebuilt for the MCP 2026-07-28 spec while keeping every existing
        connector working. The server speaks both eras side by side.
        Confirmations now travel through the client, bound to the exact
        tool, arguments and caller, so one can never be replayed against
        another. Plus a long adversarial audit: a confirmation could be
        skipped by wrapping the call in a batch, a client could opt out
        by not declaring it could ask, browser pages elsewhere could
        reach the server through DNS rebinding, and a corrupt guardrails
        setting silently disabled every rule you had configured.

Earlier majors, in short

v3.x    Native Homey Dashboards the AI can build, broken-flow detection
        that resolves every card against the live registry instead of
        trusting Homey's own flag, and the MCP 2025-11-25 spec with
        server-initiated requests.

v2.x    The release that turned a tool list into a bridge with a memory:
        persistent memory across chats, scheduled actions, device events
        with webhooks, the interactive dashboard, camera images, and the
        whole security model the rest builds on.

v1.x    The first release, 83 tools, then renamed to MCP AI Bridge. It
        gained custom AI commands built from flow cards, optional API
        key authentication, a REST shortcut with an OpenAPI spec, IP
        whitelisting and rate limiting.

Where can I add Gemini ?

@Mark1541
A quick heads up: the regular Gemini app (gemini.google.com and the phone app) cannot add a custom MCP server. The consumer app has no custom connector feature yet. MCP works with the Gemini CLI (below), with Gemini Enterprise as a custom data store, or through the Gemini API in your own code. For most people the CLI is the way.

I have everything in working order with Gemini-cli on MacBook and the MCP-server app. I have made a personal acces token to be able to write flows to my homey. But writing doesn’t work. Gemini helped to troubleshoot but without success. I asked Gemini to make a bug report for me:

 Bug Report: Flow Write Operations Unavailable (even with Full Access PAT)

  Device: Homey Pro (2026)
  Firmware: v13.2.1
  App Version: MCP AI Bridge (latest)
  Issue: The bridge consistently reports that flow write operations are unavailable, despite using a Personal Advisory Token (PAT) with all possible scopes/permissions enabled.

  Steps taken to resolve:
   1. Created multiple new PAT tokens with "Full Access" (all scopes checked).
   2. Removed and re-added the token in the app configuration.
   3. Restarted the MCP AI Bridge app multiple times (with 10+ seconds wait time).
   4. Tested with both Basic Flows (flows_create) and Advanced Flows (advanced_flows_create).
   5. Verified the user account is the Owner/Admin of the Homey.

  Error Message received via MCP:
  Error executing tool "flows_create": Flow schrijfoperaties zijn niet beschikbaar. Controleer of het PAT token correct is ingesteld en herstart de app (Homey → MCP AI Bridge →
  Uitschakelen/Inschakelen).

  Additional Technical Observation:
  List-based tools (like flows_list, devices_list, zones_list) are also failing with a Zod-like type validation error:
  Invalid input: expected record, received array at path: ["structuredContent"]. This suggests a potential issue in how the bridge serializes data or how the schema is defined for the MCP output.

  Expected behavior:
  With a valid Admin PAT token, the bridge should allow the creation and modification of flows and correctly parse list-responses as records.

@Luke_Vredeveld

Thanks for the detailed report, it was really helpful. Both problems are fixed in v2.1.0 (now in test/review).

The list-tool error. The error expected record, received array at path ["structuredContent"] was a real bug on our side. The list tools (devices, flows, zones, etc.) returned a bare JSON array, and strict MCP clients like Gemini CLI reject that during structured-content validation, they expect an object. List results are now returned as an object, so reads work again in Gemini CLI and other strict clients.

The flow-write problem. This was not your token, and not the scopes. On recent firmware the bridge’s separate token-based session fails to initialize, which blocked all flow writes no matter how many PATs you created or which scopes you checked. Flow create and edit now go through the app’s own API (which already has the required permission), so a Personal Access Token is no longer needed at all. If the token session still cannot start, the real error is now surfaced instead of the generic message.

Update to v2.1.0 and it should just work.

While I was at it I also hardened the server for all AI clients: safe serialization (no more crashes on circular or empty results), protocol-version echoing, a session id for POST-based clients like ChatGPT and Cursor, and read-only/destructive hints on every tool.

One more thing that should make future reports easier: there is now a Bug report tab in the app settings that builds a diagnostic report with no tokens included, and you can also just ask your AI assistant to “create a bug report”. It builds the same report, saves it in the app, and returns it in the chat, so the exact diagnostics (app version, firmware, flow-write status, recent errors) are one click away.

Changelog:

v2.1.2

  • New flow cards: AI controls a device, a flow becomes broken, an AI tool fails, a security event occurs, remember a value for the AI, check a remembered value, and is any flow broken.
  • New Bug report tab in the app settings that builds a diagnostic report with no tokens, plus a system_create_bug_report tool so you can ask your AI to create one straight from the chat. Both copies stay identical.
  • Better compatibility with strict clients like Gemini CLI: list results are objects, safe serialization, protocol-version echo, a session id for POST-based clients (ChatGPT, Cursor, Windsurf), and read-only/destructive tool hints.
  • Flow create and edit no longer need a Personal Access Token; they use the app’s own API on modern firmware.
  • Fixed the app settings API so all management tabs load their data, and fixed the Generate report button.
  • Security and reliability fixes from a deep audit: destructive tools are blocked until you set an API key (with an in-app warning), the energy price tools use the correct Homey API, webhooks are no longer revealed to the AI, a memory namespace limit, a scheduler cancel fix, and corrections to the Claude Code mcp-remote command and the insights time range.

While the latest update fixed the reading of large lists (no more array errors!), I am completely blocked from creating flows.

I get ‘Missing Scopes’ every time. I’ve tried a clean reinstall and followed the release notes regarding the PAT being unnecessary, but it still won’t let me write. It seems like the new firmware

might have different requirements for the app’s internal API session.

Bug Report: Persistent "Missing Scopes" on Flow Write Operations (Homey Pro 2026)

  Environment:
   * Device: Homey Pro (2026)
   * Firmware: v13.2.1
   * App: MCP AI Bridge (latest version, post-array-fix)

  The Issue:
  Despite the latest update (which successfully fixed the "expected record, received array" error for read operations), the bridge is still unable to perform any flow write operations (flows_create or
  advanced_flows_create). Every attempt returns a "Missing Scopes" error.

  Summary of Findings:
   * Read Operations: WORKING. Tools like zones_list and devices_get_all_states return full, valid JSON structures.
   * Write Operations: FAILING. Any attempt to create a flow results in Error executing tool "flows_create": Missing Scopes.
   * Internal API Route: According to the release notes, the app should now use its own internal API permissions for flows, making a PAT unnecessary. However, on this firmware (v13.2.1), this internal
     session initialization still seems to be blocked or unauthorized.

  Steps Taken (All failed to resolve the issue):
   1. Created multiple new PAT tokens with "Full Access".
   2. Verified the user is the Homey Owner/Admin.
   3. Restarted the MCP AI Bridge app multiple times.
   4. Clean Reinstall: Fully uninstalled the app and reinstalled it from the App Store.
   5. Attempted write operations both with and without a PAT configured.

  Technical Observation:
  The bridge successfully authenticates for the general Web API (reading), but the authorization specifically for the homey.flow and homey.advancedflow scopes seems to be lost or not correctly requested
  when the bridge attempts to use the app's internal API on the newest Homey Pro 2026 firmware.

  Expected Behavior:
  The app should be able to create or edit flows using its internal permissions (as stated in the release notes) without requiring an external PAT, or it should correctly pass the PAT's scopes if one is
  provided.

@Luke_Vredeveld
Thanks again, this was a different root cause than the array fix, and your “Missing Scopes” observation pointed straight at it.

I verified it against the Homey API specification:

  • Reading flows (getFlows, getFlow, getAdvancedFlows) needs the homey.flow.readonly scope.
  • Creating or editing flows (createFlow, createAdvancedFlow, updateFlow, deleteFlow) needs the full homey.flow scope.

The app’s own API token (from the homey:manager:api permission) only carries the readonly scope, so reads work but writes return “Missing Scopes”. There is no app permission that grants the homey.flow write scope, so an app simply cannot create flows with its own token. My earlier “PAT no longer required” note was wrong, and the app-API fallback is exactly what produced the error.

On top of that, the token session that does have the right scope was connecting to a hand-built local address that fails on the Homey Pro 2026 firmware. It now uses the same local URL the app’s own API uses (homey.api.getLocalUrl()), so the token session initializes correctly.

What to do in v2.1.7 (now in test/review):

  1. Keep your Full-Access PAT in the app settings (Flow scope is the part that matters).
  2. Restart the app once (Homey, MCP AI Bridge, disable/enable).
  3. Open the Bug report tab and check the “Flow write” line: it should say “PAT session active”.
  4. flows_create and advanced_flows_create should then work.

If it still does not show an active session, generate a bug report and look at the flow_api_info patError field, it will contain the real error from the token session so we can pin down whether it is the address, the token scope, or something else. Thanks for the thorough testing.

v2.1.7

  • Flow create and edit require a Personal Access Token with the Flow scope. The app’s own API can only read flows, not write them (which caused “Missing Scopes”), and no app permission grants flow-write access.
  • The token session now uses the same local address the app API uses, so it initializes correctly on Homey Pro 2026 firmware.
  • If no token is set, you now get a clear message explaining what to do instead of a cryptic “Missing Scopes” error. The Bug report tab shows whether the token session is active.

I’ve updated the app, but there is no such information in the bug report tab visible

@Luke_Vredeveld
Thanks again for reporting this and for sticking with it. Here is a summary of everything that changed since 2.1.7, ending in the real fix.

  • 2.1.8 The Bug report now clearly states whether flow writing is enabled (active token session, the real error, or no token set) instead of a misleading label.
  • 2.1.9 The Status tab also lists recent connections, since most AI clients connect with stateless requests that hold no live session.
  • 2.2.0 to 2.2.3 First round of fixes for saving the Personal Access Token: route the save through a reliable path, rebuild and verify the flow session the moment you save (no restart), validate the token (a session is created even for a wrong token, so I added a real check), and version the settings scripts so an update always loads the fresh page instead of a cached one.
  • 2.2.4 (the actual root cause) On the Homey web app (my.homey.app) the settings page runs in a sandboxed frame, and the current web SDK there no longer exposes the methods the page was using. The fallback network request was blocked by the browser (CORS, null origin with credentials). That is why saving the token and loading the tabs silently failed in the browser, while the mobile app kept working. The page now talks to the app over the channel the web SDK does support, so saving and all tabs work in the browser again.
  • 2.2.5 Reliable feedback: once the token is stored you get an immediate confirmation, and a slow flow check can no longer flip a successful save back into a false “saving failed”. It still shows flow writing active, or the exact error if the token was rejected.
  • 2.2.6 The Connection tab now shows a masked preview of the saved token, so you can see which one is active.

What to do: update to 2.2.6. If your settings page was already open, do a hard refresh (Ctrl+Shift+R) so it loads the new version. Then on the Connection tab paste a Personal Access Token created at my.homey.app, Settings, API, with the Flow scope, and save. The page will confirm whether flow writing is active. Reading flows and everything else works without a token; only flow create/edit needs that Flow-scoped token.

Hi there, still checking out the server-app, awesome development!

I can conform the write acces works. But now, the AI can only create empty advanced flows. Once again, I asked Gemini CLI to make me a bug report.

 Bug Report: advanced_flows_create silently drops nodes/edges (while flows_create works perfectly)

  Environment:
   * Device: Homey Pro (2026)
   * Firmware: v13.2.1
   * App: MCP AI Bridge (latest version, PAT session active & working)

  The Issue:
  Following the latest updates, write permissions via the PAT session are successfully established. Standard flows (flows_create) are now created flawlessly, including all nested triggers, conditions,
  and actions. 
  However, when using advanced_flows_create, the bridge successfully creates the flow object (it appears in the Homey app with the correct name and ID), but the flow canvas is completely empty. Homey
  silently drops or rejects the entire nodes and edges payload provided in the API call.

  Steps Taken & Observations:
   1. Basic Flows (Success): Called flows_create with a cron trigger and a HomeyScript action. Result: Flow created perfectly with all cards intact.
   2. Advanced Flows (Failure): Called advanced_flows_create with a minimal payload (1 trigger node, 1 action node, 1 edge).
       * The MCP tool returns {"success": true, "flow_id": "..."}.
       * Upon fetching the created flow via advanced_flows_get, the cards object is completely empty: { ..., "cards": {} }.
   3. Formatting Attempts: Tried alternative formatting strategies for the nodes (using strict UUIDs for node IDs, verifying ownerUri and cardId mapping), but all resulted in the same silent dropping of
      cards by the Homey validation engine.

  Conclusion & Request:
  The bridge's connection and token authorization are now rock solid. The issue lies entirely in the schema translation/validation for the advanced_flows_create payload. It appears the Homey Pro 2026
  firmware requires a highly specific internal formatting or nesting structure for Advanced Flow nodes/edges that differs from how the bridge currently serializes them. 

  Could you investigate the exact payload structure expected by the homey.advancedflow endpoint on firmware v13.2+?

@Luke_Vredeveld

Fixed in v2.2.7. Thanks for the excellent bug report, your diagnosis was spot on.

You were exactly right: the bridge was passing the nodes/edges payload straight through, but Homey’s advancedflow endpoint does not use nodes/edges. It stores an advanced flow as a cards map (the same structure advanced_flows_get returns), where each card carries its ownerUri, id, type, args, and its connections as output arrays on the source card. Because the payload had no cards, Homey accepted the flow but dropped the whole graph, hence the empty canvas.

What changed in 2.2.7:

  • advanced_flows_create and advanced_flows_update now translate the node graph into Homey’s card structure: ownerUri is derived from the full card id, ports map to the right outputs (output to outputSuccess, true/false to outputTrue/outputFalse, error to outputError), any/all join nodes get their input arrays, and delay nodes keep their delay args.
  • You can also pass a native cards object (exactly what advanced_flows_get returns) for a clean round-trip: get an example, tweak it, write it back.
  • A start node type is now supported, so the AI can build advanced flows that are manually triggerable.
  • Bonus, found while testing: triggering a flow (basic or advanced) used to fail with “Missing Scopes” because it went through the app’s own API token. It now uses the token session, like the writes. Same fix applied to reading/deleting notifications and restarting apps.

Tips for building one with the AI:

  • For trigger/condition/action nodes, set uri to the FULL flowcard id, for example homey:manager:notifications:create_notification or homey:device:<deviceId>:onoff_true. The bridge derives ownerUri.
  • Edges: sourcePort is output for triggers/actions, or true/false for conditions.
  • Add a start node (no uri) if you want to trigger the flow on demand.

Verified end to end on a Homey Pro: the AI created an advanced flow (start node to a notification action), it triggered, and the notification showed up. Let me know how it works on your side.

Can confirm it can write advanced flows now! Great job done :ok_hand:

@Luke_Vredeveld
Just to show I haven’t been sitting still, here’s what landed in v2.2.8:

  • Privileged actions now fall back to the token session when the app’s own permissions come up short, so writes no longer fail with a scopes error. This covers devices, zones, variables, notifications, presence, alarms, apps and system settings.
  • Setting a capability now auto-corrects a wrong value type (for example the text true for an on/off switch) and retries, so those calls don’t fail anymore.
  • New tool devices_get_flowcards returns the trigger, condition and action cards for a single device.
  • New tool advanced_flows_validate dry-runs a flow graph and catches mistakes before the flow gets created.
  • Flow-card lists now report when results were truncated, with limit/offset paging.
  • The Connect tab automatically adds the Authorization header to the generated config when you’ve set an API key.
  • New Commands tab with example questions you can ask your AI.
  • Security: unknown discovery probes get a 404 instead of cluttering the security log, and API keys are now compared in constant time.

This response was brought to you by Gemini :smile:

Hi @weide43,

Thanks again for this awesome app!

Since Google has recently replaced gemini-cli with antigravity-cli ( antigravity / agy ), there are a few changes regarding the MCP server configuration that might be useful to update in the startup post for other Gemini users:

### 1. New Config File Location

The configuration file has moved.

Old (gemini-cli): ~/.gemini/settings.json

New (antigravity-cli): ~/.gemini/antigravity-cli/mcp_config.json

### 2. Config Key Change ( serverUrl vs httpUrl )

antigravity uses the key “serverUrl” (or “url” ) for HTTP/SSE connections instead of “httpUrl” . If you use

httpUrl , the server won’t be discovered.

### Updated Config Example for ~/.gemini/antigravity-cli/mcp_config.json :

{                                                                                                                

  "mcpServers": {                                                                                                

    "homey": {                                                                                                   

      "serverUrl": "http://<homey-ip>:52199/mcp"                                                                 

    }                                                                                                            

  }                                                                                                              

}                                                                                                                

Or with an API key/token:

{                                                                                                                

  "mcpServers": {                                                                                                

    "homey": {                                                                                                   

      "serverUrl": "http://<homey-ip>:52199/mcp",                                                                

      "headers": {                                                                                               

        "Authorization": "Bearer YOUR_API_KEY"                                                                   

      }                                                                                                          

    }                                                                                                            

  }                                                                                                              

}                                                                                                                

Once updated, restarting the CLI and running /mcp will reload the server and show the Homey tools successfully.

Hope this helps other users transitioning to the new CLI!

How to Build an Autonomous Homey Pro Telegram AI Bot on a Synology NAS

Here’s how I built a personal, autonomous Telegram Bot powered by Google’s Gemini AI, connected to my Homey Pro with MCP-server, and hosted entirely on my Synology NAS via Docker.

The Architecture

  • Homey Pro: Runs the community app MCP AI Bridge, which exposes an unauthenticated local API on port 52199 for fetching device states and setting capabilities.
  • Google Gemini (2.5 Flash): Acts as the brain. It natively understands text and voice (multimodal) and has a massive context window to ingest the entire state of the house.
  • Synology NAS: Runs the Node.js script via Docker (Container Manager) 24/7 without needing a laptop.
  • Telegram: The user interface.

Prerequisites

  1. Telegram Bot Token: Request one from @BotFather on Telegram.
  2. Google Gemini API Key: Generate a free key via Google AI Studio.
  3. Homey Pro IP: Find your Homey’s local IP (e.g., 192.168.1.xxx).

The 3 Files You Need

Create a folder on your computer (e.g., homey-telegram-bot). You only need three simple files in this folder.

1. docker-compose.yml

Lesson Learned: Synology’s Container Manager strongly prefers standard docker-compose.yml files over raw Dockerfiles. By using the official Node image and mounting our local directory, we avoid the need to build an image entirely.

version: '3'
services:
  homey-telegram-bot:
    image: node:20-alpine
    container_name: homey-telegram-bot
    restart: unless-stopped
    working_dir: /app
    volumes:
      - .:/app
    command: sh -c "npm install && npm start"

2. package.json

Lesson Learned: Always use "latest" for the @google/generative-ai SDK. Older versions (like 0.2.x) lack support for modern features like responseMimeType, which causes crash loops in the bot.

{
  "name": "homey-telegram-bot",
  "version": "1.0.0",
  "main": "index.js",
  "scripts": {
    "start": "node index.js"
  },
  "dependencies": {
    "@google/generative-ai": "latest",
    "telegraf": "latest"
  }
}

3. index.js (The Bot Logic)

Lessons Learned:

  • Markdown Stripping: Even when instructed to return raw JSON, Gemini sometimes wraps its output in Markdown blocks (json ... ), especially when processing voice input. Always strip markdown backticks before JSON.parse().
  • Speaker Playback: Models instinctively try to toggle speakers using the onoff capability. You must explicitly prompt it to use speaker_playing.
  • Volume Scaling: If you ask for “volume 2”, the AI often calculates 2 / 10 = 0.20 (20%). Since Homey uses a strict 0.0 to 1.0 scale, explicitly instruct the model to treat user numbers as percentages out of 100.
const { Telegraf } = require('telegraf');
const { GoogleGenerativeAI } = require('@google/generative-ai');

const TELEGRAM_TOKEN = 'YOUR_TELEGRAM_BOT_TOKEN_HERE';
const GEMINI_API_KEY = 'YOUR_GEMINI_API_KEY_HERE';
const HOMEY_IP = '192.168.1.XXX'; // e.g. 192.168.1.27

const bot = new Telegraf(TELEGRAM_TOKEN);
const genAI = new GoogleGenerativeAI(GEMINI_API_KEY);

const model = genAI.getGenerativeModel({
  model: 'gemini-2.5-flash',
  systemInstruction: `You are a virtual smarthome assistant.
When you receive a message (text or voice), decide if you need to control devices.
ALWAYS respond strictly with a JSON object. DO NOT return markdown.
Format:
{
  "response_text": "The answer to the user in Telegram (informal and helpful)",
  "actions": [
    {
      "url": "http://${HOMEY_IP}:52199/tools/devices_set_capability",
      "body": { "device_id": "ID", "capability": "CAPABILITY", "value": "VALUE (boolean, string, or number)" }
    }
  ]
}
Important rules:
- For volume_set, the scale is ALWAYS 0.0 to 1.0. If the user says a number (e.g., 2 or 20), divide it by 100 ("volume 2" = 0.02).
- For lights/plugs, use the capability "onoff" with true/false.
- For Sonos/Speakers to play/pause, use the capability "speaker_playing" with true/false.
- If no action is needed, leave the "actions" array empty: [].`
});

let deviceListCache = "";

// Pull live state of all devices on boot
async function updateDeviceList() {
    try {
        const res = await fetch(`http://${HOMEY_IP}:52199/tools/devices_get_all_states`, {
            method: "POST", headers: {"Content-Type": "application/json"}, body: "{}"
        });
        const data = await res.json();
        const resultObj = JSON.parse(data.result);
        
        let devicesFlat = [];
        for (const zone in resultObj) {
            devicesFlat.push(...resultObj[zone]);
        }
        
        deviceListCache = devicesFlat.map(d => {
            let caps = [];
            if (d.state) {
                for (const cap in d.state) {
                    caps.push(`${cap}: ${d.state[cap].value}`);
                }
            }
            return `Name: ${d.name}, ID: ${d.id}, Status: [${caps.join(", ")}]`;
        }).join("\n");
        console.log("Device list (with live states) loaded!");
    } catch(e) {
        console.error("Failed to load devices:", e.message);
    }
}

updateDeviceList();

// Listen to text and voice notes
bot.on(['text', 'voice'], async (ctx) => {
    try {
        await ctx.sendChatAction('typing');
        const parts = [];
        
        if (ctx.message.voice) {
            console.log("[Voice message received]");
            const fileLink = await ctx.telegram.getFileLink(ctx.message.voice.file_id);
            const audioResponse = await fetch(fileLink.href);
            const arrayBuffer = await audioResponse.arrayBuffer();
            const base64Audio = Buffer.from(arrayBuffer).toString('base64');
            
            parts.push({
                inlineData: {
                    mimeType: ctx.message.voice.mime_type || 'audio/ogg',
                    data: base64Audio
                }
            });
            parts.push({ text: `Device list:\n${deviceListCache}\n\nThe user sent this voice note. What are they asking? Output JSON:` });
        } 
        else if (ctx.message.text) {
            console.log(`Received text: "${ctx.message.text}"`);
            parts.push({ text: `Device list:\n${deviceListCache}\n\nThe user says: "${ctx.message.text}"\nOutput JSON:` });
        } else {
            return;
        }

        const result = await model.generateContent({
            contents: [{ role: 'user', parts: parts }]
        });
        
        // Strip markdown backticks to prevent JSON.parse crashes
        let rawText = result.response.text();
        rawText = rawText.replace(/```json/gi, '').replace(/```/g, '').trim();
        const responseJson = JSON.parse(rawText);
        
        // Execute Homey commands
        if (responseJson.actions && responseJson.actions.length > 0) {
            for (const action of responseJson.actions) {
                console.log("Executing Homey action:", action.body);
                await fetch(action.url, {
                    method: "POST",
                    headers: { "Content-Type": "application/json" },
                    body: JSON.stringify(action.body)
                });
            }
        }

        await ctx.reply(responseJson.response_text);

    } catch (error) {
        console.error("Error:", error);
        await ctx.reply("Sorry, something went wrong internally!");
    }
});

bot.launch().then(() => console.log('Telegram Bot running!'));
process.once('SIGINT', () => bot.stop('SIGINT'));
process.once('SIGTERM', () => bot.stop('SIGTERM'));

Deployment on Synology

Important Lesson: Do not use the “Import” button under the “Images” tab for text files. Synology expects a compressed .tar file there, not a script.

You have two options depending on your Synology version:

Option A: Using Container Manager (DSM 7.2+)

  1. Copy the folder containing these three files to your Synology NAS (e.g., into your /docker shared folder).
  2. Open Container Manager.
  3. Go to ProjectsCreate.
  4. Name it homey-bot, set the Path to your copied folder, and follow the wizard. It will automatically read the docker-compose.yml.

Option B: Using the older Docker GUI (DSM 6 / 7.1)
If you don’t have “Projects” or the YAML file throws an invalid format error:

  1. Delete the docker-compose.yml (you only need index.js and package.json).
  2. Go to Registry, search for node, and download the 20-alpine or latest tag.
  3. Go to Images, select the Node image, and click Launch/Run.
  4. Volume Settings: Mount your NAS folder /docker/homey-telegram-bot to the path /app inside the container.
  5. Environment/Command: In the Execution Command field, type: sh -c "cd /app && npm install && npm start".
  6. Start the container.

Enjoy your entirely local, highly autonomous AI smarthome butler!

Which do you actually prefer, Gemini Claude ChatGPT? Which works better, or is there not much difference in practice?

@weide43 Io now see me Gemini Clio at connections but if I ask something via Gemini nothing happens what am I doing wrong

Can you ask telegram for making flows or whatever

no, with the telegram bot you cannot make flows. It can tell you the status of your devices and change them.

Did you install antigravity instead of gemini-cli? See my post