Skip to main content
Glama

dirigera-mcp

An MCP server that lets an MCP client (Claude Code) control IKEA TRÅDFRI smart plugs ("strömbrytare") paired to a DIRIGERA hub.

It talks to the hub over its local REST API (HTTPS on port 8443, bearer-token auth) using the dirigera library. Nothing goes through IKEA's cloud.

Transports

stdio, or streamable-http for sharing one hub connection across devcontainers

SDK

official Python MCP SDK (mcp 2.0.0, MCPServer)

Hub client

dirigera 1.2.7

Server

src/dirigera_mcp/server.py

Pairing helper

scripts/get_token.py


Setup

The hub issues a long-lived access token only after someone physically presses its action button. That token then has to reach the container, and it does so from your host environment via devcontainer remoteEnv — it is never committed, never written to a file in this repo, and never logged.

Do these three steps in order.

1. Generate the token

In a terminal inside the container:

python scripts/get_token.py

The script prints the hub address, then tells you to press the action button (the small recessed button on the underside of the hub). Press it, come back, hit ENTER, and the token is printed to stdout. You have about 60 seconds.

If you are driving this non-interactively, use the countdown mode instead — no ENTER needed:

python scripts/get_token.py --wait 30

The address comes from $DIRIGERA_HUB_IP; pass --ip <address> if it is not set yet.

The token is printed once and is not saved anywhere. Copy it now. If you lose it, just run the script again — pairing again is harmless and does not invalidate other tokens.

Prefer running this in your own VS Code terminal rather than having Claude run it, so the credential does not end up in a chat transcript.

2. Put it in your HOST user environment

Not in the container, and not in a .env file — the whole point of the setup is that the credential lives on the host and is injected at runtime.

On Windows, in a PowerShell window on the host:

[Environment]::SetEnvironmentVariable("DIRIGERA_TOKEN", "<paste-the-token>", "User")
[Environment]::SetEnvironmentVariable("DIRIGERA_HUB_IP", "<your-hub-ip>", "User")

On macOS/Linux hosts, put the two export lines in your shell profile (~/.zshrc, ~/.bashrc) and make sure VS Code is launched from a shell that has sourced it.

DIRIGERA_HUB_IP is required. It has no default in the source: a baked-in fallback would be one specific person's LAN address. It is also what the sandbox firewall opens its single exception for, so the same value drives both.

3. Restart VS Code and reopen the container

First confirm the values really persisted. In PowerShell — this reads the stored User-scope value directly, so it works even in the window that just set it:

[Environment]::GetEnvironmentVariable("DIRIGERA_TOKEN", "User").Length   # expect ~400, not 0
[Environment]::GetEnvironmentVariable("DIRIGERA_HUB_IP", "User")         # expect your hub address

Then restart VS Code. A Windows process inherits its environment at launch and never sees User variables created afterwards, so a window reload — and even a container rebuild — is not enough: the value comes from the running VS Code process, not from the container.

  1. Quit every VS Code instance, including windows holding unrelated projects. VS Code runs one shared main process on Windows and new windows inherit its environment, so a single surviving window is enough to keep the stale environment alive. code . from a fresh shell does not help either — it just signals the existing instance.

  2. Confirm nothing survived: Get-Process code -ErrorAction SilentlyContinue should print nothing. If processes linger without visible windows, ... | Stop-Process (save your work first).

  3. Start VS Code again, open this folder, and Reopen in Container.

No rebuild is needed. Closing the last window stops the container (shutdownAction defaults to stopContainer), and the next attach re-reads remoteEnv.

Verify inside the container, in a bash terminal (this is bash syntax; it silently prints empty values if you run it in PowerShell):

echo "hub=$DIRIGERA_HUB_IP token_length=${#DIRIGERA_TOKEN}"

A non-zero token_length means the token arrived. Then restart Claude Code so it picks up the dirigera server from .mcp.json, and ask it to list your outlets.

Troubleshooting

The variables exist in the container but are empty (env | grep DIRIGERA shows both names with no values). remoteEnv is working; ${localEnv:...} resolved to nothing. That means VS Code was started before the host variables were created — quit it fully and relaunch, as above.

