Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
@@ -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"
}
]
}
47 changes: 12 additions & 35 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

<p align="center">
<a href="#get-started">Get started</a> ·
<a href="#use-it-from-codex">Use it from Codex</a> ·
<a href="#connect-an-agent">Connect an agent</a> ·
<a href="#how-observations-work">Understand observations</a> ·
<a href="docs/desktop-workflow.md">Desktop workflow</a>
</p>
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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.

<p align="center">
<img src="docs/images/plugin_in_codex.png" alt="TopicGate installed in Codex with its MCP server and skills enabled." width="720" />
</p>
Agent setup differs by host. Use the installation guide for yours:

Install the bundled `topicgate-plugin` through your Codex plugin marketplace, enable it, and start a new thread. The plugin's default MCP configuration uses `topicgate --mode read-only` and the same platform application-data directory as TopicGate Desktop, so it can inspect the profiles, subscriptions, and observations configured there. If the executable is not on `PATH`, use TopicGate Desktop's MCP setup page to copy a configuration with the resolved absolute path.
| Agent | Setup guide | Support |
| --- | --- | --- |
| Codex | [Install TopicGate for Codex](docs/install/CODEX.md) | Plugin and MCP server validated. |
| Claude Code | [Install TopicGate for Claude Code](docs/install/CLAUDE_CODE.md) | Plugin package and MCP configuration; runtime validation pending. |
| VS Code / GitHub Copilot | [Install TopicGate for GitHub Copilot](docs/install/VSCODE_COPILOT.md) | Agent Plugins 1.0 package; runtime validation pending. |
| Cursor | [Install TopicGate for Cursor](docs/install/CURSOR.md) | Agent Plugins 1.0 package; runtime validation pending. |

Try one of these prompts:
If `topicgate` is not on the agent host's `PATH`, use the MCP setup page in TopicGate Desktop to copy a configuration with the resolved executable path.

```text
Help me set up TopicGate.
Inspect my TopicGate MQTT state.
Show the latest observed MQTT values.
```

## How observations work

Expand Down
58 changes: 58 additions & 0 deletions docs/install/CLAUDE_CODE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Install TopicGate for Claude Code

Claude Code can use TopicGate either through the bundled plugin or as a standalone stdio MCP server. The plugin is the recommended setup because it also installs the focused TopicGate skills.

Before connecting Claude Code, install TopicGate and use TopicGate Desktop to configure a broker, add subscriptions, and observe data. The MCP server reads the same local application-data directory as the desktop app.

## Install from a local checkout

From the TopicGate repository root, register the current directory as a local marketplace and install the plugin:

```powershell
claude plugin marketplace add .
claude plugin install topicgate@topicgate
```

The equivalent commands inside an interactive Claude Code session are:

```text
/plugin marketplace add .
/plugin install topicgate@topicgate
```

To load the plugin for one development session without installing it, use:

```powershell
claude --plugin-dir ./topicgate-plugin
```

The plugin automatically loads its skills and starts TopicGate in read-only mode. Restart Claude Code and open a new session after installing or updating the plugin.

## Install from GitHub

After the marketplace has been published, replace the local path with the GitHub repository:

```powershell
claude plugin marketplace add Dumdart/TopicGate
claude plugin install topicgate@topicgate
```

## Install only the MCP server

Use this when you do not want the plugin skills:

```powershell
claude mcp add topicgate -- topicgate --mode read-only
```

If `topicgate` is not on `PATH`, replace it with its absolute path. TopicGate Desktop's MCP setup page can copy a configuration with the resolved executable path.

## Control mode

Control mode can connect or disconnect brokers, change subscriptions, refresh observations, and publish MQTT messages. Use it only in a trusted environment:

```powershell
claude mcp add topicgate-control -- topicgate --mode control
```

Confirm the broker, topic, payload, and encoding before allowing a publish operation.
48 changes: 48 additions & 0 deletions docs/install/CODEX.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Install TopicGate for Codex

Codex can use TopicGate either as a standalone MCP server or through the bundled plugin. The plugin is the recommended setup because it also provides focused TopicGate skills.

Before connecting Codex, install TopicGate and use TopicGate Desktop to configure a broker, add subscriptions, and observe data. The MCP server reads the same local application-data directory as the desktop app.

## Install the plugin

From a TopicGate source checkout, add the checkout as a Codex plugin marketplace and install the bundled plugin:

```powershell
codex plugin marketplace add .
codex plugin add topicgate@topicgate
```

Enable the plugin and start a new Codex thread. Its default MCP configuration runs `topicgate --mode read-only`.

<p align="center">
<img src="../images/plugin_in_codex.png" alt="TopicGate installed in Codex with its MCP server and skills enabled." width="720" />
</p>

Try one of these prompts:

```text
Help me set up TopicGate.
Inspect my TopicGate MQTT state.
Show the latest observed MQTT values.
```

## Install only the MCP server

Use this when you do not want the plugin skills:

```powershell
codex mcp add topicgate -- topicgate --mode read-only
```

If the executable is not on `PATH`, replace `topicgate` with its absolute path. TopicGate Desktop's MCP setup page can copy a configuration with that path already resolved.

## Control mode

