livetap 0.2.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +133 -123
- package/package.json +15 -4
- package/src/mcp/tools.ts +2 -169
- package/src/shared/canonical/cli.ts +144 -0
- package/src/shared/canonical/index.ts +10 -0
- package/src/shared/canonical/meta.ts +222 -0
- package/src/shared/canonical/tools.ts +178 -0
- package/src/shared/catalog-generators.ts +53 -44
- package/src/shared/command-catalog.ts +3 -147
package/README.md
CHANGED
|
@@ -4,29 +4,36 @@
|
|
|
4
4
|
|
|
5
5
|
Connect MQTT brokers, WebSocket feeds, or tail log files. Your agent samples, watches, and acts on real-time data through natural language.
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+

|
|
8
|
+
*Real solar inverter data — from natural language request to live alert in under 30 seconds*
|
|
9
|
+
|
|
10
|
+

|
|
11
|
+
*Escalating alerts — the agent compares values across alerts and analyzes trends autonomously*
|
|
9
12
|
|
|
10
13
|
## Quick start
|
|
11
14
|
|
|
12
|
-
**Requirements:** [Bun](https://bun.sh)
|
|
15
|
+
**Requirements:** [Bun](https://bun.sh) or Node.js 18+, Claude Code v2.1.80+
|
|
13
16
|
|
|
14
17
|
```bash
|
|
15
|
-
|
|
18
|
+
npm install livetap
|
|
19
|
+
npx livetap setup
|
|
16
20
|
```
|
|
17
21
|
|
|
18
|
-
|
|
22
|
+
Restart Claude Code with Channels enabled:
|
|
19
23
|
|
|
20
24
|
```bash
|
|
21
|
-
livetap
|
|
22
|
-
claude --dangerously-load-development-channels server:livetap
|
|
25
|
+
claude --dangerously-load-development-channels server:livetap --continue
|
|
23
26
|
```
|
|
24
27
|
|
|
28
|
+
> **What's that flag?** Claude Code [Channels](https://code.claude.com/docs/en/channels) let MCP servers push real-time events (like watcher alerts) into your conversation. The `--dangerously-load-development-channels` flag enables this for locally-installed servers. It only loads servers listed in your project's `.mcp.json` — nothing is sent externally.
|
|
29
|
+
|
|
25
30
|
Then ask your agent:
|
|
26
31
|
|
|
27
32
|
> "Connect to the IoT demo at mqtt://broker.emqx.io on topic justinx/demo/# and watch for temperature above 23 degrees"
|
|
28
33
|
|
|
29
|
-
|
|
34
|
+
The agent connects to the free public MQTT broker, samples the data to learn the payload structure, creates a watcher, and alerts you when temperature exceeds 23C.
|
|
35
|
+
|
|
36
|
+
## Setup for AI agents
|
|
30
37
|
|
|
31
38
|
If you are Claude Code or another AI agent, run `npx livetap --llm-help` for structured setup instructions.
|
|
32
39
|
|
|
@@ -39,49 +46,25 @@ npx livetap setup # Step 2: creates .mcp.json and starts daemon
|
|
|
39
46
|
# claude --dangerously-load-development-channels server:livetap --continue
|
|
40
47
|
```
|
|
41
48
|
|
|
49
|
+
After restart, your 13 MCP tools are automatically available — no discovery step needed.
|
|
50
|
+
|
|
42
51
|
**IMPORTANT — do NOT:**
|
|
43
52
|
- Add livetap to `~/.claude/mcp.json` — it goes in `.mcp.json` in the project root
|
|
44
53
|
- Configure livetap as `type: "http"` — it is a stdio MCP server (command + args)
|
|
45
54
|
- Worry about the daemon — setup starts it, and the MCP proxy auto-starts it if needed
|
|
46
55
|
- Guess field names — always `read_stream` first to see actual payload structure
|
|
56
|
+
- Use `npm init` — use the existing project directory
|
|
47
57
|
|
|
48
|
-
### After restart —
|
|
49
|
-
|
|
50
|
-
Once restarted with the channel flag, you have 13 MCP tools available. Follow this workflow:
|
|
58
|
+
### After restart — workflow
|
|
51
59
|
|
|
52
60
|
1. **CONNECT** — `create_connection({ type: "mqtt", broker: "broker.emqx.io", port: 1883, tls: false, topics: ["justinx/demo/#"] })`
|
|
53
61
|
2. **SAMPLE** — `read_stream({ connectionId: "conn_xxx", backfillSeconds: 60, maxEntries: 10 })` — ALWAYS sample first to see field paths
|
|
54
62
|
3. **WATCH** — `create_watcher({ connectionId: "conn_xxx", conditions: [{ field: "sensors.temperature.value", op: ">", value: 50 }], match: "all", cooldown: 60 })`
|
|
55
|
-
|
|
56
|
-
### Supported source types
|
|
57
|
-
|
|
58
|
-
| Type | create_connection params | CLI |
|
|
59
|
-
|------|------------------------|-----|
|
|
60
|
-
| MQTT | `{ type: "mqtt", broker: "host", port: 1883, tls: false, topics: ["topic/#"] }` | `livetap tap mqtt://host:1883/topic/#` |
|
|
61
|
-
| WebSocket | `{ type: "websocket", url: "wss://..." }` | `livetap tap wss://...` |
|
|
62
|
-
| File | `{ type: "file", path: "/var/log/app.log" }` | `livetap tap file:///var/log/app.log` |
|
|
63
|
-
|
|
64
|
-
### Data shape by source
|
|
65
|
-
|
|
66
|
-
- **MQTT/WebSocket (JSON):** payload is parsed. Use dot-paths: `sensors.temperature.value`
|
|
67
|
-
- **File (plain text):** field is `payload`. Use: `{ field: "payload", op: "contains", value: "ERROR" }` or `{ field: "payload", op: "matches", value: "5[0-9]{2}" }`
|
|
68
|
-
- **File (JSON lines):** parsed. Use dot-paths: `level`, `msg`
|
|
69
|
-
- **IMPORTANT:** always `read_stream` first. The field is `payload`, NOT `line` or `message`.
|
|
70
|
-
|
|
71
|
-
### Watcher operators
|
|
72
|
-
|
|
73
|
-
`>`, `<`, `>=`, `<=`, `==`, `!=`, `contains`, `matches` (regex)
|
|
74
|
-
|
|
75
|
-
### If the daemon is not running
|
|
76
|
-
|
|
77
|
-
The daemon auto-starts on setup and on MCP proxy init, and retries on failed requests. If it still fails, run:
|
|
78
|
-
```bash
|
|
79
|
-
livetap start
|
|
80
|
-
```
|
|
63
|
+
4. **ACT** — when `<channel>` alerts arrive, do what the user asked
|
|
81
64
|
|
|
82
65
|
## What it does
|
|
83
66
|
|
|
84
|
-
|
|
67
|
+
LiveTap runs a background daemon that connects to live data sources, buffers messages in an in-memory StreamStore, and pushes alerts into your Claude Code session via the [Channels API](https://code.claude.com/docs/en/channels). Your agent sees the data in real-time and can create expression-based watchers that fire when conditions match.
|
|
85
68
|
|
|
86
69
|
```
|
|
87
70
|
Source (MQTT/WS/File) ──> Subscriber ──> StreamStore ──> Watcher Engine
|
|
@@ -92,26 +75,37 @@ Source (MQTT/WS/File) ──> Subscriber ──> StreamStore ──> Watcher Eng
|
|
|
92
75
|
──> agent acts
|
|
93
76
|
```
|
|
94
77
|
|
|
78
|
+
## Supported sources and data shapes
|
|
79
|
+
|
|
80
|
+
| Type | create_connection params | CLI | Payload format |
|
|
81
|
+
|------|------------------------|-----|----------------|
|
|
82
|
+
| **MQTT** | `{ type: "mqtt", broker, port, tls, topics, username?, password? }` | `livetap tap mqtt://host:port/topic/#` | JSON parsed — use dot-paths: `sensors.temperature.value` |
|
|
83
|
+
| **WebSocket** | `{ type: "websocket", url, headers?, handshake? }` | `livetap tap wss://...` | JSON parsed — use dot-paths: `p`, `data.value` |
|
|
84
|
+
| **File** | `{ type: "file", path }` | `livetap tap file:///path/to/log` | Plain text: `{ payload: "the raw line" }`. JSON lines: parsed into dot-paths |
|
|
85
|
+
|
|
86
|
+
**IMPORTANT:** always `read_stream` first to see actual field names. The field is `payload`, NOT `line` or `message`.
|
|
87
|
+
|
|
95
88
|
## Examples
|
|
96
89
|
|
|
97
90
|
### IoT sensor monitoring
|
|
98
91
|
|
|
99
92
|
```
|
|
100
|
-
You: "Connect to mqtt://broker.emqx.io:1883/justinx/demo/# and watch for temperature above
|
|
93
|
+
You: "Connect to mqtt://broker.emqx.io:1883/justinx/demo/# and watch for temperature above 25C"
|
|
101
94
|
|
|
102
|
-
Agent:
|
|
95
|
+
Agent: Connects to the free public broker, samples the data to learn the payload structure,
|
|
103
96
|
sets up watcher on sensors.environmental.temperature.value > 25.
|
|
104
|
-
When it fires
|
|
97
|
+
When it fires: "sensor-zone-c hit 25.4C at 10:05:08Z"
|
|
105
98
|
```
|
|
106
99
|
|
|
107
|
-
###
|
|
100
|
+
### WebSocket trade stream
|
|
108
101
|
|
|
109
102
|
```
|
|
110
|
-
You: "Tap the Binance BTC/USDT trade stream and
|
|
103
|
+
You: "Tap the Binance BTC/USDT trade stream and log each trade"
|
|
111
104
|
|
|
112
105
|
Agent: Connects to wss://stream.binance.com:9443/ws/btcusdt@trade,
|
|
113
|
-
samples to
|
|
114
|
-
sets up watcher
|
|
106
|
+
samples to discover trade fields (p=price, q=quantity, T=timestamp),
|
|
107
|
+
sets up a watcher to log each trade. Can filter by quantity or
|
|
108
|
+
use regex on the symbol field.
|
|
115
109
|
```
|
|
116
110
|
|
|
117
111
|
### Log file monitoring
|
|
@@ -120,8 +114,8 @@ Agent: Connects to wss://stream.binance.com:9443/ws/btcusdt@trade,
|
|
|
120
114
|
You: "Watch my nginx error log for 5xx errors and summarize each one"
|
|
121
115
|
|
|
122
116
|
Agent: Taps file:///var/log/nginx/error.log,
|
|
123
|
-
creates watcher: payload matches "5[0-9]{2}",
|
|
124
|
-
|
|
117
|
+
creates regex watcher: payload matches "5[0-9]{2}",
|
|
118
|
+
summarizes each match:
|
|
125
119
|
"503 Service Unavailable on /api/data — upstream auth-service not responding"
|
|
126
120
|
```
|
|
127
121
|
|
|
@@ -131,103 +125,137 @@ Agent: Taps file:///var/log/nginx/error.log,
|
|
|
131
125
|
You: "Monitor /var/log/wifi.log and alert me when WiFi drops"
|
|
132
126
|
|
|
133
127
|
Agent: Taps the file, samples to see log format, creates regex watcher
|
|
134
|
-
for power state changes.
|
|
128
|
+
for power state changes. Reports outage duration:
|
|
135
129
|
"Wi-Fi powered OFF at 17:51:45, back ON at 17:51:47 (2s outage)"
|
|
136
130
|
```
|
|
137
131
|
|
|
132
|
+
### CLI walkthrough (no agent)
|
|
133
|
+
|
|
134
|
+
You can also use LiveTap directly from the terminal:
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
# 1. Tap a source
|
|
138
|
+
livetap tap mqtt://broker.emqx.io:1883/justinx/demo/#
|
|
139
|
+
|
|
140
|
+
# 2. Sample the data to see what's flowing
|
|
141
|
+
livetap sip conn_xxxx
|
|
142
|
+
|
|
143
|
+
# 3. Set up a watcher
|
|
144
|
+
livetap watch conn_xxxx "sensors.environmental.temperature.value > 25"
|
|
145
|
+
|
|
146
|
+
# 4. Check status
|
|
147
|
+
livetap status
|
|
148
|
+
livetap watchers --logs w_xxxx
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## Expression watchers
|
|
152
|
+
|
|
153
|
+
Watchers use structured conditions:
|
|
154
|
+
|
|
155
|
+
```json
|
|
156
|
+
{
|
|
157
|
+
"conditions": [
|
|
158
|
+
{ "field": "sensors.temperature.value", "op": ">", "value": 50 },
|
|
159
|
+
{ "field": "sensors.humidity.value", "op": ">", "value": 90 }
|
|
160
|
+
],
|
|
161
|
+
"match": "all",
|
|
162
|
+
"cooldown": 60
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
**Operators:** `>`, `<`, `>=`, `<=`, `==`, `!=`, `contains`, `matches` (regex)
|
|
167
|
+
|
|
168
|
+
**Match modes:** `"all"` = AND (all conditions must be true), `"any"` = OR (at least one)
|
|
169
|
+
|
|
170
|
+
**Cooldown:** Seconds between repeated alerts. `0` for every match, `60` default. Use 0 for rare events, 30-60 for sensors, 300+ for high-frequency streams.
|
|
171
|
+
|
|
172
|
+
When a watcher fires, the alert arrives as a `<channel>` tag in your Claude Code session. The agent reads it and acts — writing to a file, calling an API, or whatever you asked.
|
|
173
|
+
|
|
138
174
|
## CLI
|
|
139
175
|
|
|
140
176
|
```bash
|
|
177
|
+
# Setup
|
|
178
|
+
livetap setup # Configure .mcp.json, start daemon, print restart instructions
|
|
179
|
+
|
|
141
180
|
# Daemon
|
|
142
181
|
livetap start # Start daemon (auto-started by setup)
|
|
143
|
-
livetap
|
|
144
|
-
livetap
|
|
182
|
+
livetap start --port 9000 # Custom port (default 8788, env: LIVETAP_PORT)
|
|
183
|
+
livetap start --foreground # Run in foreground (don't detach)
|
|
184
|
+
livetap stop # Stop daemon
|
|
185
|
+
livetap status # Show daemon, taps, and watchers
|
|
186
|
+
livetap status --json # JSON output
|
|
145
187
|
|
|
146
188
|
# Tap into data sources
|
|
147
189
|
livetap tap mqtt://broker.emqx.io:1883/sensors/# # MQTT broker
|
|
148
190
|
livetap tap wss://stream.binance.com:9443/ws/btcusdt@trade # WebSocket
|
|
149
191
|
livetap tap file:///var/log/nginx/error.log # Log file
|
|
192
|
+
livetap tap connection.json # Config from file
|
|
193
|
+
livetap tap <uri> --name "my-source" # With display name
|
|
150
194
|
livetap taps # List active taps
|
|
151
|
-
livetap untap <connectionId> # Remove
|
|
195
|
+
livetap untap <connectionId> # Remove a tap
|
|
152
196
|
|
|
153
197
|
# Sample data
|
|
154
198
|
livetap sip <connectionId> # Pretty JSON output
|
|
155
199
|
livetap sip <connectionId> --raw # Raw JSON
|
|
200
|
+
livetap sip <connectionId> --max 20 --back 120 # 20 entries, last 120 seconds
|
|
156
201
|
|
|
157
202
|
# Watchers
|
|
158
203
|
livetap watch <connId> "temperature > 50" # Numeric
|
|
159
204
|
livetap watch <connId> "payload matches 'ERROR|FATAL'" # Regex
|
|
160
205
|
livetap watch <connId> "temp > 50 AND humidity > 90" # AND
|
|
206
|
+
livetap watch <connId> "temp > 50 OR smoke > 0.05" # OR
|
|
161
207
|
livetap watch <connId> "price > 70000" --cooldown 300 # Custom cooldown
|
|
208
|
+
livetap watch <connId> "status == 'error'" --action webhook:https://... # Webhook action
|
|
162
209
|
livetap watchers # List all
|
|
163
|
-
livetap watchers
|
|
210
|
+
livetap watchers <connectionId> # Filter by connection
|
|
211
|
+
livetap watchers <watcherId> # Show details
|
|
212
|
+
livetap watchers --logs <watcherId> # View evaluation logs
|
|
164
213
|
livetap unwatch <watcherId> # Remove
|
|
165
214
|
```
|
|
166
215
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
| Protocol | Example | Use case |
|
|
170
|
-
|----------|---------|----------|
|
|
171
|
-
| **MQTT** | `livetap tap mqtt://broker.emqx.io:1883/sensors/#` | IoT sensors, home automation |
|
|
172
|
-
| **WebSocket** | `livetap tap wss://stream.binance.com:9443/ws/btcusdt@trade` | Finance, real-time APIs |
|
|
173
|
-
| **File tailing** | `livetap tap file:///var/log/nginx/error.log` | Log monitoring, DevOps |
|
|
174
|
-
| **Webhook** | `livetap tap webhook` | CI/CD, external services |
|
|
175
|
-
| Kafka | Planned | Event sourcing, analytics |
|
|
216
|
+
`livetap --help` for the full reference. `livetap --llm-help` for machine-readable JSON.
|
|
176
217
|
|
|
177
218
|
## MCP tools
|
|
178
219
|
|
|
179
|
-
|
|
220
|
+
LiveTap exposes 13 MCP tools that your agent uses automatically:
|
|
180
221
|
|
|
181
222
|
| Tool | What it does |
|
|
182
223
|
|------|-------------|
|
|
183
|
-
| `create_connection` | Connect to MQTT, WebSocket, or
|
|
184
|
-
| `list_connections` | List active connections |
|
|
185
|
-
| `get_connection` |
|
|
186
|
-
| `destroy_connection` |
|
|
224
|
+
| `create_connection` | Connect to MQTT, WebSocket, file, or webhook |
|
|
225
|
+
| `list_connections` | List active connections with status and message rate |
|
|
226
|
+
| `get_connection` | Get detailed connection status |
|
|
227
|
+
| `destroy_connection` | Stop and remove a connection |
|
|
187
228
|
| `read_stream` | Sample recent entries from a stream |
|
|
188
229
|
| `create_watcher` | Set up expression-based alerts |
|
|
189
|
-
| `list_watchers` | List watchers |
|
|
190
|
-
| `get_watcher` | Watcher details |
|
|
191
|
-
| `get_watcher_logs` | View MATCH
|
|
192
|
-
| `update_watcher` | Change conditions or cooldown |
|
|
193
|
-
| `delete_watcher` |
|
|
230
|
+
| `list_watchers` | List watchers, optionally filter by connection |
|
|
231
|
+
| `get_watcher` | Watcher details: conditions, status, match count |
|
|
232
|
+
| `get_watcher_logs` | View MATCH, SUPPRESSED, FIELD_NOT_FOUND logs |
|
|
233
|
+
| `update_watcher` | Change conditions, match mode, action, or cooldown |
|
|
234
|
+
| `delete_watcher` | Stop and remove a watcher |
|
|
194
235
|
| `restart_watcher` | Restart a stopped watcher |
|
|
195
236
|
| `status` | Daemon health, uptime, connections, and watchers summary |
|
|
196
237
|
|
|
197
|
-
##
|
|
198
|
-
|
|
199
|
-
Watchers use structured conditions — not arbitrary code:
|
|
200
|
-
|
|
201
|
-
```json
|
|
202
|
-
{
|
|
203
|
-
"conditions": [
|
|
204
|
-
{ "field": "sensors.temperature.value", "op": ">", "value": 50 },
|
|
205
|
-
{ "field": "sensors.humidity.value", "op": ">", "value": 90 }
|
|
206
|
-
],
|
|
207
|
-
"match": "all",
|
|
208
|
-
"cooldown": 60
|
|
209
|
-
}
|
|
210
|
-
```
|
|
211
|
-
|
|
212
|
-
**Operators:** `>`, `<`, `>=`, `<=`, `==`, `!=`, `contains`, `matches` (regex)
|
|
238
|
+
## Troubleshooting
|
|
213
239
|
|
|
214
|
-
**
|
|
240
|
+
**Daemon won't start / "Unable to connect"**
|
|
241
|
+
Run `livetap start --foreground` to see error output. Check if port 8788 is in use: `lsof -i :8788`. Use `--port` or `LIVETAP_PORT` env var to change.
|
|
215
242
|
|
|
216
|
-
|
|
243
|
+
**MQTT connection refused**
|
|
244
|
+
Verify the broker is reachable: `nc -zv broker.emqx.io 1883`. Check that `tls: false` and `port: 1883` are set for unencrypted brokers. Brokers on port 8883 typically require `tls: true`.
|
|
217
245
|
|
|
218
|
-
|
|
246
|
+
**Watcher not firing**
|
|
247
|
+
Run `read_stream` (or `livetap sip`) to verify data is flowing. Check field paths match the actual payload structure. View watcher logs: `livetap watchers --logs <watcherId>` — look for FIELD_NOT_FOUND or SUPPRESSED events.
|
|
219
248
|
|
|
220
|
-
|
|
249
|
+
**MCP tools not showing after restart**
|
|
250
|
+
Verify `.mcp.json` exists in the project root (not `~/.claude/mcp.json`). Restart Claude Code with the `--dangerously-load-development-channels server:livetap` flag.
|
|
221
251
|
|
|
222
|
-
|
|
223
|
-
2. **SAMPLE** — `read_stream` to see the data shape (always before creating watchers)
|
|
224
|
-
3. **WATCH** — `create_watcher` with the correct field paths
|
|
225
|
-
4. **ACT** — when `<channel>` alerts arrive, do what the user asked
|
|
252
|
+
## Limitations
|
|
226
253
|
|
|
227
|
-
|
|
228
|
-
- **
|
|
229
|
-
- **
|
|
230
|
-
- **
|
|
254
|
+
- **In-memory stream buffer** — data does not persist across daemon restarts.
|
|
255
|
+
- **No daemon API auth** — the HTTP API listens on localhost only (port 8788).
|
|
256
|
+
- **Throughput** — tested with streams up to ~50 msg/s. High-throughput streams (1000+ msg/s) may need higher cooldowns on watchers.
|
|
257
|
+
- **Credentials** — MQTT credentials are passed as tool parameters, not stored on disk.
|
|
258
|
+
- **Single instance** — one daemon per port. Multiple projects can share a daemon or use different ports.
|
|
231
259
|
|
|
232
260
|
## Configuration
|
|
233
261
|
|
|
@@ -235,19 +263,9 @@ The agent knows field paths differ by source:
|
|
|
235
263
|
|
|
236
264
|
**State directory:** `~/.livetap/` stores `daemon.pid`, daemon logs, and watcher evaluation logs.
|
|
237
265
|
|
|
238
|
-
**MCP config:** `.mcp.json` in your project root
|
|
239
|
-
```json
|
|
240
|
-
{
|
|
241
|
-
"mcpServers": {
|
|
242
|
-
"livetap": {
|
|
243
|
-
"command": "bun",
|
|
244
|
-
"args": ["path/to/src/mcp/channel.ts"]
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
```
|
|
266
|
+
**MCP config:** `npx livetap setup` generates `.mcp.json` in your project root with the correct absolute path.
|
|
249
267
|
|
|
250
|
-
**Machine-readable help:** `livetap --llm-help` outputs structured JSON
|
|
268
|
+
**Machine-readable help:** `npx livetap --llm-help` outputs structured JSON with setup steps, CLI commands, and MCP tool schemas.
|
|
251
269
|
|
|
252
270
|
## Development
|
|
253
271
|
|
|
@@ -255,22 +273,14 @@ The agent knows field paths differ by source:
|
|
|
255
273
|
git clone https://github.com/livetap/livetap.git
|
|
256
274
|
cd livetap
|
|
257
275
|
bun install
|
|
258
|
-
bun test #
|
|
259
|
-
bun test tests/
|
|
260
|
-
SKIP_LIVE_MQTT=1 bun test # Skip tests needing
|
|
276
|
+
bun test # Run all tests
|
|
277
|
+
bun test tests/phase0/ # Canonical drift detection
|
|
278
|
+
SKIP_LIVE_MQTT=1 bun test # Skip tests needing external brokers
|
|
261
279
|
```
|
|
262
280
|
|
|
263
|
-
See [docs/PLAN.md](docs/PLAN.md) for the full build plan with phased architecture.
|
|
264
|
-
|
|
265
281
|
## Contributing
|
|
266
282
|
|
|
267
|
-
|
|
268
|
-
2. `bun install && bun test`
|
|
269
|
-
3. Make changes, add tests
|
|
270
|
-
4. `bun test` must pass
|
|
271
|
-
5. PR to `main` branch
|
|
272
|
-
|
|
273
|
-
See [docs/PLAN.md](docs/PLAN.md) for architecture and module layout.
|
|
283
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for setup, architecture, testing, and PR guidelines.
|
|
274
284
|
|
|
275
285
|
## License
|
|
276
286
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "livetap",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Push live data streams into your AI coding agent. LLM/AI agent? Run: npx livetap --llm-help",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -10,9 +10,20 @@
|
|
|
10
10
|
"test": "bun test"
|
|
11
11
|
},
|
|
12
12
|
"keywords": [
|
|
13
|
-
"mqtt",
|
|
14
|
-
"
|
|
15
|
-
"
|
|
13
|
+
"mqtt",
|
|
14
|
+
"kafka",
|
|
15
|
+
"websocket",
|
|
16
|
+
"webhook",
|
|
17
|
+
"streaming",
|
|
18
|
+
"real-time",
|
|
19
|
+
"monitoring",
|
|
20
|
+
"alerts",
|
|
21
|
+
"mcp",
|
|
22
|
+
"claude-code",
|
|
23
|
+
"ai-agent",
|
|
24
|
+
"iot",
|
|
25
|
+
"observability",
|
|
26
|
+
"data-pipeline"
|
|
16
27
|
],
|
|
17
28
|
"license": "MIT",
|
|
18
29
|
"repository": {
|
package/src/mcp/tools.ts
CHANGED
|
@@ -3,181 +3,14 @@
|
|
|
3
3
|
* Registers tools on an MCP Server that proxy to the daemon HTTP API.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { z } from 'zod'
|
|
7
6
|
import type { Server } from '@modelcontextprotocol/sdk/server/index.js'
|
|
8
7
|
import {
|
|
9
8
|
ListToolsRequestSchema,
|
|
10
9
|
CallToolRequestSchema,
|
|
11
10
|
} from '@modelcontextprotocol/sdk/types.js'
|
|
12
11
|
|
|
13
|
-
export
|
|
14
|
-
|
|
15
|
-
name: 'create_connection',
|
|
16
|
-
description: 'Create a data connection. MQTT: connects to a broker and subscribes to topics. WebSocket: connects to a remote WS URL. Webhook: creates an HTTP ingest endpoint.',
|
|
17
|
-
inputSchema: {
|
|
18
|
-
type: 'object' as const,
|
|
19
|
-
properties: {
|
|
20
|
-
type: { type: 'string', enum: ['mqtt', 'webhook', 'websocket', 'file'], description: 'Source type', default: 'mqtt' },
|
|
21
|
-
name: { type: 'string', description: 'Display name for the connection' },
|
|
22
|
-
broker: { type: 'string', description: 'MQTT broker hostname (required for mqtt)' },
|
|
23
|
-
port: { type: 'number', description: 'Broker port (default 1883 for mqtt)', default: 1883 },
|
|
24
|
-
tls: { type: 'boolean', description: 'Use TLS (default false)', default: false },
|
|
25
|
-
username: { type: 'string', description: 'MQTT username', default: '' },
|
|
26
|
-
password: { type: 'string', description: 'MQTT password', default: '' },
|
|
27
|
-
topics: { type: 'array', items: { type: 'string' }, description: 'MQTT topic filters (required for mqtt)' },
|
|
28
|
-
url: { type: 'string', description: 'WebSocket URL (required for websocket)' },
|
|
29
|
-
headers: { type: 'object', description: 'WebSocket auth headers' },
|
|
30
|
-
handshake: { type: 'string', description: 'Message to send after WS connect (e.g. subscription JSON)' },
|
|
31
|
-
path: { type: 'string', description: 'Absolute file path to tail (required for file type, e.g. "/var/log/app.log")' },
|
|
32
|
-
},
|
|
33
|
-
},
|
|
34
|
-
},
|
|
35
|
-
{
|
|
36
|
-
name: 'list_connections',
|
|
37
|
-
description: 'List all active connections with their status, message rate, and buffered count.',
|
|
38
|
-
inputSchema: { type: 'object' as const, properties: {} },
|
|
39
|
-
},
|
|
40
|
-
{
|
|
41
|
-
name: 'get_connection',
|
|
42
|
-
description: 'Get detailed status of a specific connection.',
|
|
43
|
-
inputSchema: {
|
|
44
|
-
type: 'object' as const,
|
|
45
|
-
properties: {
|
|
46
|
-
connectionId: { type: 'string', description: 'The connection ID (e.g. "conn_a1b2c3d4")' },
|
|
47
|
-
},
|
|
48
|
-
required: ['connectionId'],
|
|
49
|
-
},
|
|
50
|
-
},
|
|
51
|
-
{
|
|
52
|
-
name: 'destroy_connection',
|
|
53
|
-
description: 'Destroy a connection — stops the source subscriber, cleans up the stream buffer.',
|
|
54
|
-
inputSchema: {
|
|
55
|
-
type: 'object' as const,
|
|
56
|
-
properties: {
|
|
57
|
-
connectionId: { type: 'string', description: 'The connection ID to destroy' },
|
|
58
|
-
},
|
|
59
|
-
required: ['connectionId'],
|
|
60
|
-
},
|
|
61
|
-
},
|
|
62
|
-
{
|
|
63
|
-
name: 'read_stream',
|
|
64
|
-
description: "Read recent entries from a connection's live stream. Use this to inspect what data is flowing through a connection and understand the payload structure before creating watchers.",
|
|
65
|
-
inputSchema: {
|
|
66
|
-
type: 'object' as const,
|
|
67
|
-
properties: {
|
|
68
|
-
connectionId: { type: 'string', description: 'The connection ID to read from' },
|
|
69
|
-
backfillSeconds: { type: 'number', description: 'Include entries from the last N seconds (default 60)', default: 60 },
|
|
70
|
-
maxEntries: { type: 'number', description: 'Max entries to return (default 10)', default: 10 },
|
|
71
|
-
},
|
|
72
|
-
required: ['connectionId'],
|
|
73
|
-
},
|
|
74
|
-
},
|
|
75
|
-
{
|
|
76
|
-
name: 'create_watcher',
|
|
77
|
-
description: 'Create an expression-based watcher on a connection. The watcher evaluates structured conditions against each stream entry and fires an alert when conditions match. ALWAYS use read_stream first to understand the data shape and field paths.',
|
|
78
|
-
inputSchema: {
|
|
79
|
-
type: 'object' as const,
|
|
80
|
-
properties: {
|
|
81
|
-
connectionId: { type: 'string', description: 'The connection ID to watch' },
|
|
82
|
-
conditions: {
|
|
83
|
-
type: 'array',
|
|
84
|
-
description: 'Array of conditions: [{field: "dot.path", op: ">", value: 50}]. Supported ops: >, <, >=, <=, ==, !=, contains, matches (regex)',
|
|
85
|
-
items: {
|
|
86
|
-
type: 'object',
|
|
87
|
-
properties: {
|
|
88
|
-
field: { type: 'string', description: 'Dot-path into the JSON payload (e.g. "sensors.temperature.value")' },
|
|
89
|
-
op: { type: 'string', enum: ['>', '<', '>=', '<=', '==', '!=', 'contains', 'matches'], description: 'Comparison operator. "matches" takes a regex pattern string.' },
|
|
90
|
-
value: { description: 'Value to compare against (number, string, or boolean)' },
|
|
91
|
-
},
|
|
92
|
-
required: ['field', 'op', 'value'],
|
|
93
|
-
},
|
|
94
|
-
},
|
|
95
|
-
match: { type: 'string', enum: ['all', 'any'], description: 'How to combine conditions: "all" (AND) or "any" (OR). Default: "all"', default: 'all' },
|
|
96
|
-
action: { description: '"channel_alert" (default), or {webhook: "url"}, or {shell: "command"}', default: 'channel_alert' },
|
|
97
|
-
cooldown: { type: 'number', description: 'Seconds between repeated alerts. 0 for every match, 60 default. Use 0 for rare events (webhooks), 30-60 for sensors, 300+ for high-frequency streams.', default: 60 },
|
|
98
|
-
},
|
|
99
|
-
required: ['connectionId', 'conditions'],
|
|
100
|
-
},
|
|
101
|
-
},
|
|
102
|
-
{
|
|
103
|
-
name: 'list_watchers',
|
|
104
|
-
description: 'List watchers. Optionally filter by connectionId. If omitted, lists all watchers.',
|
|
105
|
-
inputSchema: {
|
|
106
|
-
type: 'object' as const,
|
|
107
|
-
properties: {
|
|
108
|
-
connectionId: { type: 'string', description: 'Optional: filter by connection ID' },
|
|
109
|
-
},
|
|
110
|
-
},
|
|
111
|
-
},
|
|
112
|
-
{
|
|
113
|
-
name: 'get_watcher',
|
|
114
|
-
description: 'Get details of a specific watcher including conditions, status, match count, and config.',
|
|
115
|
-
inputSchema: {
|
|
116
|
-
type: 'object' as const,
|
|
117
|
-
properties: {
|
|
118
|
-
watcherId: { type: 'string', description: 'The watcher ID' },
|
|
119
|
-
},
|
|
120
|
-
required: ['watcherId'],
|
|
121
|
-
},
|
|
122
|
-
},
|
|
123
|
-
{
|
|
124
|
-
name: 'get_watcher_logs',
|
|
125
|
-
description: 'Get evaluation logs from a watcher — shows MATCH, SUPPRESSED, FIELD_NOT_FOUND, and CHECKPOINT events.',
|
|
126
|
-
inputSchema: {
|
|
127
|
-
type: 'object' as const,
|
|
128
|
-
properties: {
|
|
129
|
-
watcherId: { type: 'string', description: 'The watcher ID' },
|
|
130
|
-
lines: { type: 'number', description: 'Number of log lines to return (default 50)', default: 50 },
|
|
131
|
-
},
|
|
132
|
-
required: ['watcherId'],
|
|
133
|
-
},
|
|
134
|
-
},
|
|
135
|
-
{
|
|
136
|
-
name: 'update_watcher',
|
|
137
|
-
description: "Update a watcher's conditions, match mode, action, or cooldown. The watcher restarts with the new config.",
|
|
138
|
-
inputSchema: {
|
|
139
|
-
type: 'object' as const,
|
|
140
|
-
properties: {
|
|
141
|
-
watcherId: { type: 'string', description: 'The watcher ID' },
|
|
142
|
-
conditions: { type: 'array', description: 'New conditions array', items: { type: 'object' } },
|
|
143
|
-
match: { type: 'string', enum: ['all', 'any'] },
|
|
144
|
-
action: { description: 'New action' },
|
|
145
|
-
cooldown: { type: 'number', description: 'New cooldown in seconds' },
|
|
146
|
-
},
|
|
147
|
-
required: ['watcherId'],
|
|
148
|
-
},
|
|
149
|
-
},
|
|
150
|
-
{
|
|
151
|
-
name: 'delete_watcher',
|
|
152
|
-
description: 'Stop and remove a watcher.',
|
|
153
|
-
inputSchema: {
|
|
154
|
-
type: 'object' as const,
|
|
155
|
-
properties: {
|
|
156
|
-
watcherId: { type: 'string', description: 'The watcher ID to delete' },
|
|
157
|
-
},
|
|
158
|
-
required: ['watcherId'],
|
|
159
|
-
},
|
|
160
|
-
},
|
|
161
|
-
{
|
|
162
|
-
name: 'restart_watcher',
|
|
163
|
-
description: 'Restart a stopped watcher.',
|
|
164
|
-
inputSchema: {
|
|
165
|
-
type: 'object' as const,
|
|
166
|
-
properties: {
|
|
167
|
-
watcherId: { type: 'string', description: 'The watcher ID to restart' },
|
|
168
|
-
},
|
|
169
|
-
required: ['watcherId'],
|
|
170
|
-
},
|
|
171
|
-
},
|
|
172
|
-
{
|
|
173
|
-
name: 'status',
|
|
174
|
-
description: 'Get daemon status: uptime, port, active connections and watchers count.',
|
|
175
|
-
inputSchema: {
|
|
176
|
-
type: 'object' as const,
|
|
177
|
-
properties: {},
|
|
178
|
-
},
|
|
179
|
-
},
|
|
180
|
-
]
|
|
12
|
+
export { TOOLS } from '../shared/canonical/tools.js'
|
|
13
|
+
import { TOOLS } from '../shared/canonical/tools.js'
|
|
181
14
|
|
|
182
15
|
function text(content: string) {
|
|
183
16
|
return { content: [{ type: 'text' as const, text: content }] }
|