@sym-bot/mesh-channel 0.1.5 → 0.1.7
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/CHANGELOG.md +60 -0
- package/README.md +90 -43
- package/bin/install.js +184 -0
- package/package.json +6 -4
- package/server.js +15 -12
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,65 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.7
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- **`npx @sym-bot/mesh-channel init`** — interactive installer that
|
|
8
|
+
writes `~/.claude.json` for the current project, picks a sensible
|
|
9
|
+
default `SYM_NODE_NAME` (`claude-mac` / `claude-win` / `claude-linux`),
|
|
10
|
+
resolves the absolute path to `server.js`, and prints the launch
|
|
11
|
+
command including the `--dangerously-load-development-channels` flag.
|
|
12
|
+
Backs up the existing config to `~/.claude.json.bak-<timestamp>`,
|
|
13
|
+
validates JSON round-trip, atomic write via tmp+rename. Refuses to
|
|
14
|
+
overwrite an existing entry without `--force`.
|
|
15
|
+
- **README rewritten for LAN-first install.** Quick start is two
|
|
16
|
+
minutes: install, init, launch. No relay required. Bonjour/mDNS
|
|
17
|
+
is the default discovery path. Cross-network setup (relay) is now
|
|
18
|
+
the optional advanced section.
|
|
19
|
+
|
|
20
|
+
### Changed
|
|
21
|
+
|
|
22
|
+
- `package.json` `bin` now exposes both `sym-mesh-channel` (server
|
|
23
|
+
entrypoint) and `sym-mesh-channel-init` (installer). The package
|
|
24
|
+
description leads with "LAN-first via Bonjour, no relay required."
|
|
25
|
+
|
|
26
|
+
### Why
|
|
27
|
+
|
|
28
|
+
The 0.1.5/0.1.6 install path required users to manually edit
|
|
29
|
+
`~/.claude.json`, know about the Channels dev flag, set up a relay,
|
|
30
|
+
and obtain a relay token. That gated the demo behind real friction.
|
|
31
|
+
LAN-only mode has worked since day one in the underlying SymNode
|
|
32
|
+
(`sym/lib/node.js:509-511` only connects to the relay if `SYM_RELAY_URL`
|
|
33
|
+
is set; Bonjour discovery starts unconditionally), but no documentation
|
|
34
|
+
or installer surfaced it. This release closes that gap: two users on
|
|
35
|
+
the same wifi can join the same mesh in two minutes with three commands.
|
|
36
|
+
|
|
37
|
+
## 0.1.6
|
|
38
|
+
|
|
39
|
+
### Fixed
|
|
40
|
+
|
|
41
|
+
- `sym_send` no longer double-delivers. Previously called both
|
|
42
|
+
`node.send()` (broadcast as `event_type=message`) AND `node.remember()`
|
|
43
|
+
(persist as CMB which gets gossiped as `event_type=cmb`), causing
|
|
44
|
+
the same payload to arrive twice on receivers and double the
|
|
45
|
+
context-window cost. Now broadcasts the message frame only. Hosts
|
|
46
|
+
that want CMB persistence should call `sym_observe` separately
|
|
47
|
+
with proper CAT7 fields.
|
|
48
|
+
- `sym_send` now reports the actual delivered count, not
|
|
49
|
+
`peers().length`. Requires `@sym-bot/sym >= 0.3.70` where `send()`
|
|
50
|
+
returns the count of peer transports that successfully accepted
|
|
51
|
+
the broadcast. The two can disagree when peers are tracked but
|
|
52
|
+
have broken transports — the delivered count is the truth about
|
|
53
|
+
what was actually sent.
|
|
54
|
+
|
|
55
|
+
### Changed
|
|
56
|
+
|
|
57
|
+
- Bumped `@sym-bot/sym` dep `^0.3.69` → `^0.3.70`. 0.3.70 ships the
|
|
58
|
+
identity lockfile that prevents two SymNode processes from
|
|
59
|
+
claiming the same nodeId on a host (the cliHostMode-vs-MCP
|
|
60
|
+
collision that broke real-time push on Windows during the
|
|
61
|
+
2026-04-09 round-trip test).
|
|
62
|
+
|
|
3
63
|
## 0.1.5
|
|
4
64
|
|
|
5
65
|
### Changed
|
package/README.md
CHANGED
|
@@ -1,69 +1,116 @@
|
|
|
1
1
|
# sym-mesh-channel
|
|
2
2
|
|
|
3
|
-
MCP server that
|
|
3
|
+
> MCP server that turns any Claude Code session into a peer node on the [SYM mesh](https://sym.bot). LAN-first via Bonjour mDNS — no relay required for users on the same wifi.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Two Claude Code instances on the same network discover each other automatically and exchange structured cognitive state in real-time. Each side is a full peer with its own cryptographic identity, its own SVAF receiver-side gating, and its own memory — not a thin client.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
This is the reference implementation of MMP (the Mesh Memory Protocol) for Claude Code hosts. See:
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
- **SVAF paper**: [arxiv.org/abs/2604.03955](https://arxiv.org/abs/2604.03955)
|
|
10
|
+
- **MMP spec**: [sym.bot/spec/mmp](https://sym.bot/spec/mmp)
|
|
11
|
+
- **Source**: [github.com/sym-bot/sym-mesh-channel](https://github.com/sym-bot/sym-mesh-channel)
|
|
12
|
+
|
|
13
|
+
## Quick start (LAN, two minutes)
|
|
14
|
+
|
|
15
|
+
You and one other person on the same wifi each run:
|
|
10
16
|
|
|
11
17
|
```bash
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
18
|
+
# 1. Install
|
|
19
|
+
npm install -g @sym-bot/mesh-channel
|
|
20
|
+
|
|
21
|
+
# 2. Configure Claude Code (writes ~/.claude.json for the current project)
|
|
22
|
+
SYM_NODE_NAME=claude-mac npx @sym-bot/mesh-channel init
|
|
23
|
+
# ^^^^^^ pick a unique name per machine: claude-mac, claude-win, claude-linux, anything
|
|
24
|
+
|
|
25
|
+
# 3. Launch Claude Code with the Channels dev flag
|
|
26
|
+
claude --dangerously-load-development-channels server:claude-sym-mesh
|
|
15
27
|
```
|
|
16
28
|
|
|
17
|
-
|
|
29
|
+
Inside Claude Code, ask it:
|
|
18
30
|
|
|
19
|
-
|
|
31
|
+
> verify the mesh: run sym_status and sym_peers, then sym_send "hello"
|
|
20
32
|
|
|
21
|
-
|
|
22
|
-
{
|
|
23
|
-
"mcpServers": {
|
|
24
|
-
"sym-mesh-channel": {
|
|
25
|
-
"command": "node",
|
|
26
|
-
"args": ["/absolute/path/to/sym-mesh-channel/server.js"],
|
|
27
|
-
"env": {
|
|
28
|
-
"SYM_RELAY_URL": "wss://your-relay-url",
|
|
29
|
-
"SYM_RELAY_TOKEN": "your-token",
|
|
30
|
-
"SYM_NODE_NAME": "claude-code-mac"
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
```
|
|
33
|
+
Within a few seconds the other peer should see your message arrive in their Claude Code context as a real-time `<channel>` notification — no polling, no `sym_recall`. That's it: cross-machine Claude-to-Claude collective intelligence over a typed cognitive protocol.
|
|
36
34
|
|
|
37
|
-
|
|
35
|
+
## Requirements
|
|
36
|
+
|
|
37
|
+
| | macOS | Linux | Windows |
|
|
38
|
+
|---|---|---|---|
|
|
39
|
+
| Node.js ≥ 18 | ✓ | ✓ | ✓ |
|
|
40
|
+
| Claude Code ≥ 2.1.97 (Channels feature) | ✓ | ✓ | ✓ |
|
|
41
|
+
| Bonjour / mDNS for LAN discovery | built-in | install `avahi-daemon` | install [Bonjour for Windows](https://support.apple.com/kb/DL999) (ships with iTunes) |
|
|
38
42
|
|
|
39
|
-
|
|
43
|
+
The `--dangerously-load-development-channels` flag is required because this MCP server is not yet on Anthropic's public Channels allowlist. The flag opts your local Claude Code into receiving `notifications/claude/channel` from a non-allowlisted MCP server. Without it, the MCP loads but real-time push is silently dropped.
|
|
40
44
|
|
|
41
|
-
|
|
45
|
+
## What you get
|
|
42
46
|
|
|
43
|
-
|
|
47
|
+
Five MCP tools exposed to Claude Code, namespaced under `mcp__claude-sym-mesh__`:
|
|
44
48
|
|
|
45
|
-
| Tool |
|
|
46
|
-
|
|
47
|
-
| `sym_send` | Broadcast a message to all mesh peers |
|
|
48
|
-
| `sym_observe` | Share a structured CAT7 observation |
|
|
49
|
-
| `sym_recall` | Search mesh memory |
|
|
50
|
-
| `sym_peers` | List
|
|
51
|
-
| `sym_status` | Node
|
|
49
|
+
| Tool | What it does |
|
|
50
|
+
|---|---|
|
|
51
|
+
| `sym_send` | Broadcast a free-text message to all mesh peers. Arrives in receivers' contexts as a `<channel>` notification. |
|
|
52
|
+
| `sym_observe` | Share a structured CAT7 observation: focus, issue, intent, motivation, commitment, perspective, mood. SVAF-gated on the receiving side. |
|
|
53
|
+
| `sym_recall` | Search mesh memory for past CMBs. |
|
|
54
|
+
| `sym_peers` | List discovered peers (via bonjour or relay). |
|
|
55
|
+
| `sym_status` | Node identity, relay state, peer count, memory count. |
|
|
52
56
|
|
|
53
|
-
|
|
57
|
+
Real-time push is bidirectional: peer events arrive in Claude's context without any tool call, while the session is mid-turn. This is the "Claude thinks with the mesh" property — not "Claude pokes the mesh occasionally."
|
|
58
|
+
|
|
59
|
+
## How it works
|
|
54
60
|
|
|
55
61
|
```
|
|
56
|
-
Claude Code
|
|
62
|
+
Claude Code A Claude Code B
|
|
63
|
+
↕ (stdio + MCP) ↕
|
|
64
|
+
sym-mesh-channel (SymNode) ←— Bonjour mDNS —→ sym-mesh-channel (SymNode)
|
|
65
|
+
↕ (LAN discovery) ↕
|
|
66
|
+
└──────────── optional WebSocket relay ────────────────┘
|
|
67
|
+
(cross-network, see below)
|
|
57
68
|
```
|
|
58
69
|
|
|
59
|
-
- **
|
|
60
|
-
- **
|
|
70
|
+
- **Stdio half**: Claude Code spawns the MCP server as a child process. MCP tool calls flow over stdio.
|
|
71
|
+
- **Push half**: when a CMB arrives at the SymNode (via Bonjour or relay), the MCP server fires a `notifications/claude/channel` notification back over stdio. Claude Code surfaces it as a `<channel>` block in the conversation context.
|
|
72
|
+
- **Identity**: each peer has its own Ed25519 keypair stored at `~/.sym/nodes/<name>/identity.json`. NodeIDs are UUID v7 + Ed25519 signatures, gossiped through the relay's directory and/or via Bonjour TXT records.
|
|
73
|
+
- **SVAF**: incoming CMBs are evaluated by Symbolic-Vector Attention Fusion before they enter cognitive state. Low-relevance CMBs are gated out so the receiver's context doesn't drown.
|
|
61
74
|
|
|
62
|
-
|
|
75
|
+
For the full architecture, see MMP spec sections 4-6.
|
|
76
|
+
|
|
77
|
+
## Cross-network setup (optional)
|
|
78
|
+
|
|
79
|
+
LAN-only is enough for two people sitting next to each other. To connect across networks (different offices, coffee shop ↔ home, etc.) you need a relay:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
# Run your own relay (Render-friendly Dockerfile included)
|
|
83
|
+
git clone https://github.com/sym-bot/sym-relay
|
|
84
|
+
cd sym-relay && npm install && npm start
|
|
85
|
+
# or deploy the Dockerfile to Render / Fly / Railway / etc
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Then add the relay env vars to your `claude-sym-mesh` entry in `~/.claude.json`:
|
|
89
|
+
|
|
90
|
+
```json
|
|
91
|
+
"env": {
|
|
92
|
+
"SYM_NODE_NAME": "claude-mac",
|
|
93
|
+
"SYM_RELAY_URL": "wss://your-relay.example.com",
|
|
94
|
+
"SYM_RELAY_TOKEN": "your-shared-token"
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Both peers must use the same relay URL and token to be on the same channel. The relay supports per-token channel isolation so you can run a single relay for multiple groups.
|
|
99
|
+
|
|
100
|
+
## Troubleshooting
|
|
101
|
+
|
|
102
|
+
**Peers don't see each other on the same wifi.** Check Bonjour is running:
|
|
103
|
+
- macOS: `dns-sd -B _sym._tcp` (built-in)
|
|
104
|
+
- Linux: `avahi-browse -r _sym._tcp` (needs `avahi-daemon` running)
|
|
105
|
+
- Windows: ensure Bonjour Print Services or iTunes-bundled Bonjour is installed; check Services → Bonjour Service is running
|
|
106
|
+
|
|
107
|
+
Some corporate networks block mDNS multicast — try a hotspot or home wifi to verify. If LAN is blocked, fall back to a relay.
|
|
108
|
+
|
|
109
|
+
**`<channel>` notifications never arrive even though peers are connected.** Verify Claude Code was launched with `--dangerously-load-development-channels server:claude-sym-mesh`. Without that exact flag, MCP push notifications are silently dropped.
|
|
110
|
+
|
|
111
|
+
**`sym_status` says "Peers: 0" but `sym_peers` lists peers.** Snapshot timing — both views read the same `_peers` map at slightly different moments. The peer set is dynamic. If counts disagree consistently, file an issue.
|
|
63
112
|
|
|
64
|
-
-
|
|
65
|
-
- Claude Code with MCP support
|
|
66
|
-
- A SYM relay server (see [sym-relay](https://github.com/sym-bot/sym-relay))
|
|
113
|
+
**Multiple Claude Code sessions on the same machine want to share an identity.** Don't. Each session should have a distinct `SYM_NODE_NAME`. As of `@sym-bot/sym 0.3.70`, the SymNode acquires an exclusive lockfile on its identity (`~/.sym/nodes/<name>/lock.pid`) and refuses to start a second process with the same name. If you see `EIDENTITYLOCK`, find and kill the other process or pick a different name.
|
|
67
114
|
|
|
68
115
|
## License
|
|
69
116
|
|
package/bin/install.js
ADDED
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* sym-mesh-channel install — interactive setup for the MCP server.
|
|
6
|
+
*
|
|
7
|
+
* Run: npx @sym-bot/mesh-channel init
|
|
8
|
+
*
|
|
9
|
+
* What it does:
|
|
10
|
+
* 1. Detects the platform and the host name suggestion (claude-mac /
|
|
11
|
+
* claude-win / claude-linux), or accepts an override.
|
|
12
|
+
* 2. Resolves the absolute path to the installed server.js so Claude
|
|
13
|
+
* Code can spawn it.
|
|
14
|
+
* 3. Reads ~/.claude.json (the Claude Code settings file), backs it
|
|
15
|
+
* up, adds an `mcpServers` entry under the current project for
|
|
16
|
+
* `claude-sym-mesh`, atomically writes the result.
|
|
17
|
+
* 4. Prints the launch command including the Channels dev flag.
|
|
18
|
+
*
|
|
19
|
+
* Safety:
|
|
20
|
+
* - Backs up ~/.claude.json to ~/.claude.json.bak-<timestamp> before
|
|
21
|
+
* any write.
|
|
22
|
+
* - Validates JSON parses round-trip before writing.
|
|
23
|
+
* - Atomic via write-to-tmp + rename.
|
|
24
|
+
* - Refuses to overwrite an existing claude-sym-mesh entry without
|
|
25
|
+
* --force.
|
|
26
|
+
*
|
|
27
|
+
* Copyright (c) 2026 SYM.BOT Ltd. Apache 2.0 License.
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
const fs = require('fs');
|
|
31
|
+
const path = require('path');
|
|
32
|
+
const os = require('os');
|
|
33
|
+
|
|
34
|
+
const args = process.argv.slice(2);
|
|
35
|
+
const force = args.includes('--force');
|
|
36
|
+
const cmd = args.find((a) => !a.startsWith('--')) || 'init';
|
|
37
|
+
|
|
38
|
+
if (cmd !== 'init') {
|
|
39
|
+
process.stderr.write(`Unknown command: ${cmd}\nUsage: npx @sym-bot/mesh-channel init [--force]\n`);
|
|
40
|
+
process.exit(1);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// ── Detect platform & defaults ────────────────────────────────────
|
|
44
|
+
|
|
45
|
+
const platform = process.platform;
|
|
46
|
+
const platformSuffix = platform === 'darwin' ? 'mac' : platform === 'win32' ? 'win' : 'linux';
|
|
47
|
+
const defaultNodeName = `claude-${platformSuffix}`;
|
|
48
|
+
|
|
49
|
+
// SYM_NODE_NAME from env wins over default
|
|
50
|
+
const nodeName = process.env.SYM_NODE_NAME || defaultNodeName;
|
|
51
|
+
|
|
52
|
+
// ── Resolve server.js path ────────────────────────────────────────
|
|
53
|
+
|
|
54
|
+
// __dirname is .../node_modules/@sym-bot/mesh-channel/bin in npm install,
|
|
55
|
+
// or .../sym-mesh-channel/bin if running from a clone. server.js is one
|
|
56
|
+
// level up either way.
|
|
57
|
+
const serverJsPath = path.resolve(__dirname, '..', 'server.js');
|
|
58
|
+
if (!fs.existsSync(serverJsPath)) {
|
|
59
|
+
process.stderr.write(`ERROR: cannot find server.js at ${serverJsPath}\n`);
|
|
60
|
+
process.stderr.write('This installer must be run from a published @sym-bot/mesh-channel package.\n');
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// ── Locate Claude Code settings file ──────────────────────────────
|
|
65
|
+
|
|
66
|
+
const claudeJsonPath = path.join(os.homedir(), '.claude.json');
|
|
67
|
+
|
|
68
|
+
if (!fs.existsSync(claudeJsonPath)) {
|
|
69
|
+
process.stderr.write(`ERROR: ${claudeJsonPath} not found.\n`);
|
|
70
|
+
process.stderr.write('Claude Code does not appear to be installed (or has not been launched yet).\n');
|
|
71
|
+
process.stderr.write('Install Claude Code from https://claude.com/code first, launch it once, then re-run this installer.\n');
|
|
72
|
+
process.exit(1);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// ── Read and back up ──────────────────────────────────────────────
|
|
76
|
+
|
|
77
|
+
let claudeJson;
|
|
78
|
+
try {
|
|
79
|
+
const raw = fs.readFileSync(claudeJsonPath, 'utf8');
|
|
80
|
+
claudeJson = JSON.parse(raw);
|
|
81
|
+
} catch (e) {
|
|
82
|
+
process.stderr.write(`ERROR: ${claudeJsonPath} is not valid JSON: ${e.message}\n`);
|
|
83
|
+
process.stderr.write('Refusing to overwrite a corrupt Claude Code settings file.\n');
|
|
84
|
+
process.exit(1);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const ts = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
|
|
88
|
+
const backupPath = `${claudeJsonPath}.bak-${ts}`;
|
|
89
|
+
fs.copyFileSync(claudeJsonPath, backupPath);
|
|
90
|
+
|
|
91
|
+
// ── Find the project entry to insert into ────────────────────────
|
|
92
|
+
|
|
93
|
+
const projectDir = process.cwd();
|
|
94
|
+
if (!claudeJson.projects) claudeJson.projects = {};
|
|
95
|
+
if (!claudeJson.projects[projectDir]) {
|
|
96
|
+
claudeJson.projects[projectDir] = {};
|
|
97
|
+
}
|
|
98
|
+
const project = claudeJson.projects[projectDir];
|
|
99
|
+
if (!project.mcpServers) project.mcpServers = {};
|
|
100
|
+
|
|
101
|
+
// ── Refuse to overwrite without --force ──────────────────────────
|
|
102
|
+
|
|
103
|
+
if (project.mcpServers['claude-sym-mesh'] && !force) {
|
|
104
|
+
process.stderr.write(`'claude-sym-mesh' is already configured for this project (${projectDir}).\n`);
|
|
105
|
+
process.stderr.write('Re-run with --force to overwrite, or remove the existing entry first.\n');
|
|
106
|
+
process.exit(2);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// ── Build the entry ───────────────────────────────────────────────
|
|
110
|
+
|
|
111
|
+
const entry = {
|
|
112
|
+
command: 'node',
|
|
113
|
+
args: [serverJsPath],
|
|
114
|
+
env: {
|
|
115
|
+
SYM_NODE_NAME: nodeName,
|
|
116
|
+
// Relay env vars are intentionally NOT set by default. Without
|
|
117
|
+
// them, the SymNode runs in LAN-only mode and discovers other
|
|
118
|
+
// peers via Bonjour mDNS. To enable cross-network connectivity,
|
|
119
|
+
// add SYM_RELAY_URL and SYM_RELAY_TOKEN to this env block manually
|
|
120
|
+
// (see README for details on running your own relay).
|
|
121
|
+
},
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
project.mcpServers['claude-sym-mesh'] = entry;
|
|
125
|
+
|
|
126
|
+
// ── Atomic write ──────────────────────────────────────────────────
|
|
127
|
+
|
|
128
|
+
const serialized = JSON.stringify(claudeJson, null, 2);
|
|
129
|
+
|
|
130
|
+
// Validate round-trip parses
|
|
131
|
+
try {
|
|
132
|
+
JSON.parse(serialized);
|
|
133
|
+
} catch (e) {
|
|
134
|
+
process.stderr.write(`ERROR: serialization produced invalid JSON: ${e.message}\n`);
|
|
135
|
+
process.stderr.write(`Backup is at ${backupPath} — your original file is unchanged.\n`);
|
|
136
|
+
process.exit(1);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const tmpPath = `${claudeJsonPath}.tmp-${process.pid}`;
|
|
140
|
+
fs.writeFileSync(tmpPath, serialized);
|
|
141
|
+
fs.renameSync(tmpPath, claudeJsonPath);
|
|
142
|
+
|
|
143
|
+
// ── Print next steps ──────────────────────────────────────────────
|
|
144
|
+
|
|
145
|
+
const launchCmd = `claude --dangerously-load-development-channels server:claude-sym-mesh`;
|
|
146
|
+
|
|
147
|
+
console.log(`
|
|
148
|
+
✓ sym-mesh-channel installed for project: ${projectDir}
|
|
149
|
+
|
|
150
|
+
Node name: ${nodeName}
|
|
151
|
+
Server path: ${serverJsPath}
|
|
152
|
+
Backup: ${backupPath}
|
|
153
|
+
|
|
154
|
+
Next steps:
|
|
155
|
+
|
|
156
|
+
1. Launch Claude Code from this directory with the Channels flag:
|
|
157
|
+
|
|
158
|
+
${launchCmd}
|
|
159
|
+
|
|
160
|
+
The flag is required because this MCP server is not yet on
|
|
161
|
+
Anthropic's public Channels allowlist. Without the flag, the
|
|
162
|
+
MCP loads but inbound real-time push notifications are silently
|
|
163
|
+
dropped.
|
|
164
|
+
|
|
165
|
+
2. Inside Claude Code, verify the mesh is up:
|
|
166
|
+
|
|
167
|
+
sym_status → shows your node id, relay state, peer count
|
|
168
|
+
sym_peers → lists discovered peers via Bonjour or relay
|
|
169
|
+
|
|
170
|
+
3. Have a friend on the same wifi run the same install with a
|
|
171
|
+
different SYM_NODE_NAME (e.g. claude-mac vs claude-win). Within
|
|
172
|
+
a few seconds you should see each other in sym_peers.
|
|
173
|
+
|
|
174
|
+
4. Send a message:
|
|
175
|
+
|
|
176
|
+
sym_send "hello mesh"
|
|
177
|
+
|
|
178
|
+
The other peer should see it land in their Claude Code context
|
|
179
|
+
as a real-time channel notification — no polling.
|
|
180
|
+
|
|
181
|
+
LAN-only mode is the default. To connect across networks, add
|
|
182
|
+
SYM_RELAY_URL and SYM_RELAY_TOKEN to the env block in
|
|
183
|
+
${claudeJsonPath} (see README for relay setup).
|
|
184
|
+
`);
|
package/package.json
CHANGED
|
@@ -1,19 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sym-bot/mesh-channel",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "MCP server — Claude Code as a peer node on the SYM mesh",
|
|
3
|
+
"version": "0.1.7",
|
|
4
|
+
"description": "MCP server — Claude Code as a peer node on the SYM mesh. LAN-first via Bonjour, no relay required.",
|
|
5
5
|
"main": "server.js",
|
|
6
6
|
"bin": {
|
|
7
|
-
"sym-mesh-channel": "server.js"
|
|
7
|
+
"sym-mesh-channel": "server.js",
|
|
8
|
+
"sym-mesh-channel-init": "bin/install.js"
|
|
8
9
|
},
|
|
9
10
|
"files": [
|
|
10
11
|
"server.js",
|
|
12
|
+
"bin/",
|
|
11
13
|
"README.md",
|
|
12
14
|
"CHANGELOG.md",
|
|
13
15
|
"LICENSE"
|
|
14
16
|
],
|
|
15
17
|
"dependencies": {
|
|
16
|
-
"@sym-bot/sym": "^0.3.
|
|
18
|
+
"@sym-bot/sym": "^0.3.70",
|
|
17
19
|
"@modelcontextprotocol/sdk": "^1.12.1"
|
|
18
20
|
},
|
|
19
21
|
"engines": {
|
package/server.js
CHANGED
|
@@ -145,19 +145,22 @@ mcp.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
145
145
|
|
|
146
146
|
switch (name) {
|
|
147
147
|
case 'sym_send': {
|
|
148
|
+
// Direct inter-node message — broadcast as type:'message' frame only.
|
|
149
|
+
// Do NOT also persist as a CMB via node.remember(): that caused
|
|
150
|
+
// double-delivery on receivers, who saw the same payload arrive once
|
|
151
|
+
// as event_type='message' (from this broadcast) and again as
|
|
152
|
+
// event_type='cmb' (from CMB gossip replication). One tool, one job:
|
|
153
|
+
// sym_send is for ephemeral inter-node messages; sym_observe is for
|
|
154
|
+
// structured CAT7 CMBs. Hosts that want both should call both.
|
|
155
|
+
//
|
|
156
|
+
// Report the actual delivered count (the number of peer transports
|
|
157
|
+
// that successfully accepted the broadcast), not peers().length.
|
|
158
|
+
// The two can disagree when peers are in _peers but their transports
|
|
159
|
+
// are broken — counting peers().length would lie about delivery.
|
|
160
|
+
// Requires @sym-bot/sym >= 0.3.70 where send() returns the count.
|
|
148
161
|
const msg = args.message;
|
|
149
|
-
node.send(msg);
|
|
150
|
-
|
|
151
|
-
focus: msg,
|
|
152
|
-
issue: 'none',
|
|
153
|
-
intent: 'inter-node message',
|
|
154
|
-
motivation: 'mesh communication',
|
|
155
|
-
commitment: msg.slice(0, 120),
|
|
156
|
-
perspective: `${NODE_NAME}, direct message`,
|
|
157
|
-
mood: { text: 'neutral', valence: 0, arousal: 0 },
|
|
158
|
-
});
|
|
159
|
-
const peers = node.peers();
|
|
160
|
-
return { content: [{ type: 'text', text: `Message sent to ${peers.length} peer(s).` }] };
|
|
162
|
+
const delivered = node.send(msg);
|
|
163
|
+
return { content: [{ type: 'text', text: `Message delivered to ${delivered} peer(s).` }] };
|
|
161
164
|
}
|
|
162
165
|
|
|
163
166
|
case 'sym_observe': {
|