Control mode exposes operations that can connect or disconnect brokers, change subscriptions, refresh observations, and publish MQTT messages. Add it only to a trusted Codex environment:

```powershell
codex mcp add topicgate-control -- topicgate --mode control
```

Confirm the broker, topic, payload, and encoding before allowing a publish operation.
55 changes: 55 additions & 0 deletions docs/install/CURSOR.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Install TopicGate for Cursor

Cursor loads TopicGate through the portable Agent Plugins 1.0 package. The plugin installs eight TopicGate skills and starts the local MCP server in read-only mode.

Before installing the plugin, install TopicGate and use TopicGate Desktop to configure a broker, add subscriptions, and observe data. The MCP server reads the same local application-data directory as the desktop app.

## Test or install the plugin locally

From a TopicGate source checkout, copy the `topicgate-plugin` directory to Cursor's local plugin directory:

```powershell
$destination = Join-Path $env:USERPROFILE ".cursor\plugins\local\topicgate"
New-Item -ItemType Directory -Force -Path $destination
Copy-Item -Recurse -Force -Path ".\topicgate-plugin\*" -Destination $destination
```

Restart Cursor or run **Developer: Reload Window**. Open **Customize** and confirm that the TopicGate skills and MCP server are enabled. Skills appear under **Agent Decides** and can also be invoked with `/skill-name`.

Cursor caches the copied plugin. Repeat the copy and reload steps after changing the local plugin source.

Public one-click installation requires TopicGate to be submitted to and approved for the Cursor Marketplace. Until then, use the local installation above or distribute it through a Cursor team marketplace.

## Install only the MCP server

Use this project configuration when you want the MCP tools without the plugin skills. Create `.cursor/mcp.json` in the project:

```json
{
"mcpServers": {
"topicgate": {
"command": "topicgate",
"args": ["--mode", "read-only"]
}
}
}
```

If `topicgate` is not on `PATH`, replace it with its absolute path. TopicGate Desktop's MCP setup page can copy a configuration with the resolved executable path.

## Control mode

The installed plugin intentionally uses read-only mode. To expose operations that connect or disconnect brokers, change subscriptions, refresh observations, or publish MQTT messages, configure a separate project MCP server:

```json
{
"mcpServers": {
"topicgate-control": {
"command": "topicgate",
"args": ["--mode", "control"]
}
}
}
```

Use control mode only in a trusted project. Confirm the broker, topic, payload, and encoding before allowing a publish operation.
75 changes: 75 additions & 0 deletions docs/install/VSCODE_COPILOT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# Install TopicGate for VS Code and GitHub Copilot

TopicGate is packaged as an Agent Plugins 1.0 plugin for GitHub Copilot in VS Code and GitHub Copilot CLI. The plugin installs eight TopicGate skills and starts the local MCP server in read-only mode.

Before installing the plugin, install TopicGate and use TopicGate Desktop to configure a broker, add subscriptions, and observe data. The MCP server reads the same local application-data directory as the desktop app.

## Install in VS Code

Agent plugins require a current VS Code release with GitHub Copilot and the `chat.plugins.enabled` setting enabled.

Add the TopicGate marketplace to your VS Code `settings.json`:

```json
"chat.plugins.marketplaces": [
"Dumdart/TopicGate"
]
```

Open the Extensions view, search for `@agentPlugins`, select TopicGate, and choose **Install**. You can also run **Chat: Open Customizations**, open the **Plugins** tab, and install TopicGate from the marketplace there.

After installation, start a new chat. The skills appear in **Chat: Configure Skills**, and the `topicgate` server appears under **MCP: List Servers**.

## Install with GitHub Copilot CLI

From the TopicGate repository root, register the current directory as a local marketplace and install the plugin:

```powershell
copilot plugin marketplace add .
copilot plugin install topicgate@topicgate
```

VS Code automatically discovers plugins installed by GitHub Copilot CLI. Start a new Copilot session and use `/skills list` to confirm that the TopicGate skills loaded.

To install from GitHub after the marketplace has been published, use `Dumdart/TopicGate` instead of `.`:

```powershell
copilot plugin marketplace add Dumdart/TopicGate
copilot plugin install topicgate@topicgate
```

## Install only the MCP server

Use this workspace configuration when you want the MCP tools without the plugin skills. Create `.vscode/mcp.json` in the workspace:

```json
{
"servers": {
"topicgate": {
"type": "stdio",
"command": "topicgate",
"args": ["--mode", "read-only"]
}
}
}
```

If `topicgate` is not on `PATH`, replace it with its absolute path. TopicGate Desktop's MCP setup page can copy a configuration with the resolved executable path.

## Control mode

The installed plugin intentionally uses read-only mode. To expose operations that connect or disconnect brokers, change subscriptions, refresh observations, or publish MQTT messages, configure a separate workspace MCP server:

```json
{
"servers": {
"topicgate-control": {
"type": "stdio",
"command": "topicgate",
"args": ["--mode", "control"]
}
}
}
```

Use control mode only in a trusted workspace. Confirm the broker, topic, payload, and encoding before allowing a publish operation.
55 changes: 55 additions & 0 deletions tests/test_plugin_bundle.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,61 @@ 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_bundle_matches_copilot_and_cursor_agent_plugins_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"))

Expand Down
Loading
Loading