Still empty after a full restart. Check whether VS Code is reading the Windows environment at all: if /home/vscode/.gitconfig-host exists and is non-empty, ${localEnv:USERPROFILE} resolved, so the mechanism works and only the DIRIGERA values are missing. If that file is missing, VS Code is resolving localEnv somewhere else — typically because the window was opened through Remote-WSL, where Windows User variables are not visible unless forwarded via WSLENV. Set the two variables inside WSL instead, or open the folder as a Windows path.

The tools work but the plug does not switch. Check is_reachable in the result. The hub accepts writes for offline devices, so the server flags this with a warning field rather than reporting a state it cannot confirm.


Related MCP server: Power Switch Pro MCP Server

MCP tools

Tool

Arguments

Returns

list_outlets

every outlet: id, name, room, is_on, is_reachable

get_outlet

outlet_id

current state of one outlet, read fresh from the hub

set_outlet

outlet_id, on (bool)

new state, plus previous_is_on

toggle_outlet

outlet_id

new state, plus previous_is_on

power_cycle

outlet_id, off_seconds (0.5–300, default 5)

state after power is restored

power_cycle switches the outlet off, waits, and switches it back on — for rebooting hardware such as a development board. Power is restored on every path out of the wait, and the call refuses outright on an unreachable outlet rather than risk cutting power it cannot restore. The call blocks for off_seconds, so keep it well inside the client's tool-call timeout.

set_outlet and toggle_outlet re-read the outlet from the hub after writing, so the state they report is the hub's, not an optimistic guess. If the hub reports the plug as unreachable, the result carries a warning field — the hub accepts writes for offline devices, so a successful call is not by itself proof that the plug switched.

Every failure comes back as a plain sentence, not a stack trace:

Situation

What the client sees

DIRIGERA_TOKEN empty

"DIRIGERA_TOKEN is not set in this container's environment…" + how to fix

Token wrong/expired (401/403)

"The hub rejected the token…" + how to regenerate

Hub off or wrong IP

"Cannot reach the DIRIGERA hub at <ip>:8443…"

Unknown outlet id

"Device id not found. Call list_outlets to get the ids…"

Id belongs to a lamp, not a plug

"Device is not an outlet. Call list_outlets…"


Registration

The server is registered in the repo's .mcp.json alongside the other project servers:

"dirigera": {
    "type": "stdio",
    "command": "/workspaces/ikea-mcp/.venv/bin/python",
    "args": ["/workspaces/ikea-mcp/src/dirigera_mcp/server.py"],
    "env": {
        "DIRIGERA_HUB_IP": "${DIRIGERA_HUB_IP:-}",
        "DIRIGERA_TOKEN": "${DIRIGERA_TOKEN:-}"
    }
}

Absolute paths, because the client chooses the working directory. The :- defaults let the server start even with the variables unset, so an unconfigured setup produces a helpful tool error instead of a server that fails to launch.

The server is a single module run directly by the venv interpreter — the project is not installed as a package, so there is no build step and uv sync is all that is needed.


Sharing one server with other devcontainers

An MCP stdio server is spawned as a child process by its client, so Claude Code running inside devcontainer X always starts the server inside X. Installing it on the host does nothing for a containerised client. To use one hub connection from several projects, run the server over HTTP.

The payoff: the hub token and the firewall's LAN exception exist in exactly one place. Project containers get neither — they only reach host.docker.internal.

Run it on the host

docker compose from deploy/, with both secrets in the shell's environment:

$env:DIRIGERA_MCP_KEY = python -c "import secrets; print(secrets.token_urlsafe(32))"
[Environment]::SetEnvironmentVariable("DIRIGERA_MCP_KEY", $env:DIRIGERA_MCP_KEY, "User")
cd deploy
docker compose up -d --build

DIRIGERA_MCP_KEY is the shared secret between the server and every client. Without it the server refuses to start over HTTP — an unauthenticated endpoint here can cut power to whatever is plugged in.

To run the published image instead of building locally — no clone needed on the host:

docker run -d --name dirigera-mcp --restart unless-stopped `
  -p 127.0.0.1:8765:8765 `
  -e DIRIGERA_HUB_IP -e DIRIGERA_TOKEN -e DIRIGERA_MCP_KEY `
  ghcr.io/david-s-svedberg/ikea-mcp:latest

.github/workflows/publish-image.yml builds and publishes that image on every push to main, using the workflow's built-in GITHUB_TOKEN. The image holds no credentials; both secrets arrive as runtime environment.

Point a project at it

In that project's .mcp.json:

"dirigera": {
    "type": "http",
    "url": "http://host.docker.internal:8765/mcp",
    "headers": { "Authorization": "Bearer ${DIRIGERA_MCP_KEY}" }
}

The project's devcontainer needs DIRIGERA_MCP_KEY in remoteEnv (same host-variable flow as the token above) and must allow egress to host.docker.internal. Under the firewall pattern in .devcontainer/init-firewall.sh that is already covered by the 192.168.65.0/24 carve-out for Docker Desktop's internal services. No LAN exception is needed.

Security properties

Check

Behaviour

Missing or wrong Authorization

401, constant-time comparison so the secret does not leak byte by byte

Forged Host header

421, DNS-rebinding protection with an explicit allow-list

Published port

host loopback only, so the endpoint is not on the LAN

Image contents

no credentials; both secrets arrive as runtime environment

Verified locally over HTTP: 401 without and with a wrong secret, 421 on a forged Host header, and a working session against the real hub with the correct one.

Loopback publishing is enough — verified on Docker Desktop for Windows. With the port published as 127.0.0.1:8765:8765, a devcontainer resolves host.docker.internal to the Docker Desktop gateway (192.168.65.254), which proxies through to the host's loopback and reaches the server. The port stays off the LAN while remaining reachable from containers.

If a different Docker setup cannot do that, publish on all interfaces ("8765:8765") and block the port from outside in the host firewall, or put the server and the project container on a shared user-defined Docker network and address it by container name.

Sandbox notes

This devcontainer is hardened (see .devcontainer/init-firewall.sh): egress to RFC1918 space is rejected, with exactly one exception for $DIRIGERA_HUB_IP:8443.

No LAN address is committed anywhere in this repo. The firewall script takes the hub address as an argument, which devcontainer.json fills from remoteEnv, and the Python side reads the same variable. Renumbering the hub therefore means changing one value in your host environment. If DIRIGERA_HUB_IP is unset the script falls back to RFC 5737 documentation space, so it fails closed: the exception points at an address nothing answers on, and the real hub stays blocked.

Interactive sudo is disabled. System packages belong in .devcontainer/Dockerfile, followed by a container rebuild.

The hub serves a self-signed certificate, so the dirigera library disables TLS verification. That is the library's own behaviour and acceptable here: the connection is to a single pinned LAN IP that the firewall restricts to one port.

Development

uv sync                       # install locked deps into .venv
ruff check src scripts        # lint

Available Tools

5 tools
get_outletA

Get the current state of a single outlet, freshly read from the hub.

Args: outlet_id: The outlet's id, as returned by list_outlets.

ParametersJSON Schema
NameRequiredDescriptionDefault
outlet_idYes

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.2/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden of behavioral disclosure. It does add a useful behavioral detail: 'freshly read from the hub' indicates the data is not cached. However, it does not explicitly state that this is a read-only operation, nor does it describe error handling or what happens if the outlet does not exist. For a simple getter, this is adequate but not fully transparent.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is extremely concise: two sentences plus a focused Args block. Every element earns its place—the first sentence states purpose, the second adds a key behavioral nuance, and the Args block adds necessary parameter context. No superfluous words.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple one-parameter getter, the description is largely complete. It clearly defines the tool's purpose and parameter. The output schema (though not shown) likely covers return format, so the description need not explain that. The main gap is the lack of error/edge-case behavior, but given the low complexity, the description is sufficient to select and invoke correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema has 0% description coverage, so the description must compensate. It does so effectively for the only parameter, outlet_id, by specifying that it is 'as returned by list_outlets.' This adds provenance and usage context beyond the schema's generic string type, significantly aiding correct invocation.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's function: 'Get the current state of a single outlet, freshly read from the hub.' This specifies a precise verb (get), resource (single outlet), and distinguishes it from sibling tools like list_outlets (multiple outlets) and set_outlet/toggle_outlet/power_cycle (mutations).

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides clear context: this tool is for retrieving the current state of one specific outlet. It implicitly indicates that list_outlets is the source for the outlet_id, but it does not explicitly name alternatives or state when not to use this tool. The context is clear, but exclusions are not stated, so it does not receive a 5.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_outletsA

List every outlet (smart plug) paired to the DIRIGERA hub.

Returns id, name, room, on/off state and reachability for each one. Use the id with the other outlet tools.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4.7/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full transparency burden. It clearly states the tool lists all outlets and discloses the exact return fields (id, name, room, on/off state, reachability), which is the core behavioral trait. While it does not mention error handling or empty results, the description adequately communicates the tool's read-only enumeration behavior.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is two concise sentences, front-loaded with the core purpose ('List every outlet'). The second sentence provides return fields and usage guidance without redundancy. Every word earns its place, making this an exemplary concise description.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple zero-parameter list tool, the description is fully complete: it states scope, output fields, and how the ids integrate with sibling tools. The presence of an output schema reduces the need for detailed return descriptions, and the description provides enough context for an agent to invoke the tool correctly and use its results.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The input schema is an empty object with zero parameters, so parameter semantics are fully covered by the schema. Per the rubric, 0 parameters earns a baseline of 4. The description adds context by confirming the tool lists all outlets without filters, which enriches the empty schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description uses the specific verb 'List' and the resource 'outlets (smart plug) paired to the DIRIGERA hub', immediately establishing the tool's scope. The phrase 'every outlet' distinguishes it from sibling tools like get_outlet, set_outlet, toggle_outlet, and power_cycle, which operate on individual outlets. Purpose is unambiguous.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly instructs 'Use the id with the other outlet tools', clearly positioning this tool as the enumeration/selection step before applying actions via siblings. This gives strong guidance on when to use the tool and how its output feeds into other operations, effectively distinguishing its role from the alternatives.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

power_cycleA

Power-cycle whatever is plugged into an outlet: switch it off, wait, switch it back on.

Intended for rebooting hardware attached to a smart plug, such as a development board. Power is restored even if the wait is interrupted. Keep off_seconds short -- the call blocks for that long and must finish inside the MCP client's tool-call timeout.

Args: outlet_id: The outlet's id, as returned by list_outlets. off_seconds: How long to stay powered off, in seconds (0.5 to 300).

ParametersJSON Schema
NameRequiredDescriptionDefault
outlet_idYes
off_secondsNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.5/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden. It discloses blocking behavior, timeout constraint, and guarantees power restoration even if the wait is interrupted. It also notes off_seconds range. These are meaningful behavioral traits beyond the schema; however it omits error conditions or what happens on failure, but output schema may cover returns.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is well-structured: a clear one-line definition, followed by use-case context, a timeout warning, and a compact Args list. Every sentence adds information; no filler, though it is longer than the minimal example.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a tool with no annotations and minimal schema, the description covers purpose, usage context, blocking/timeout behavior, interruption guarantee, and parameter semantics. An output schema exists, so return values needn't be explained. It is sufficiently complete for an agent to select and invoke the tool correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must compensate. The Args section explains both parameters: outlet_id sourced from list_outlets, and off_seconds as a range (0.5 to 300) and as the powered-off duration. This adds meaning the schema lacks.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a specific verb and resource: 'Power-cycle whatever is plugged into an outlet: switch it off, wait, switch it back on.' This clearly distinguishes it from sibling tools like toggle_outlet or set_outlet by naming the off-wait-on sequence and intended use for rebooting hardware.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It states the intended scenario: 'Intended for rebooting hardware attached to a smart plug, such as a development board.' This gives clear context, though it does not explicitly contrast with alternatives like toggle_outlet or set_outlet. Thus clear context but no explicit exclusionary guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

set_outletA

Switch an outlet on or off and return its state as read back from the hub.

Args: outlet_id: The outlet's id, as returned by list_outlets. on: True to switch the outlet on, False to switch it off.

ParametersJSON Schema
NameRequiredDescriptionDefault
onYes
outlet_idYes

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.3/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description discloses the key side effect (switching) and adds the behavioral detail that it returns the state read back from the hub. It could mention failure modes or prerequisites, but for a simple control tool this is reasonably transparent.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is extremely concise, front-loads the action, and includes only necessary details. Every sentence earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple two-parameter tool with an output schema, the description adequately covers action and parameters. It lacks explicit comparison to siblings, but the low complexity and output schema reduce the need for additional detail.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 0%, but the description fully compensates by explaining both parameters: outlet_id is the id from list_outlets, and on is a boolean to switch on/off. This adds clear meaning beyond the bare schema types.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's function: 'Switch an outlet on or off and return its state as read back from the hub.' The verb 'switch' and resource 'outlet' are specific, and the read-back phrase distinguishes it from toggle_outlet and power_cycle.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies usage for setting a specific on/off state but does not explicitly mention when to use this tool over alternatives like toggle_outlet or power_cycle. No exclusions or alternative references are provided.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

toggle_outletA

Flip an outlet to the opposite of its current state and return the new state.

Args: outlet_id: The outlet's id, as returned by list_outlets.

ParametersJSON Schema
NameRequiredDescriptionDefault
outlet_idYes

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.2/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden. It discloses the core behavior (changing to the opposite state) and the return value ('return the new state'). It also provides a useful constraint that outlet_id must be 'as returned by list_outlets.' This goes beyond a generic 'toggle' statement, though it does not cover error cases or side effects beyond the state change.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is compact and well-structured: a clear one-sentence purpose, followed by a concise Args section. No unnecessary words or repetition. The front-loaded first sentence immediately conveys the tool's action.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's simplicity (one required parameter) and presence of an output schema, the description covers the essential aspects: what it does, how to supply the ID, and that it returns the new state. It could have mentioned alternatives for completeness, but for a toggle operation this is sufficient.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must compensate. It does so by clarifying the single parameter: 'outlet_id: The outlet's id, as returned by list_outlets.' This adds meaning beyond the schema's title 'Outlet Id' by specifying the source and expected format, which is valuable for correct invocation.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's function: 'Flip an outlet to the opposite of its current state and return the new state.' This uses a specific verb ('flip') and resource ('outlet'), and distinguishes it from siblings like set_outlet (which sets a specific state) and get_outlet (which reads state).

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies usage by describing the toggle behavior, but does not explicitly state when to use this tool versus alternatives. It lacks any mention of 'use this instead of set_outlet when you want to flip state' or exclusions. Sibling tools exist, but no contrast is provided.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

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

  1. 5 tool updatesv0.1.0
    • First observedget_outlet
    • First observedlist_outlets
    • First observedpower_cycle
    • First observedset_outlet
    • First observedtoggle_outlet

TDQS

A4.5/5.0

Scored across 5 tools

Disambiguation5/5

Each tool has a clearly distinct purpose: listing all outlets, reading one outlet's state, setting on/off, toggling, and power-cycling. No two tools overlap in function; the descriptions make the boundaries obvious.

Naming Consistency5/5

All tool names follow the verb_noun pattern with imperative verbs (list, get, set, toggle, power_cycle) and consistent use of underscores. The noun is 'outlet' where relevant, maintaining a predictable style.

Tool Count5/5

Five tools is well-scoped for a smart-plug controller. Each tool addresses a real need (discovery, state read, state write, state flip, and a power-cycle sequence) without unnecessary bloat or missing essentials.

Completeness5/5

For the domain of outlet control, the surface is complete: you can enumerate, inspect, set, toggle, and power-cycle outlets. There are no obvious gaps, as outlets are paired through the hub rather than created or deleted via API.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    C
    maintenance
    MCP server for controlling and monitoring Digital Loggers Power Switch Pro devices, enabling outlet control, power monitoring, and device management through natural language.
    BSD 3-Clause
  • A
    license
    A
    quality
    B
    maintenance
    MCP server for controlling Wyze smart home devices (plugs, switches, thermostats, air purifiers) via Claude Desktop or Home Assistant.
    7
    24 npm
    MIT