openHAB Semantic MCP Server

Hi everyone,

by working on the MCP server you can find here: Openhab MCP Server , I learned a bit about what works well and what works not so well in terms of LLM ↔ MCP Interface. The implementation offers a lot of features but all in all its more of a wrapper of the REST API which does not work well for quite a few use cases. For example if you tell the LLM to switch off all the lights in the top floor it starts collecting items like crazy to find out where it can get information about rooms and floors and lights and the like, forgetting half of what it has learned on its way and in the end (if you’re lucky) it finds a few lights and switches them off. More often you get a timeout or max. iteration warning from your agent and nothing happens at all, especially when questions get even more complex.

So I thought about a different approach, this time with only the end user in mind (the old implementation is basically a complete admin toolkit). The most important thing for me was to create a tool interface that is easy to understand for an LLM and can be used very quickly without using a lot of iterations to get it right.

So, I finally came up with the openHAB Semantic MCP Server:

As the name suggests, the new implementation makes strong use of the semantic model and can only be used to control devices that are properly modeled there. Items tagged with the generic “Point” or “Equipment” tags also cannot be reliably controlled but their use is discouraged anyway as they do not provide a lot of additional semantic value.

I’m pretty sure that there will be bugs, so I’m more than happy to get some feedback from you. But for the use case it is made for, namely to get any information about your home or send commands/updates to devices it delivers quite impressive results compared to the other implementation in terms of speed and reliability. Feel free to give it a try and let me know if/how it works for you!

The cool thing is, that if something is not working as expected it most likely can be fixed by correcting the semantic model representation in OH. I already discovered many shortcomings in my model and it just works nicely once these have been corrected.

The server caches all item information locally, state changes are retrieved live via the openHAB SSE interface so that state information is always up to date. However to also pick up static changes the inventory is refreshed periodically (default 60 minutes but can be adjusted).

Make it clear to your LLM that it is mandatory to retrieve the possible filter values from the mcp server first and don’t just guess room names and the like. The server will return a meaningful error message when it tries to do so anyway. Here is an example system message (I haven’t played around with it much, but it seems to work ok):

You are a home automation control agent.

SOURCE OF TRUTH:
- The server inventory is the ONLY source of truth.
- You MUST NOT assume, guess, translate, or invent locations, rooms, device types, or item names.
- Human-friendly names (e.g. "Erdgeschoss", "Salon") MUST NOT be used unless they were returned by the server tools.

DISCOVERY RULES:
- Before referencing a location, device type, or point type for the first time in a session, 
  you MUST retrieve the available options from the server.
- If a user mentions a term that has not been returned by the server, you MUST perform discovery.

TOOL USAGE:
- All tool calls MUST strictly follow the provided JSON schema.
- All selections MUST use values exactly as returned by the server tools.
- Free-text values are NOT allowed in selections.

ERROR HANDLING:
- If a tool returns an error indicating an unknown or invalid value, you MUST correct the input and retry.
- You MUST NOT continue with empty or invalid selections.

ITEM NAME RULES:
- You MUST NOT display item names by default to the user.
- Item names may only be used internally for refinement if there is ambiguity.
- Only present item names to the user when explicitly requested or when a refinement is necessary to execute a command.

GENERAL:
- When in doubt, retrieve inventory information instead of guessing.

Turns out that the openAI bots tend to be extremely careful with a system message as the one above. They don’t do anything without getting a fresh view on the inventory over and over again. So here is a different approach that works very nicely and with a cute personality when used with gpt4-mini:

You are a home automation control agent with the personality of “Dobby”, the helpful house-elf.

PERSONALITY:
- Friendly, loyal, and eager to help.
- Short, warm, slightly playful sentences.
- May refer to yourself as “Dobby” (e.g., “Dobby hilft gern.”).
- Always polite, calm, and clear.

TASK:
- Help users control or query their smart home.
- Use only the provided MCP tools for any smart home action or data.
- Do NOT guess, invent, or translate locations, device types, or item names.
- Use only exact values returned by the tools.

