Openhab MCP Server

Hi everyone,

I was planning to try to create a MCP Server to interface my OH instance to an LLM mainly to find out what it could do. Fortunately I found this little project here before I actually started implementing something myself:

It took me a while to set it up the way I was intending to use it (switch to http-transport and integrate it in Windsurf IDE/Cascade AI) but I must say I’m really impressed and pleased with what it can do. If you are interested I’m happy to do a write-up of what I needed to do to get this going in the end.

To learn more about this I also created a fork and added the functionality to query persistence data which gives a lot more fun use cases. What I tried/did so far:

  1. I don’t have any descriptions for my OH rules yet: Cascade is able to analyze my rules and update the rules with meaningful descriptions.
  2. Analyzing of persistence data: For example getting the highest/lowest or average temperature of an item within a given time frame works quite nicely.
  3. Get more complex information about the whole system: Which room is currently the warmest? The LLM did a mistake there and did not actually put the highest temperature on the list but you can get some insights that would take otherwise a lot of work to put together.
  4. Analyze naming convention breaches of my items and give me hints where my items seem to be diverging from otherwise similar configurations.
  5. Create simple rules: Switch on the bedroom light when I switch on the hallway light. It did generate Rule DSL script but it was working perfectly. It made sure to find the correct items first with my help (wasn’t able to find it by itself).
  6. Analyse my rules to find the ones with the highest code complexity.

Ultimately there are so many more options with further extensions of the MCP server. For example I think this could be used to do some time consuming work after replacing equipment to link the items to the new thing or to create new things and connected items when you add another device of a type that is already in your setup or to add a new custom metadata field to a lot of different items or rename items (aka delete, recreate and re-link)…

5 Likes

I would greatly appreciate tou writing a comprehensive write-up of your setup.

For now I do not have the resources to run a local LLM but concepts like MCP will change everything in the near future.

So knowing that OpenHAB will be ready for the next quantum leap i so good to know.

1 Like

Actually this might also replace rules if you have an agent running that can decide for himself when there is a good time to check some parameters. I have a rule that calculates whether it is good time to ventilate a room to reduce humidity indoors. You can do the same with an AI agent:

Question:
Would it be a good time to ventilate the bathroom to reduce indoor humidity based on the temperature, humidity and dewpoint values you can obtain from openhab? You can use the carport values as a reference for outside measurements

Answer:
<AI gathers all required information>

Here's what I found:

Outside (Carport):

* Temperature: 12.7°C
* Dewpoint: 4.4°C

Bathroom:

* Temperature: 23.3°C
* Dewpoint: 10°C

Based on these values, here's my analysis:

1. The bathroom temperature is significantly higher than the outside temperature (23.3°C vs 12.7°C)
2. The bathroom dewpoint is higher than the outside dewpoint (10°C vs 4.4°C)
3. The temperature difference between inside and outside is 10.6°C, which is quite significant

Recommendation: Yes, it would be a good time to ventilate the bathroom because:

1. The outside air is significantly cooler than the inside air, which will help cool down the bathroom
2. The outside dewpoint is lower than the inside dewpoint, which means bringing in outside air will actually reduce humidity levels
3. The significant temperature difference (10.6°C) means that ventilation will be effective in both cooling and dehumidifying

The ventilation will work because:

* The cooler outside air will help reduce the temperature in the bathroom
* The lower dewpoint of the outside air means that when it mixes with the warmer inside air, it will actually reduce the relative humidity
* The temperature difference is large enough to create a good air exchange

I recommend ventilating the bathroom for about 15-20 minutes to achieve good air exchange. After that, you might want to close the windows to maintain the temperature inside.

Sorry that this is German but this is a scenario I was hoping for:

  1. AI creates a new Semantic Tag for me
  2. Finds matching groups that should be updated with the new tag
  3. Updates the tag
  4. (Forgets to remove the general Equipment Tags but that’s done with another small hint)
  5. EDIT: Ok, and it switched the item type from Group to String :smiley: But it could also fix that pretty fast. And is was actually a coding issue, not the AI that messed up…

Me: Kannst du bitte ein neues Equipment Tag für Access Points anlegen?

AI Output

Ich werde jetzt ein neues Equipment Tag für Access Points erstellen:

MCP Tool: openhab / create_tag

Ran with these arguments:

{
  "tag": {
    "uid": "Equipment_AccessPoint",
    "name": "AccessPoint",
    "label": "Access Point",
    "description": "WLAN Access Points für die Netzwerkkonnektivität",
    "synonyms": [
      "WLAN",
      "Wireless Access Point",
      "WiFi Access Point"
    ],
    "editable": false
  }
}

Output

{
  "uid": "Equipment_AccessPoint",
  "name": "AccessPoint",
  "label": "Access Point",
  "description": "WLAN Access Points für die Netzwerkkonnektivität",
  "synonyms": [
    "WLAN",
    "Wireless Access Point",
    "WiFi Access Point"
  ],
  "editable": true
}

Perfekt! Das neue Tag wurde erfolgreich erstellt. Lassen Sie uns es jetzt den Access Points zuordnen. Zuerst aktualisieren wir

ap_diningroom

MCP Tool: openhab / update_item

Ran with these arguments:

{
  "item_name": "ap_diningroom",
  "item": {
    "name": "ap_diningroom",
    "tags": [
      "Equipment",
      "Equipment_AccessPoint"
    ]
  }
}

Output

{
  "type": "String",
  "name": "ap_diningroom",
  "state": "NULL",
  "label": "Access Point Erdgeschoss",
  "tags": [
    "Equipment",
    "Equipment_AccessPoint"
  ],
  "groupNames": [
    "loc_diningroom",
    "aps"
  ]
}

Lassen Sie uns jetzt auch die anderen Access Points aktualisieren:

MCP Tool: openhab / update_item

Ran with these arguments:

{
  "item_name": "ap_garden",
  "item": {
    "name": "ap_garden",
    "tags": [
      "Equipment",
      "Equipment_AccessPoint"
    ]
  }
}

Output

{
  "type": "String",
  "name": "ap_garden",
  "state": "NULL",
  "label": "Access Point Garten",
  "tags": [
    "Equipment",
    "Equipment_AccessPoint"
  ],
  "groupNames": [
    "loc_garden",
    "aps"
  ]
}

MCP Tool: openhab / update_item

Ran with these arguments:

{
  "item_name": "ap_office",
  "item": {
    "name": "ap_office",
    "tags": [
      "Equipment",
      "Equipment_AccessPoint"
    ]
  }
}

Output

{
  "type": "String",
  "name": "ap_office",
  "state": "NULL",
  "label": "Access Point Dachgeschoss",
  "tags": [
    "Equipment",
    "Equipment_AccessPoint"
  ],
  "groupNames": [
    "loc_office",
    "aps"
  ]
}

MCP Tool: openhab / update_item

Ran with these arguments:

{
  "item_name": "ap_storageroom",
  "item": {
    "name": "ap_storageroom",
    "tags": [
      "Equipment",
      "Equipment_AccessPoint"
    ]
  }
}

Output

{
  "type": "String",
  "name": "ap_storageroom",
  "state": "NULL",
  "label": "Access Point Keller",
  "tags": [
    "Equipment",
    "Equipment_AccessPoint"
  ],
  "groupNames": [
    "loc_storageroom",
    "aps"
  ]
}

Perfekt! Alle Access Points wurden erfolgreich mit dem neuen Tag “Equipment_AccessPoint” versehen. Die Struktur ist jetzt:

  1. Neues Equipment Tag:
  • UID: Equipment_AccessPoint

  • Label: Access Point

  • Synonyms: WLAN, Wireless Access Point, WiFi Access Point

  1. Aktualisierte Access Points:
    ap_diningroom (Erdgeschoss)
    ap_garden (Garten)
    ap_office (Dachgeschoss)
    ap_storageroom (Keller)

Alle Access Points haben jetzt sowohl das generelle

Equipment

Tag als auch das spezifische

Equipment_AccessPoint

Tag. Möchten Sie noch weitere Anpassungen vornehmen?

OMG - My AI Buddy just optimized a rule with me. After that he asked if we want to test the rule. I agreed and so he created a test plan for every possible state combination that would affect the outcome of the rule.

But the best of all: He can execute the whole test plan by himself without me needing to open or close any windows. He just updates the states accordingly and checks whether other items show the expected changes :smiley:

Thank you very much for the inspiration @DrRSatzteil! After 4h of try & error I was finally able to connect my openhab system with Claude Desktop under Windows. It’s amazing what improvement an LLM is compared to Alexa. Can’t wait until Claude Desktop supports TTS and voice recognition. :smiley:
Things I learned:

  • Docker Desktop needs to have the “MCP Toolkit” extension installed and in this extension it needs to have Claude Desktop enabled as MCP Client
  • the claude_desktop_config.json is wrong in the github page and must be as follows:
{
	"mcpServers": {
		"MCP_DOCKER": {
			"name": "openhab-mcp",
			"command": "docker",
			"args": [
				"run",
				"--rm",
				"-i",
				"-p",
				"8081:8080",
				"-e",
				"OPENHAB_URL=http://yourURLorIP",
				"-e",
				"OPENHAB_API_TOKEN=oh.MCP.yourToken",
				"--name",
				"openhab-mcp",
				"openhab-mcp"
			]
		}
	}
}

Hope this helps others to shorten the time to get it up & running :wink:

My current struggle is, that the amount of items in my openhab system is too big, so that Claude Desktop (free plan) often tells me, that I’ve reached the conversation limit. :sweat_smile:

I also ran into some problems but more related to the communication between my MCP Server and Client. I therefore added some pagination in the list tools which can reduce the amount of data queried in one go.

If you want you can check out my repo here:

One thing you might want to change back is the transport type. Just change the last line in the openhab_mcp_server.py from mcp.run(transport=“streamable-http”) to mcp.run().

For others who also want to run the MCP Server on a different machine you can leave it as it is.

Besides of adding pagination for list_items and list_things I also did some other changes:

  • Added tools to get persistence data for items
  • Added tools to handle tags (create, change, delete)
  • Removed item details when you use the list_items tool. I figured out that I still need to rename the tool get_item tool because the LLM does not realize that it needs to use this tool to get details for an item and will only get a summary from the list_items tool. So I will change that when I find the time
  • Other minor fixes/improvements

I also got the impression that the AI could handle the pagination better when the page index would start from 1, so I will probably change this in the future too.

As I am lazy I haven’t setup a local pipeline to get the new code on my server but used Github for that (you can see that in my commit history). So don’t be surprised to get lots of changes there every now and again or if the state is broken for a while.

I use my implementation of the server in the Windsurf IDE (https://windsurf.com). Add this to your mcp_config.json to connect to the MCP Server:

{
    "mcpServers": {
      "openhab": {
        "command": "npx",
        "args": [
          "mcp-remote",
          "http://<IP>:8081/mcp",
          "--allow-http"
        ]
      }
    }
}

Make sure to install npx first. The --allow-http flag is only needed if you don’t have a https connection. Port needs to match the port you used in your docker port mapping. Also you need to map the port to 8000 in the container (documentation in the project says 8080). I guess that is the default port for the streamable-http transport. At least that works for me.

1 Like

And one more thing: make sure to set an environment variable FASTMCP_HOST to 0.0.0.0 or else you won’t be able to route your requests to the server in the container. By default the http server only binds to 127.0.0.1. This wasn’t necessary the last time I was playing around with it and it was giving me headaches yesterday…

I also found the time to cleanup my code yesterday and to test all the tools. They should all be working right now.

I can approve that it’s working. Used your repo since the beginning of my experiments. After creating OH4 rules out of some NodeRed flows (where I was too lazy to create it in OH) with the help of my AI buddy, I pushed the limits further. I was able to automatically create Grafana dashboard (with corresponding MCP server), which shows the power consumption of my 3d printer. It looks really great and provides everything what I need. Awsome!! Just with a small prompt:
Create a Grafana dashboard that shows me the power consumption of the item “Socket_3D_Printer_Power_Consumption”
Even giving the name of the item would have not been required, but I wanted to avoid Claude to list items, because this always pushes me in the throtteling :wink:
Amazing…I’m feeling like a child exploring the world.

1 Like

I added the ability to add links between items and channels.

I just successfully created a new thing, new items, channel links and a rule via the LLM by referencing exiting things, items and rules as reference :star_struck:

1 Like

Added some more changes this evening:

  • Create, update or remove item metadata
  • CommandDescriptions (not yet tested)
  • StateDescriptions (not yet tested)
  • Give a meaningful error message to the LLM when it hallucinates its own configurations that are not part of the model

Oh I just realise: updating an item with command and state description and the other additional item field will definitely not work right now. I will provide an update for that soon

EDIT: Should work now. Rule update still seems to fail. I still need to have a closer look at that one.