From b3da57de35d4814b465d1807c4412d4f1318cb53 Mon Sep 17 00:00:00 2001 From: Paul Thumfart <158182803+Dumdart@users.noreply.github.com> Date: Thu, 20 Aug 2026 20:26:52 +0200 Subject: [PATCH 1/4] Plugin: Add cloude code support --- .claude-plugin/marketplace.json | 14 +++++++++++ tests/test_plugin_bundle.py | 24 +++++++++++++++++++ topicgate-plugin/.claude-plugin/plugin.json | 19 +++++++++++++++ topicgate-plugin/CONTRACT.md | 4 ++-- topicgate-plugin/SCENARIOS.md | 2 +- .../skills/setup-topicgate/SKILL.md | 13 +++++----- 6 files changed, 67 insertions(+), 9 deletions(-) create mode 100644 .claude-plugin/marketplace.json create mode 100644 topicgate-plugin/.claude-plugin/plugin.json diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json new file mode 100644 index 0000000..dc58b92 --- /dev/null +++ b/.claude-plugin/marketplace.json @@ -0,0 +1,14 @@ +{ + "name": "topicgate", + "owner": { + "name": "Dumdart" + }, + "plugins": [ + { + "name": "topicgate", + "source": "./topicgate-plugin", + "description": "Secure local access to MQTT state through TopicGate", + "version": "1.0.0" + } + ] +} diff --git a/tests/test_plugin_bundle.py b/tests/test_plugin_bundle.py index a9345f8..74a3a33 100644 --- a/tests/test_plugin_bundle.py +++ b/tests/test_plugin_bundle.py @@ -42,6 +42,30 @@ def test_plugin_bundle_matches_codex_ingestion_contract() -> None: assert asset.is_file() +def test_plugin_bundle_matches_claude_code_ingestion_contract() -> None: + manifest = json.loads( + (PLUGIN_ROOT / ".claude-plugin" / "plugin.json").read_text( + encoding="utf-8" + ) + ) + marketplace = json.loads( + (REPOSITORY_ROOT / ".claude-plugin" / "marketplace.json").read_text( + encoding="utf-8" + ) + ) + marketplace_plugin = next( + plugin for plugin in marketplace["plugins"] if plugin["name"] == manifest["name"] + ) + + assert marketplace_plugin["source"] == "./topicgate-plugin" + assert marketplace_plugin["version"] == manifest["version"] + assert manifest["author"]["name"] + assert manifest["skills"] == "./skills/" + assert manifest["mcpServers"] == "./.mcp.json" + assert (PLUGIN_ROOT / manifest["skills"].removeprefix("./")).is_dir() + assert (PLUGIN_ROOT / manifest["mcpServers"].removeprefix("./")).is_file() + + def test_plugin_skills_have_valid_frontmatter() -> None: skill_files = sorted((PLUGIN_ROOT / "skills").glob("*/SKILL.md")) diff --git a/topicgate-plugin/.claude-plugin/plugin.json b/topicgate-plugin/.claude-plugin/plugin.json new file mode 100644 index 0000000..984ac11 --- /dev/null +++ b/topicgate-plugin/.claude-plugin/plugin.json @@ -0,0 +1,19 @@ +{ + "name": "topicgate", + "version": "1.0.0", + "description": "Secure local access to your MQTT topics through TopicGate MCP", + "author": { + "name": "Dumdart", + "url": "https://github.com/Dumdart" + }, + "homepage": "https://github.com/Dumdart/TopicGate", + "repository": "https://github.com/Dumdart/TopicGate", + "license": "MIT", + "keywords": [ + "mqtt", + "mcp", + "iot" + ], + "skills": "./skills/", + "mcpServers": "./.mcp.json" +} diff --git a/topicgate-plugin/CONTRACT.md b/topicgate-plugin/CONTRACT.md index 5079427..0e21aef 100644 --- a/topicgate-plugin/CONTRACT.md +++ b/topicgate-plugin/CONTRACT.md @@ -1,8 +1,8 @@ # TopicGate plugin contract This plugin supports TopicGate 1.x and the TopicGate MCP contract `1.0`. -It is tested with FastMCP 3.4.7 and the Codex plugin ingestion contract used by -the repository test suite. +It is tested with FastMCP 3.4.7 and the Codex and Claude Code plugin ingestion +contracts used by the repository test suite. The installed `.mcp.json` is read-only. Control mode is a separate, explicit configuration in `.mcp-control.json`; copy it only when connection changes, diff --git a/topicgate-plugin/SCENARIOS.md b/topicgate-plugin/SCENARIOS.md index 7809471..d5fe8b7 100644 --- a/topicgate-plugin/SCENARIOS.md +++ b/topicgate-plugin/SCENARIOS.md @@ -152,7 +152,7 @@ expected skill path and the key assertions to check. - Tells the user to install and verify TopicGate with `python -m pip install topicgate` followed by `python -m topicgate --help`. - Explains that `python topicgate` is invalid because the `-m` option is required. -- Tells the user to refresh the plugin, restart Codex, and open a new thread. +- Tells the user to refresh the plugin, restart the agent host, and open a new thread. - Does not attempt to substitute another tool. --- diff --git a/topicgate-plugin/skills/setup-topicgate/SKILL.md b/topicgate-plugin/skills/setup-topicgate/SKILL.md index 7f6c736..ac39621 100644 --- a/topicgate-plugin/skills/setup-topicgate/SKILL.md +++ b/topicgate-plugin/skills/setup-topicgate/SKILL.md @@ -10,7 +10,7 @@ Give the user a short introduction before installation instructions: - TopicGate is a local MQTT gateway with a desktop application and an MCP server. - The desktop application manages broker profiles, credentials, subscriptions, and retained observation settings. -- The read-only MCP server lets Codex inspect broker health, subscriptions, topics, +- The read-only MCP server lets the agent inspect broker health, subscriptions, topics, and the latest MQTT values observed by TopicGate. Those values can be cached, stale, or partial; they are not authoritative broker history. @@ -20,8 +20,9 @@ If TopicGate tools are already available, do not suggest reinstalling. Briefly introduce TopicGate, explain that this plugin uses the local read-only server, and continue with the user's MQTT request. -If the tools are unavailable, explain that the Codex plugin supplies skills and MCP -configuration but requires the TopicGate Python package on the machine running Codex. +If the tools are unavailable, explain that the TopicGate plugin supplies skills and +MCP configuration but requires the TopicGate Python package on the machine running +the agent host. Do not run an installation command without the user's permission. ## Install and verify @@ -38,7 +39,7 @@ without relying on a separate scripts directory being on `PATH`. `python topicga is not a valid substitute; the `-m` option is required. After verification, tell the user to reinstall or refresh the TopicGate plugin if -needed, restart Codex, and open a new thread. The portable plugin configuration +needed, restart the agent host, and open a new thread. The portable plugin configuration expects the `topicgate` console executable to be on `PATH` and starts: ```console @@ -46,7 +47,7 @@ topicgate --mode read-only ``` Do not tell the user to launch that blocking stdio command manually during ordinary -Codex use. +agent use. If `topicgate` is not on `PATH`, use TopicGate Desktop's MCP setup page to copy a configuration containing the resolved absolute executable path. Control mode must @@ -58,7 +59,7 @@ Once the tools are available: 1. Ask the user to open TopicGate Desktop with `topicgate-gui` and create a broker profile if none exists. -2. Use `list_brokers` to confirm that Codex can see the configured profiles. +2. Use `list_brokers` to confirm that the agent can see the configured profiles. 3. Offer a passive overview using `get_connection_status`, `list_subscriptions`, and `get_broker_snapshot`. From ea344bbadd619fa19b0f73a38ffdfbe2017e5dda Mon Sep 17 00:00:00 2001 From: Paul Thumfart <158182803+Dumdart@users.noreply.github.com> Date: Thu, 20 Aug 2026 20:31:06 +0200 Subject: [PATCH 2/4] Plugin: add vscode copilot support --- tests/test_plugin_bundle.py | 31 +++++++++++++++++++++++++++++++ topicgate-plugin/CONTRACT.md | 11 ++++++----- topicgate-plugin/mcp.json | 13 +++++++++++++ topicgate-plugin/plugin.json | 18 ++++++++++++++++++ 4 files changed, 68 insertions(+), 5 deletions(-) create mode 100644 topicgate-plugin/mcp.json create mode 100644 topicgate-plugin/plugin.json diff --git a/tests/test_plugin_bundle.py b/tests/test_plugin_bundle.py index 74a3a33..749c7e0 100644 --- a/tests/test_plugin_bundle.py +++ b/tests/test_plugin_bundle.py @@ -66,6 +66,37 @@ def test_plugin_bundle_matches_claude_code_ingestion_contract() -> None: assert (PLUGIN_ROOT / manifest["mcpServers"].removeprefix("./")).is_file() +def test_plugin_bundle_matches_agent_plugins_ingestion_contract() -> None: + manifest = json.loads( + (PLUGIN_ROOT / "plugin.json").read_text(encoding="utf-8") + ) + mcp_config = json.loads( + (PLUGIN_ROOT / "mcp.json").read_text(encoding="utf-8") + ) + marketplace = json.loads( + (REPOSITORY_ROOT / ".claude-plugin" / "marketplace.json").read_text( + encoding="utf-8" + ) + ) + marketplace_plugin = next( + plugin for plugin in marketplace["plugins"] if plugin["name"] == manifest["name"] + ) + server_config = mcp_config["mcpServers"]["topicgate"] + + assert manifest["$schema"] == ( + "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json" + ) + assert marketplace_plugin["source"] == "./topicgate-plugin" + assert marketplace_plugin["version"] == manifest["version"] + assert (PLUGIN_ROOT / "skills").is_dir() + assert mcp_config["$schema"] == ( + "https://agent-plugins.org/schemas/1.0.0/mcp.schema.json" + ) + assert server_config["type"] == "stdio" + assert server_config["command"] == "topicgate" + assert server_config["args"] == ["--mode", "read-only"] + + def test_plugin_skills_have_valid_frontmatter() -> None: skill_files = sorted((PLUGIN_ROOT / "skills").glob("*/SKILL.md")) diff --git a/topicgate-plugin/CONTRACT.md b/topicgate-plugin/CONTRACT.md index 0e21aef..b5451b7 100644 --- a/topicgate-plugin/CONTRACT.md +++ b/topicgate-plugin/CONTRACT.md @@ -1,12 +1,13 @@ # TopicGate plugin contract This plugin supports TopicGate 1.x and the TopicGate MCP contract `1.0`. -It is tested with FastMCP 3.4.7 and the Codex and Claude Code plugin ingestion -contracts used by the repository test suite. +It is tested with FastMCP 3.4.7 and the Codex, Claude Code, and Agent Plugins 1.0 +ingestion contracts used by the repository test suite. -The installed `.mcp.json` is read-only. Control mode is a separate, explicit -configuration in `.mcp-control.json`; copy it only when connection changes, -subscription changes, observation refresh, and publishing are intended. +The installed `.mcp.json` and portable `mcp.json` are read-only. Control mode is a +separate, explicit configuration in `.mcp-control.json`; copy it only when +connection changes, subscription changes, observation refresh, and publishing are +intended. TopicGate Desktop remains the owner of broker credentials, retention policy, cache deletion, and other destructive or complex maintenance. diff --git a/topicgate-plugin/mcp.json b/topicgate-plugin/mcp.json new file mode 100644 index 0000000..7826383 --- /dev/null +++ b/topicgate-plugin/mcp.json @@ -0,0 +1,13 @@ +{ + "$schema": "https://agent-plugins.org/schemas/1.0.0/mcp.schema.json", + "mcpServers": { + "topicgate": { + "type": "stdio", + "command": "topicgate", + "args": [ + "--mode", + "read-only" + ] + } + } +} diff --git a/topicgate-plugin/plugin.json b/topicgate-plugin/plugin.json new file mode 100644 index 0000000..0fb6bfe --- /dev/null +++ b/topicgate-plugin/plugin.json @@ -0,0 +1,18 @@ +{ + "$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json", + "name": "topicgate", + "version": "1.0.0", + "description": "Secure local access to your MQTT topics through TopicGate MCP", + "author": { + "name": "Dumdart", + "url": "https://github.com/Dumdart" + }, + "homepage": "https://github.com/Dumdart/TopicGate", + "repository": "https://github.com/Dumdart/TopicGate", + "license": "MIT", + "keywords": [ + "mqtt", + "mcp", + "iot" + ] +} From baca22ca7d598d4e8d5cb7fd622d4d931a7e82bd Mon Sep 17 00:00:00 2001 From: Paul Thumfart <158182803+Dumdart@users.noreply.github.com> Date: Thu, 20 Aug 2026 20:49:22 +0200 Subject: [PATCH 3/4] Plugin: Add cursor plugin support --- tests/test_plugin_bundle.py | 2 +- topicgate-plugin/CONTRACT.md | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/test_plugin_bundle.py b/tests/test_plugin_bundle.py index 749c7e0..65caa0a 100644 --- a/tests/test_plugin_bundle.py +++ b/tests/test_plugin_bundle.py @@ -66,7 +66,7 @@ def test_plugin_bundle_matches_claude_code_ingestion_contract() -> None: assert (PLUGIN_ROOT / manifest["mcpServers"].removeprefix("./")).is_file() -def test_plugin_bundle_matches_agent_plugins_ingestion_contract() -> None: +def test_plugin_bundle_matches_copilot_and_cursor_agent_plugins_contract() -> None: manifest = json.loads( (PLUGIN_ROOT / "plugin.json").read_text(encoding="utf-8") ) diff --git a/topicgate-plugin/CONTRACT.md b/topicgate-plugin/CONTRACT.md index b5451b7..0b158fc 100644 --- a/topicgate-plugin/CONTRACT.md +++ b/topicgate-plugin/CONTRACT.md @@ -1,8 +1,9 @@ # TopicGate plugin contract This plugin supports TopicGate 1.x and the TopicGate MCP contract `1.0`. -It is tested with FastMCP 3.4.7 and the Codex, Claude Code, and Agent Plugins 1.0 -ingestion contracts used by the repository test suite. +It is tested with FastMCP 3.4.7 and the Codex, Claude Code, GitHub Copilot, and +Cursor-compatible Agent Plugins 1.0 ingestion contracts used by the repository +test suite. The installed `.mcp.json` and portable `mcp.json` are read-only. Control mode is a separate, explicit configuration in `.mcp-control.json`; copy it only when From 189233963703b6705fffb3906a1540a482de461c Mon Sep 17 00:00:00 2001 From: Paul Thumfart <158182803+Dumdart@users.noreply.github.com> Date: Thu, 20 Aug 2026 21:28:49 +0200 Subject: [PATCH 4/4] Docs: add agent specific install guides --- README.md | 47 ++++++--------------- docs/install/CLAUDE_CODE.md | 58 ++++++++++++++++++++++++++ docs/install/CODEX.md | 48 ++++++++++++++++++++++ docs/install/CURSOR.md | 55 +++++++++++++++++++++++++ docs/install/VSCODE_COPILOT.md | 75 ++++++++++++++++++++++++++++++++++ 5 files changed, 248 insertions(+), 35 deletions(-) create mode 100644 docs/install/CLAUDE_CODE.md create mode 100644 docs/install/CODEX.md create mode 100644 docs/install/CURSOR.md create mode 100644 docs/install/VSCODE_COPILOT.md diff --git a/README.md b/README.md index 66a9af4..ad77f54 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@
Get started · - Use it from Codex · + Connect an agent · Understand observations · Desktop workflow
@@ -37,37 +37,16 @@ TopicGate supports exact MQTT paths and the standard `+` and `#` wildcard filter TopicGate requires Python 3.11+ and access to an MQTT 5-compatible broker. It is currently installed from a source checkout; package distribution is planned but not yet published. > [!IMPORTANT] -> **Windows is the only validated platform today.** The macOS and Linux paths, desktop behaviour, and credential-store integrations have not been tested end to end. Codex is the only MCP host and plugin harness validated so far; other MCP clients may work, but are not currently supported installation paths. +> **Windows is the only validated platform today.** The macOS and Linux paths, desktop behaviour, and credential-store integrations have not been tested end to end. Codex is the only agent host validated end to end so far. Claude Code, GitHub Copilot, and Cursor plugin packaging are covered by repository contract tests, but runtime validation is still pending. TopicGate uses `keyring` for the operating-system credential store; those integrations have not yet been tested across platforms. The Windows development installation is: ```powershell git clone https://github.com/Dumdart/TopicGate.git cd TopicGate -python -m venv .venv -.\.venv\Scripts\Activate.ps1 python -m pip install -e . ``` -Then install mcp in readonly mode (codex): - -```powershell -codex mcp add topicgate -- python -m topicgate -``` - -or with full access: - -```powershell -codex mcp add topicgate -- python -m topicgate --mode control -``` - -Install Plugin (codex): - -```powershell -codex plugin marketplace add . -codex plugin add topicgate@topicgate -``` - For an unvalidated macOS or Linux source checkout, activate the environment with `source .venv/bin/activate`. Install the optional MCP Apps dashboard with `uv sync --extra apps` or `python -m pip install -e ".[apps]"`. ### 2. Configure and observe @@ -113,23 +92,21 @@ Use the absolute path to `topicgate` or `topicgate.exe` if the environment is no fastmcp call --command topicgate --target list_brokers --json ``` -## Use it from Codex +## Connect an agent -TopicGate includes a Codex plugin with eight focused skills for setting up the connection, inspecting the current MQTT state, working with subscriptions, and safely refreshing or publishing only when control mode is enabled. Codex is the only plugin host tested by this project. +TopicGate can be connected to an MCP-capable agent after you have configured a broker and observed data in the desktop application. The server is read-only by default; enable control mode only in a host you trust to change broker connections, subscriptions, or device state. -
-
-
+
+