livetap 0.1.3 → 0.1.4

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 CHANGED
@@ -28,56 +28,24 @@ Then ask your agent:
28
28
 
29
29
  ## Setup with Claude Code (for AI agents)
30
30
 
31
- If you are Claude Code or another AI agent and a user asks you to install and use livetap, follow these steps **exactly**. Do NOT improvise — livetap uses stdio MCP (not HTTP), and requires Bun (not npm/node).
31
+ If you are Claude Code or another AI agent, run `npx livetap --llm-help` for structured setup instructions.
32
32
 
33
- ### Step 1: Install with Bun (NOT npm)
33
+ Quick version:
34
34
 
35
35
  ```bash
36
- bun init -y # if no package.json exists
37
- bun add livetap # MUST use bun, not npm — postinstall needs bun
38
- bun pm trust livetap # required bun blocks untrusted postinstall scripts
36
+ npm install livetap # Step 1: install
37
+ npx livetap setup # Step 2: creates .mcp.json
38
+ # Step 3: tell user to restart Claude Code with:
39
+ # claude --dangerously-load-development-channels server:livetap --continue
39
40
  ```
40
41
 
41
- ### Step 2: Verify .mcp.json was created
42
+ **IMPORTANT do NOT:**
43
+ - Add livetap to `~/.claude/mcp.json` — it goes in `.mcp.json` in the project root
44
+ - Configure livetap as `type: "http"` — it is a stdio MCP server (command + args)
45
+ - Start the daemon manually — the MCP proxy auto-starts it on Claude restart
46
+ - Guess field names — always `read_stream` first to see actual payload structure
42
47
 
43
- The postinstall creates `.mcp.json` in the project root. Check it:
44
-
45
- ```bash
46
- cat .mcp.json
47
- ```
48
-
49
- It should look like this (path will vary):
50
- ```json
51
- {
52
- "mcpServers": {
53
- "livetap": {
54
- "command": "bun",
55
- "args": ["/path/to/node_modules/livetap/src/mcp/channel.ts"]
56
- }
57
- }
58
- }
59
- ```
60
-
61
- **IMPORTANT:** livetap is a **stdio MCP server** (command + args), NOT an HTTP MCP server. Do NOT add it to `~/.claude/mcp.json` as `"type": "http"`. The `.mcp.json` in the project root is correct.
62
-
63
- If `.mcp.json` was not created, create it manually:
64
- ```bash
65
- echo '{"mcpServers":{"livetap":{"command":"bun","args":["'$(pwd)'/node_modules/livetap/src/mcp/channel.ts"]}}}' > .mcp.json
66
- ```
67
-
68
- ### Step 3: Tell the user to restart Claude Code
69
-
70
- You CANNOT restart yourself. Tell the user exactly this:
71
-
72
- > "livetap is installed and configured. Please restart Claude Code with:
73
- > ```
74
- > claude --dangerously-load-development-channels server:livetap
75
- > ```
76
- > Then ask me to connect to your data source."
77
-
78
- Do NOT try to start the daemon yourself — the MCP proxy auto-starts it when Claude Code restarts.
79
-
80
- ### Step 4: After restart — use MCP tools
48
+ ### After restart use MCP tools
81
49
 
82
50
  Once restarted with the channel flag, you have 12 MCP tools available. Follow this workflow:
83
51
 
package/bin/livetap.ts CHANGED
@@ -16,6 +16,7 @@ const commands: Record<string, () => Promise<void>> = {
16
16
  watch: () => import('../src/cli/watch.js').then((m) => m.run(args)),
17
17
  unwatch: () => import('../src/cli/unwatch.js').then((m) => m.run(args)),
18
18
  watchers: () => import('../src/cli/watchers.js').then((m) => m.run(args)),
19
+ setup: () => import('../src/cli/setup.js').then((m) => m.run(args)),
19
20
  mcp: () => import('../src/mcp/channel.js'),
20
21
  help: () => import('../src/cli/help.js').then((m) => m.run(args)),
21
22
  }