FRIENDLY DISPLAY NAMES:
- Internal canonical inventory (e.g., LivingRoom, Bedroom, FirstFloor) MUST be used exactly for all tool calls.
- When presenting information to the user, Dobby MUST convert these internal names into human-friendly names in the user's language (e.g., LivingRoom → Wohnzimmer, Bedroom → Schlafzimmer).
- Friendly names are only for display; do NOT modify the internal names used in tool calls.
- Maintain Dobby’s personality and short, warm, playful sentences when reporting information.

DISCOVERY & CACHE:
- Retrieve inventory only when a term is not already known.
- Reuse previously retrieved data for the entire session.
- Avoid repeated discovery for the same category unless explicitly requested or server reports stale data.

TOOL USAGE:
- Follow the provided JSON schema strictly.
- No free-text values for tool calls.
- Correct invalid inputs and retry once if a tool returns an error.

COMMUNICATION:
- Do not display internal item names unless explicitly needed.
- Explain errors politely and ask for clarification if needed.
- Avoid unnecessary questions.

I’ve just pushed a major update to the openHAB Semantic MCP Server. The biggest addition is a new item monitoring system — you can now ask your LLM to watch specific items for state changes, either as a one-shot check or over a time window. When a trigger fires, a webhook notifies your LLM so it can react. I wanted the agent to become a bit more proactive while we are still keeping it deterministic. It basically just gives your agent the ability to accomplish actions in the future like: “When I leave the house within the next three hours, please switch off all the lights in house in case I forgot any”

What’s new:

  • Monitoring tasks — Create time-based monitoring tasks for any item, filtered by location, equipment, or semantic properties. Supports one-shot and time-window modes.
  • CalDAV backend — Monitoring tasks can sync bidirectionally with a CalDAV calendar (e.g. Nextcloud), so you can see and manage them alongside your regular calendar.
  • Pluggable storage — Choose between in-memory, file-based, or CalDAV storage for monitoring tasks depending on your setup or create your own backend.
  • Timezone-aware — All monitoring operations are fully timezone-aware, with your configured timezone automatically included in tool descriptions so the LLM gets it right.
  • Cleaner architecture — The MCP server has been significantly refactored under the hood for better maintainability and lots of bugs were fixed.

I also have a complete n8n agent workflow for a Nextcloud chat bot that interacts with the MCP server including the monitoring subsystem. If you are interested I can share that as well.

Feedback and contributions are very welcome :slight_smile:

Very cool, thanks for this! It works but seems to have a cap of 100 items. I have more than that. Is there support for pagination or some other way to get the full list?

Can you elaborate more what you are trying to achieve?

There is no hard limit on items in general, I use it with roughly 400 semantic items. All items that are not part of the semantic model (or only use the generic Equipment/Point tags) cannot be queried with this server implementation right now though.

The only actual limits are applied for large item operations. I was thinking about making the threshold configurable but I think it is 20 items now to prevent accidental commands or updates on a large number of items.

The whole idea of the system is that usually you do not want to interact will all of your items at once but for example only with all light switches or all temperature sensors. The first MCP implementation I linked in the first post is able to retrieve all items as a list but in my experience this is just too much information for the LLM to digest (a few 1000 items is perfectly normal for an openHAB installation) even with pagination support.

So the idea of this version is that you use semantic types and properties as early as possible in the process to reduce the number of items right in the first step to the few relevant items required for the desired operation: e.g. „Switch off all the ground floor lighting“ should only select lighting items that can receive an OFF command in the ground floor location. With this approach you should not need to query all items at once but you can query a combination of equipment, location, property, item type to reduce the list to the required minimum. In the context of LLMs this also helps to save tokens and thus money.

Ok, it might be that I only have 100 items with semantic types then.

I guess I could add a config option to load all items from openHAB. However the query functionality would be restricted to item names and types then. Also it would mean that these items would always be filtered when any of the semantic search filters are used. In general the big benefit of LLMs, being able to interact with them in natural language, would be counteracted in a way if you are required to use technical item names. But if this still sounds interesting to you I think it shouldn’t be too much effort to add this (experimental) option.

Why experimental?
In contrast to classical software development as I used to know it, implementing an MCP Server also involves quite a bit of prompting here and there. The tools for example recommend in their descriptions to only use item names when this is required to resolve ambiguity. Also error messages here and there recommend the use of the correct semantic properties. So even when this would be technically possible it may be that LLMs let themselves guide by these hints here and there (as intended) and it might not work out for you the way you would be expecting.