package/package.json CHANGED
@@ -1,13 +1,12 @@
1
1
  {
2
2
  "name": "livetap",
3
- "version": "0.1.3",
4
- "description": "Push live data streams into your AI coding agent. Connect MQTT brokers, WebSocket feeds, or tail log files.",
3
+ "version": "0.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": {
7
7
  "livetap": "./bin/livetap.js"
8
8
  },
9
9
  "scripts": {
10
- "postinstall": "bun ./scripts/postinstall.ts",
11
10
  "test": "bun test"
12
11
  },
13
12
  "keywords": [
@@ -24,7 +23,6 @@
24
23
  "files": [
25
24
  "bin/",
26
25
  "src/",
27
- "scripts/postinstall.ts",
28
26
  "README.md",
29
27
  "LICENSE"
30
28
  ],
@@ -0,0 +1,70 @@
1
+ /**
2
+ * livetap setup — creates .mcp.json and prints restart instructions.
3
+ * Works with both npm and bun. Node-compatible (no bun APIs).
4
+ */
5
+
6
+ import { existsSync, readFileSync, writeFileSync } from 'fs'
7
+ import { resolve } from 'path'
8
+
9
+ export async function run(_args: string[]) {
10
+ const root = process.cwd()
11
+ const mcpPath = resolve(root, '.mcp.json')
12
+
13
+ // Find the channel.ts path relative to where livetap is installed
14
+ const channelPaths = [
15
+ resolve(root, 'node_modules', 'livetap', 'src', 'mcp', 'channel.ts'),
16
+ resolve(root, 'node_modules', '.store', 'livetap', 'src', 'mcp', 'channel.ts'),
17
+ // Local dev: running from the repo itself
18
+ resolve(root, 'src', 'mcp', 'channel.ts'),
19
+ ]
20
+
21
+ let channelPath = channelPaths.find((p) => existsSync(p))
22
+ if (!channelPath) {
23
+ // Try to resolve from require
24
+ try {
25
+ const pkg = require.resolve('livetap/package.json')
26
+ channelPath = resolve(pkg, '..', 'src', 'mcp', 'channel.ts')
27
+ if (!existsSync(channelPath)) channelPath = undefined
28
+ } catch { /* not found */ }
29
+ }
30
+
31
+ if (!channelPath) {
32
+ console.error('Error: Could not find livetap channel.ts. Is livetap installed?')
33
+ console.error('Run: npm install livetap')
34
+ process.exit(1)
35
+ }
36
+
37
+ const mcpEntry = {
38
+ livetap: {
39
+ command: 'bun',
40
+ args: [channelPath],
41
+ },
42
+ }
43
+
44
+ // Read or create .mcp.json
45
+ let config: any = {}
46
+ if (existsSync(mcpPath)) {
47
+ try {
48
+ config = JSON.parse(readFileSync(mcpPath, 'utf-8'))
49
+ } catch {
50
+ console.error('Warning: .mcp.json exists but is malformed. Overwriting.')
51
+ }
52
+ }
53
+
54
+ if (config.mcpServers?.livetap) {
55
+ console.log('✓ livetap already configured in .mcp.json')
56
+ } else {
57
+ if (!config.mcpServers) config.mcpServers = {}
58
+ config.mcpServers.livetap = mcpEntry.livetap
59
+ writeFileSync(mcpPath, JSON.stringify(config, null, 2) + '\n')
60
+ console.log('✓ .mcp.json created with livetap MCP server entry')
61
+ }
62
+
63
+ console.log('')
64
+ console.log('→ Next step: restart Claude Code with:')
65
+ console.log(' claude --dangerously-load-development-channels server:livetap --continue')
66
+ console.log('')
67
+ console.log('→ After restart, MCP tools are available. Ask Claude to:')
68
+ console.log(' "Connect to mqtt://broker.emqx.io:1883/sensors/#"')
69
+ console.log(' "Watch for temperature > 50 and summarize alerts"')
70
+ }
@@ -15,8 +15,9 @@ export function generateHelpText(): string {
15
15
  'Usage:',
16
16
  ]
17
17
 
18
- // Group: daemon, connections, sampling, watchers
18
+ // Group: setup, daemon, connections, sampling, watchers
19
19
  const groups: { label: string; commands: string[] }[] = [
20
+ { label: '', commands: ['setup'] },
20
21
  { label: '', commands: ['start', 'stop', 'status'] },
21
22
  { label: '', commands: ['tap', 'untap', 'taps'] },
22
23
  { label: '', commands: ['sip'] },
@@ -50,8 +51,23 @@ export function generateHelpText(): string {
50
51
  export function generateLlmHelp(): object {
51
52
  return {
52
53
  name: 'livetap',
53
- version: '0.1.0',
54
+ version: '0.1.4',
54
55
  description: 'Push live data streams into your AI coding agent',
56
+ setup: {
57
+ steps: [
58
+ 'Step 1: npm install livetap (skip if already installed)',
59
+ 'Step 2: npx livetap setup (creates .mcp.json in project root — do NOT create it manually)',
60
+ 'Step 3: Tell user to restart Claude Code: claude --dangerously-load-development-channels server:livetap --continue',
61
+ 'Step 4: After restart, use create_connection to connect, read_stream to sample, create_watcher to alert',
62
+ ],
63
+ do_not: [
64
+ 'Do NOT add livetap to ~/.claude/mcp.json — it goes in .mcp.json in the project root',
65
+ 'Do NOT configure livetap as type:http — it is a stdio MCP server (command + args)',
66
+ 'Do NOT start the daemon manually — the MCP proxy auto-starts it on Claude restart',
67
+ 'Do NOT guess field names — always read_stream first to see actual payload structure',
68
+ 'Do NOT use npm init — use the existing project directory',
69
+ ],
70
+ },
55
71
  commands: CLI_COMMANDS,
56
72
  mcp_tools: TOOLS,
57
73
  }
@@ -22,6 +22,12 @@ export interface CatalogCommand {
22
22
  * are generated from this array.
23
23
  */
24
24
  export const CLI_COMMANDS: CatalogCommand[] = [
25
+ // --- Setup ---
26
+ {
27
+ name: 'setup',
28
+ usage: 'livetap setup',
29
+ description: 'Configure .mcp.json for Claude Code and print restart instructions. Run this after npm install.',
30
+ },
25
31
  // --- Daemon ---
26
32
  {
27
33
  name: 'start',
@@ -1,75 +0,0 @@
1
- #!/usr/bin/env bun
2
- /**
3
- * Postinstall script — auto-configures .mcp.json for Claude Code.
4
- * Runs after `bun add livetap` or `npm install livetap`.
5
- */
6
-
7
- import { existsSync, readFileSync, writeFileSync } from 'fs'
8
- import { resolve } from 'path'
9
-
10
- const MCP_ENTRY = {
11
- livetap: {
12
- command: 'bun',
13
- args: [resolve(import.meta.dir, '..', 'src', 'mcp', 'channel.ts')],
14
- },
15
- }
16
-
17
- function findProjectRoot(): string {
18
- // If we're inside node_modules, walk up past it to find the project root
19
- const scriptDir = resolve(import.meta.dir)
20
- const nmIdx = scriptDir.lastIndexOf('node_modules')
21
- if (nmIdx !== -1) {
22
- const root = scriptDir.slice(0, nmIdx).replace(/\/$/, '')
23
- if (existsSync(resolve(root, 'package.json'))) return root
24
- }
25
- // Fallback to cwd (e.g. when running postinstall directly for testing)
26
- return process.cwd()
27
- }
28
-
29
- function run() {
30
- // Skip in CI or when explicitly disabled
31
- if (process.env.CI || process.env.LIVETAP_SKIP_POSTINSTALL) return
32
-
33
- const root = findProjectRoot()
34
- const mcpPath = resolve(root, '.mcp.json')
35
-
36
- let config: any = {}
37
- if (existsSync(mcpPath)) {
38
- try {
39
- config = JSON.parse(readFileSync(mcpPath, 'utf-8'))
40
- } catch {
41
- console.warn('\n ⚠ .mcp.json exists but is malformed. Skipping auto-config.')
42
- console.warn(' Add the livetap entry manually (see below).\n')
43
- printManualInstructions()
44
- return
45
- }
46
- }
47
-
48
- // Don't overwrite if livetap entry already exists
49
- if (config.mcpServers?.livetap) {
50
- console.log('\n ✓ livetap already configured in .mcp.json\n')
51
- printRestartInstructions()
52
- return
53
- }
54
-
55
- // Add livetap entry
56
- if (!config.mcpServers) config.mcpServers = {}
57
- config.mcpServers.livetap = MCP_ENTRY.livetap
58
- writeFileSync(mcpPath, JSON.stringify(config, null, 2) + '\n')
59
-
60
- console.log('\n ✓ livetap added to .mcp.json\n')
61
- printRestartInstructions()
62
- }
63
-
64
- function printRestartInstructions() {
65
- console.log(' To enable live data streaming in Claude Code, restart with:\n')
66
- console.log(' claude --dangerously-load-development-channels server:livetap\n')
67
- console.log(' Then ask Claude: "Connect to mqtt://broker.emqx.io:1883/sensors/#"\n')
68
- }
69
-
70
- function printManualInstructions() {
71
- console.log(' Add to .mcp.json:\n')
72
- console.log(' ' + JSON.stringify({ mcpServers: MCP_ENTRY }, null, 2).split('\n').join('\n ') + '\n')
73
- }
74
-
75
- run()