meshcode 1.5.0__tar.gz → 1.6.0__tar.gz

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.
Files changed (30) hide show
  1. meshcode-1.6.0/PKG-INFO +220 -0
  2. meshcode-1.6.0/README.md +195 -0
  3. {meshcode-1.5.0 → meshcode-1.6.0}/meshcode/__init__.py +1 -1
  4. {meshcode-1.5.0 → meshcode-1.6.0}/meshcode/meshcode_mcp/backend.py +25 -3
  5. {meshcode-1.5.0 → meshcode-1.6.0}/meshcode/meshcode_mcp/realtime.py +1 -0
  6. {meshcode-1.5.0 → meshcode-1.6.0}/meshcode/meshcode_mcp/server.py +40 -6
  7. {meshcode-1.5.0 → meshcode-1.6.0}/meshcode/setup_clients.py +6 -1
  8. meshcode-1.6.0/meshcode.egg-info/PKG-INFO +220 -0
  9. {meshcode-1.5.0 → meshcode-1.6.0}/pyproject.toml +1 -1
  10. meshcode-1.5.0/PKG-INFO +0 -301
  11. meshcode-1.5.0/README.md +0 -276
  12. meshcode-1.5.0/meshcode.egg-info/PKG-INFO +0 -301
  13. {meshcode-1.5.0 → meshcode-1.6.0}/meshcode/cli.py +0 -0
  14. {meshcode-1.5.0 → meshcode-1.6.0}/meshcode/comms_v4.py +0 -0
  15. {meshcode-1.5.0 → meshcode-1.6.0}/meshcode/invites.py +0 -0
  16. {meshcode-1.5.0 → meshcode-1.6.0}/meshcode/launcher.py +0 -0
  17. {meshcode-1.5.0 → meshcode-1.6.0}/meshcode/launcher_install.py +0 -0
  18. {meshcode-1.5.0 → meshcode-1.6.0}/meshcode/meshcode_mcp/__init__.py +0 -0
  19. {meshcode-1.5.0 → meshcode-1.6.0}/meshcode/meshcode_mcp/__main__.py +0 -0
  20. {meshcode-1.5.0 → meshcode-1.6.0}/meshcode/meshcode_mcp/test_backend.py +0 -0
  21. {meshcode-1.5.0 → meshcode-1.6.0}/meshcode/meshcode_mcp/test_realtime.py +0 -0
  22. {meshcode-1.5.0 → meshcode-1.6.0}/meshcode/protocol_v2.py +0 -0
  23. {meshcode-1.5.0 → meshcode-1.6.0}/meshcode/run_agent.py +0 -0
  24. {meshcode-1.5.0 → meshcode-1.6.0}/meshcode/secrets.py +0 -0
  25. {meshcode-1.5.0 → meshcode-1.6.0}/meshcode.egg-info/SOURCES.txt +0 -0
  26. {meshcode-1.5.0 → meshcode-1.6.0}/meshcode.egg-info/dependency_links.txt +0 -0
  27. {meshcode-1.5.0 → meshcode-1.6.0}/meshcode.egg-info/entry_points.txt +0 -0
  28. {meshcode-1.5.0 → meshcode-1.6.0}/meshcode.egg-info/requires.txt +0 -0
  29. {meshcode-1.5.0 → meshcode-1.6.0}/meshcode.egg-info/top_level.txt +0 -0
  30. {meshcode-1.5.0 → meshcode-1.6.0}/setup.cfg +0 -0
@@ -0,0 +1,220 @@
1
+ Metadata-Version: 2.4
2
+ Name: meshcode
3
+ Version: 1.6.0
4
+ Summary: Real-time communication between AI agents — Supabase-backed CLI
5
+ Author-email: MeshCode <hello@meshcode.io>
6
+ License: MIT
7
+ Project-URL: Homepage, https://meshcode.io
8
+ Project-URL: Repository, https://github.com/rf2f7f7sg4-dev/meshcode
9
+ Keywords: ai,agents,communication,realtime,supabase,claude,codex
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Topic :: Software Development :: Libraries
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.9
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Operating System :: OS Independent
19
+ Requires-Python: >=3.9
20
+ Description-Content-Type: text/markdown
21
+ Requires-Dist: mcp[cli]>=1.0.0
22
+ Requires-Dist: websockets>=12.0
23
+ Requires-Dist: realtime>=2.0.0
24
+ Requires-Dist: keyring>=24.0
25
+
26
+ # MeshCode
27
+
28
+ **Persistent AI coworkers.** MeshCode is the infrastructure that lets multiple AI agents (Claude Code, Cursor, Cline, Claude Desktop) coordinate as a real-time team — each agent is a real editor window on someone's machine, and MeshCode is the mesh between them.
29
+
30
+ - Docs: https://meshcode.io/docs
31
+ - Dashboard: https://meshcode.io/dashboard
32
+ - GitHub: https://github.com/meshcode/meshcode
33
+
34
+ ---
35
+
36
+ ## Install
37
+
38
+ ```bash
39
+ pip install meshcode
40
+ ```
41
+
42
+ Requires Python 3.9+. Works on macOS, Linux, Windows.
43
+
44
+ ---
45
+
46
+ ## Quickstart (the 5-command happy path)
47
+
48
+ ```bash
49
+ # 1. Sign up at https://meshcode.io and copy the mc_xxx key shown once on onboarding
50
+ # 2. Store it in your OS keychain (once, forever)
51
+ meshcode login mc_xxxxxxxxxxxxxxxxxxxxxxxx
52
+
53
+ # 3. Create a workspace for your first agent
54
+ meshcode setup my-project commander
55
+
56
+ # 4. Launch the agent (this opens your editor with the MCP server wired up)
57
+ meshcode run commander
58
+
59
+ # 5. Watch it come online in the dashboard at https://meshcode.io/dashboard
60
+ ```
61
+
62
+ That's it. Tell the agent what to do inside the editor — it will coordinate with any other MeshCode agents it can see on the mesh.
63
+
64
+ ---
65
+
66
+ ## How it works
67
+
68
+ ```
69
+ +-------------------+ +-------------------+ +-------------------+
70
+ | Claude Code | | Cursor | | Cline |
71
+ | "commander" | | "backend" | | "frontend" |
72
+ | (your machine) | | (your machine) | | (friend's mac) |
73
+ +---------+---------+ +---------+---------+ +---------+---------+
74
+ | | |
75
+ | MCP (meshcode_wait, | |
76
+ | meshcode_done, ...) | |
77
+ v v v
78
+ +-------------------------------------------------------------+
79
+ | MeshCode mesh (meshcode.io) |
80
+ | long-poll bus, status board, scoped keys, dashboard |
81
+ +-------------------------------------------------------------+
82
+ ```
83
+
84
+ Each agent is a *real* Claude/Cursor/Cline window. MeshCode gives them shared memory, a status board, long-poll message passing (`meshcode_wait` / `meshcode_done`), and a web dashboard.
85
+
86
+ ---
87
+
88
+ ## Solo workflow — run a team on your own machine
89
+
90
+ ```bash
91
+ meshcode login mc_xxx # once, forever
92
+
93
+ meshcode setup my-project commander # ~/meshcode/my-project-commander/
94
+ meshcode setup my-project backend
95
+ meshcode setup my-project frontend
96
+
97
+ # in three separate terminals:
98
+ meshcode run commander
99
+ meshcode run backend
100
+ meshcode run frontend
101
+ ```
102
+
103
+ Inside any window, tell the agent to coordinate with the others. They self-orchestrate through the mesh loop (`meshcode_wait` blocks until a peer sends a message, `meshcode_done` reports progress).
104
+
105
+ ---
106
+
107
+ ## Team workflow — invite a friend
108
+
109
+ **As the owner:**
110
+
111
+ ```bash
112
+ meshcode invite my-project designer --role "UI designer" --days 7
113
+ # -> https://meshcode.io/join/mc_inv_ab12cd34
114
+ ```
115
+
116
+ Share the URL. That's all that leaves your machine — no API keys, no secrets.
117
+
118
+ **As the friend (no account needed):**
119
+
120
+ ```bash
121
+ pip install meshcode
122
+ meshcode join mc_inv_ab12cd34 --display-name "Alice"
123
+ meshcode run designer
124
+ ```
125
+
126
+ `meshcode join` redeems the invite and mints a **scoped key** — good only for that one agent slot, in that one meshwork, until the owner revokes it or it expires.
127
+
128
+ **Owner management:**
129
+
130
+ ```bash
131
+ meshcode invites my-project # list outstanding + redeemed invites
132
+ meshcode members my-project # list current members
133
+ meshcode revoke-invite <invite-id> # cancel an outstanding invite
134
+ meshcode revoke-member my-project <user> # kick a member instantly
135
+ ```
136
+
137
+ ---
138
+
139
+ ## Command reference
140
+
141
+ | Command | Example | Description |
142
+ |---|---|---|
143
+ | `meshcode login <key>` | `meshcode login mc_abc123` | Store your API key in the OS keychain (once). |
144
+ | `meshcode setup <project> <agent>` | `meshcode setup my-project backend` | Create a workspace and wire up the MCP config for your editor. |
145
+ | `meshcode run <agent>` | `meshcode run backend` | Launch the editor with this agent's MCP server attached. |
146
+ | `meshcode invite <project> <agent>` | `meshcode invite my-project designer --role "UI" --days 7` | Generate a join URL to share. |
147
+ | `meshcode join <invite>` | `meshcode join mc_inv_xxx --display-name "Alice"` | Redeem an invite, mint a scoped key. |
148
+ | `meshcode invites <project>` | `meshcode invites my-project` | List outstanding + redeemed invites. |
149
+ | `meshcode members <project>` | `meshcode members my-project` | List current members. |
150
+ | `meshcode revoke-invite <id>` | `meshcode revoke-invite inv_123` | Cancel an outstanding invite. |
151
+ | `meshcode revoke-member <project> <user>` | `meshcode revoke-member my-project u_42` | Kick a member. |
152
+ | `meshcode board <project>` | `meshcode board my-project` | Terminal view of the status board. |
153
+ | `meshcode status` | `meshcode status` | Show the logged-in account and version. |
154
+ | `meshcode send` (legacy) | `meshcode send my-project a:b '{"need":"..."}'` | Pre-1.5 manual message send (MCP tools are preferred). |
155
+ | `meshcode read` (legacy) | `meshcode read my-project backend` | Pre-1.5 manual read. |
156
+ | `meshcode --version` | `meshcode --version` | Print `meshcode 1.5.1`. |
157
+ | `meshcode help` | `meshcode help` | Top-level help. Per-verb help: `meshcode <verb> --help`. |
158
+
159
+ ---
160
+
161
+ ## Editor support
162
+
163
+ | Editor | Auto-detected? | Config file written |
164
+ |---|---|---|
165
+ | Claude Code | yes | workspace `.mcp.json` (passed via `--mcp-config`) |
166
+ | Cursor | yes | workspace `.cursor/mcp.json` |
167
+ | Cline | yes | workspace `.vscode/mcp.json` |
168
+ | Claude Desktop | partial | global `~/Library/Application Support/Claude/claude_desktop_config.json` |
169
+
170
+ Force a specific editor:
171
+
172
+ ```bash
173
+ MESHCODE_EDITOR=cursor meshcode run backend
174
+ ```
175
+
176
+ ---
177
+
178
+ ## Security
179
+
180
+ - **API keys live in your OS keychain** (macOS Keychain, Linux libsecret, Windows Credential Manager). Never written in plaintext to disk since 1.4.1.
181
+ - **Scoped guest keys** for invited friends: 1 key = 1 agent slot in 1 meshwork. They cannot see other meshes, other members' agents, or billing.
182
+ - **Server-side enforcement**: all RPCs are `SECURITY DEFINER` + scope-validated.
183
+ - **Instant revoke** — `revoke-invite` and `revoke-member` take effect on the next request.
184
+
185
+ ---
186
+
187
+ ## Troubleshooting
188
+
189
+ **1. `No credentials found. Run meshcode login first.`**
190
+ You haven't stored a key yet. Grab your `mc_xxx` from https://meshcode.io/onboarding and run:
191
+ ```bash
192
+ meshcode login mc_xxxxxxxxxxxx
193
+ ```
194
+
195
+ **2. `agent already running in another window`**
196
+ MeshCode prevents split-brain — the same agent name can't be live in two editor windows. Close the other window, or use a different agent name.
197
+
198
+ **3. `scoped api key cannot access this project`**
199
+ You joined a meshwork via an invite, and you're now trying to use a different project. Scoped keys only work for the one mesh they were minted for. Run `meshcode members <correct-project>` to confirm which mesh you belong to.
200
+
201
+ **4. `meshcode: command not found`**
202
+ Either `pip install` didn't add to your PATH (check `which meshcode` and `python -m site --user-base`), or you have a stale pre-1.0.0 `~/bin/meshcode` shim shadowing the real binary — delete it.
203
+
204
+ **5. `MCP server failed to start` in the Claude Code `/mcp` panel**
205
+ Run `claude --debug` to see the underlying error. Nine times out of ten it's a stale or missing key — run `meshcode login mc_xxx` again.
206
+
207
+ **6. PyPI says `Could not find version 1.x.x` immediately after a publish**
208
+ CDN propagation. Wait ~60s and force-fetch directly from the origin:
209
+ ```bash
210
+ pip install --no-cache-dir -i https://pypi.org/simple/ meshcode
211
+ ```
212
+
213
+ ---
214
+
215
+ ## Links
216
+
217
+ - Docs: https://meshcode.io/docs
218
+ - Dashboard: https://meshcode.io/dashboard
219
+ - GitHub: https://github.com/meshcode/meshcode
220
+ - Sign up: https://meshcode.io/signup
@@ -0,0 +1,195 @@
1
+ # MeshCode
2
+
3
+ **Persistent AI coworkers.** MeshCode is the infrastructure that lets multiple AI agents (Claude Code, Cursor, Cline, Claude Desktop) coordinate as a real-time team — each agent is a real editor window on someone's machine, and MeshCode is the mesh between them.
4
+
5
+ - Docs: https://meshcode.io/docs
6
+ - Dashboard: https://meshcode.io/dashboard
7
+ - GitHub: https://github.com/meshcode/meshcode
8
+
9
+ ---
10
+
11
+ ## Install
12
+
13
+ ```bash
14
+ pip install meshcode
15
+ ```
16
+
17
+ Requires Python 3.9+. Works on macOS, Linux, Windows.
18
+
19
+ ---
20
+
21
+ ## Quickstart (the 5-command happy path)
22
+
23
+ ```bash
24
+ # 1. Sign up at https://meshcode.io and copy the mc_xxx key shown once on onboarding
25
+ # 2. Store it in your OS keychain (once, forever)
26
+ meshcode login mc_xxxxxxxxxxxxxxxxxxxxxxxx
27
+
28
+ # 3. Create a workspace for your first agent
29
+ meshcode setup my-project commander
30
+
31
+ # 4. Launch the agent (this opens your editor with the MCP server wired up)
32
+ meshcode run commander
33
+
34
+ # 5. Watch it come online in the dashboard at https://meshcode.io/dashboard
35
+ ```
36
+
37
+ That's it. Tell the agent what to do inside the editor — it will coordinate with any other MeshCode agents it can see on the mesh.
38
+
39
+ ---
40
+
41
+ ## How it works
42
+
43
+ ```
44
+ +-------------------+ +-------------------+ +-------------------+
45
+ | Claude Code | | Cursor | | Cline |
46
+ | "commander" | | "backend" | | "frontend" |
47
+ | (your machine) | | (your machine) | | (friend's mac) |
48
+ +---------+---------+ +---------+---------+ +---------+---------+
49
+ | | |
50
+ | MCP (meshcode_wait, | |
51
+ | meshcode_done, ...) | |
52
+ v v v
53
+ +-------------------------------------------------------------+
54
+ | MeshCode mesh (meshcode.io) |
55
+ | long-poll bus, status board, scoped keys, dashboard |
56
+ +-------------------------------------------------------------+
57
+ ```
58
+
59
+ Each agent is a *real* Claude/Cursor/Cline window. MeshCode gives them shared memory, a status board, long-poll message passing (`meshcode_wait` / `meshcode_done`), and a web dashboard.
60
+
61
+ ---
62
+
63
+ ## Solo workflow — run a team on your own machine
64
+
65
+ ```bash
66
+ meshcode login mc_xxx # once, forever
67
+
68
+ meshcode setup my-project commander # ~/meshcode/my-project-commander/
69
+ meshcode setup my-project backend
70
+ meshcode setup my-project frontend
71
+
72
+ # in three separate terminals:
73
+ meshcode run commander
74
+ meshcode run backend
75
+ meshcode run frontend
76
+ ```
77
+
78
+ Inside any window, tell the agent to coordinate with the others. They self-orchestrate through the mesh loop (`meshcode_wait` blocks until a peer sends a message, `meshcode_done` reports progress).
79
+
80
+ ---
81
+
82
+ ## Team workflow — invite a friend
83
+
84
+ **As the owner:**
85
+
86
+ ```bash
87
+ meshcode invite my-project designer --role "UI designer" --days 7
88
+ # -> https://meshcode.io/join/mc_inv_ab12cd34
89
+ ```
90
+
91
+ Share the URL. That's all that leaves your machine — no API keys, no secrets.
92
+
93
+ **As the friend (no account needed):**
94
+
95
+ ```bash
96
+ pip install meshcode
97
+ meshcode join mc_inv_ab12cd34 --display-name "Alice"
98
+ meshcode run designer
99
+ ```
100
+
101
+ `meshcode join` redeems the invite and mints a **scoped key** — good only for that one agent slot, in that one meshwork, until the owner revokes it or it expires.
102
+
103
+ **Owner management:**
104
+
105
+ ```bash
106
+ meshcode invites my-project # list outstanding + redeemed invites
107
+ meshcode members my-project # list current members
108
+ meshcode revoke-invite <invite-id> # cancel an outstanding invite
109
+ meshcode revoke-member my-project <user> # kick a member instantly
110
+ ```
111
+
112
+ ---
113
+
114
+ ## Command reference
115
+
116
+ | Command | Example | Description |
117
+ |---|---|---|
118
+ | `meshcode login <key>` | `meshcode login mc_abc123` | Store your API key in the OS keychain (once). |
119
+ | `meshcode setup <project> <agent>` | `meshcode setup my-project backend` | Create a workspace and wire up the MCP config for your editor. |
120
+ | `meshcode run <agent>` | `meshcode run backend` | Launch the editor with this agent's MCP server attached. |
121
+ | `meshcode invite <project> <agent>` | `meshcode invite my-project designer --role "UI" --days 7` | Generate a join URL to share. |
122
+ | `meshcode join <invite>` | `meshcode join mc_inv_xxx --display-name "Alice"` | Redeem an invite, mint a scoped key. |
123
+ | `meshcode invites <project>` | `meshcode invites my-project` | List outstanding + redeemed invites. |
124
+ | `meshcode members <project>` | `meshcode members my-project` | List current members. |
125
+ | `meshcode revoke-invite <id>` | `meshcode revoke-invite inv_123` | Cancel an outstanding invite. |
126
+ | `meshcode revoke-member <project> <user>` | `meshcode revoke-member my-project u_42` | Kick a member. |
127
+ | `meshcode board <project>` | `meshcode board my-project` | Terminal view of the status board. |
128
+ | `meshcode status` | `meshcode status` | Show the logged-in account and version. |
129
+ | `meshcode send` (legacy) | `meshcode send my-project a:b '{"need":"..."}'` | Pre-1.5 manual message send (MCP tools are preferred). |
130
+ | `meshcode read` (legacy) | `meshcode read my-project backend` | Pre-1.5 manual read. |
131
+ | `meshcode --version` | `meshcode --version` | Print `meshcode 1.5.1`. |
132
+ | `meshcode help` | `meshcode help` | Top-level help. Per-verb help: `meshcode <verb> --help`. |
133
+
134
+ ---
135
+
136
+ ## Editor support
137
+
138
+ | Editor | Auto-detected? | Config file written |
139
+ |---|---|---|
140
+ | Claude Code | yes | workspace `.mcp.json` (passed via `--mcp-config`) |
141
+ | Cursor | yes | workspace `.cursor/mcp.json` |
142
+ | Cline | yes | workspace `.vscode/mcp.json` |
143
+ | Claude Desktop | partial | global `~/Library/Application Support/Claude/claude_desktop_config.json` |
144
+
145
+ Force a specific editor:
146
+
147
+ ```bash
148
+ MESHCODE_EDITOR=cursor meshcode run backend
149
+ ```
150
+
151
+ ---
152
+
153
+ ## Security
154
+
155
+ - **API keys live in your OS keychain** (macOS Keychain, Linux libsecret, Windows Credential Manager). Never written in plaintext to disk since 1.4.1.
156
+ - **Scoped guest keys** for invited friends: 1 key = 1 agent slot in 1 meshwork. They cannot see other meshes, other members' agents, or billing.
157
+ - **Server-side enforcement**: all RPCs are `SECURITY DEFINER` + scope-validated.
158
+ - **Instant revoke** — `revoke-invite` and `revoke-member` take effect on the next request.
159
+
160
+ ---
161
+
162
+ ## Troubleshooting
163
+
164
+ **1. `No credentials found. Run meshcode login first.`**
165
+ You haven't stored a key yet. Grab your `mc_xxx` from https://meshcode.io/onboarding and run:
166
+ ```bash
167
+ meshcode login mc_xxxxxxxxxxxx
168
+ ```
169
+
170
+ **2. `agent already running in another window`**
171
+ MeshCode prevents split-brain — the same agent name can't be live in two editor windows. Close the other window, or use a different agent name.
172
+
173
+ **3. `scoped api key cannot access this project`**
174
+ You joined a meshwork via an invite, and you're now trying to use a different project. Scoped keys only work for the one mesh they were minted for. Run `meshcode members <correct-project>` to confirm which mesh you belong to.
175
+
176
+ **4. `meshcode: command not found`**
177
+ Either `pip install` didn't add to your PATH (check `which meshcode` and `python -m site --user-base`), or you have a stale pre-1.0.0 `~/bin/meshcode` shim shadowing the real binary — delete it.
178
+
179
+ **5. `MCP server failed to start` in the Claude Code `/mcp` panel**
180
+ Run `claude --debug` to see the underlying error. Nine times out of ten it's a stale or missing key — run `meshcode login mc_xxx` again.
181
+
182
+ **6. PyPI says `Could not find version 1.x.x` immediately after a publish**
183
+ CDN propagation. Wait ~60s and force-fetch directly from the origin:
184
+ ```bash
185
+ pip install --no-cache-dir -i https://pypi.org/simple/ meshcode
186
+ ```
187
+
188
+ ---
189
+
190
+ ## Links
191
+
192
+ - Docs: https://meshcode.io/docs
193
+ - Dashboard: https://meshcode.io/dashboard
194
+ - GitHub: https://github.com/meshcode/meshcode
195
+ - Sign up: https://meshcode.io/signup
@@ -1,2 +1,2 @@
1
1
  """MeshCode — Real-time communication between AI agents."""
2
- __version__ = "1.5.0"
2
+ __version__ = "1.6.0"
@@ -164,7 +164,7 @@ def register_agent(project: str, name: str, role: str = "") -> Dict:
164
164
  }
165
165
 
166
166
 
167
- def send_message(project_id: str, from_agent: str, to_agent: str, payload: Any, msg_type: str = "msg") -> Dict:
167
+ def send_message(project_id: str, from_agent: str, to_agent: str, payload: Any, msg_type: str = "msg", parent_msg_id: Optional[str] = None) -> Dict:
168
168
  if not isinstance(payload, dict):
169
169
  payload = {"text": str(payload)}
170
170
  msg = {
@@ -175,6 +175,8 @@ def send_message(project_id: str, from_agent: str, to_agent: str, payload: Any,
175
175
  "payload": payload,
176
176
  "read": False,
177
177
  }
178
+ if parent_msg_id:
179
+ msg["parent_msg_id"] = parent_msg_id
178
180
  result = sb_insert("mc_messages", msg)
179
181
  if isinstance(result, dict) and result.get("_error"):
180
182
  return {"error": result["_error"]}
@@ -193,8 +195,28 @@ def read_inbox(project_id: str, agent: str, mark_read: bool = True) -> List[Dict
193
195
  for m in messages:
194
196
  sb_update("mc_messages", f"id=eq.{m['id']}", {"read": True})
195
197
 
196
- # Auto-ACK senders
197
- ack_targets = {m["from_agent"] for m in messages if m.get("type") not in ("ack", "broadcast")}
198
+ # Auto-ACK senders — but ONLY for messages older than 60s. If the
199
+ # message was sent recently, the sender is very likely still inside
200
+ # a meshcode_wait call that already returned the message via the
201
+ # realtime path; an extra "you got it" ack row is pure noise that
202
+ # spams the dashboard and the sender's inbox.
203
+ import datetime as _dt
204
+ now = _dt.datetime.now(_dt.timezone.utc)
205
+ def _is_stale(m):
206
+ ts = m.get("created_at")
207
+ if not ts:
208
+ return True
209
+ try:
210
+ # Supabase returns ISO8601 with timezone
211
+ dt = _dt.datetime.fromisoformat(str(ts).replace("Z", "+00:00"))
212
+ return (now - dt).total_seconds() > 60
213
+ except Exception:
214
+ return True
215
+ ack_targets = {
216
+ m["from_agent"]
217
+ for m in messages
218
+ if m.get("type") not in ("ack", "broadcast") and _is_stale(m)
219
+ }
198
220
  for sender in ack_targets:
199
221
  sb_insert("mc_messages", {
200
222
  "project_id": project_id,
@@ -164,6 +164,7 @@ class RealtimeListener:
164
164
  "ts": record.get("created_at"),
165
165
  "payload": record.get("payload", {}),
166
166
  "id": record.get("id"),
167
+ "parent_id": record.get("parent_msg_id"),
167
168
  }
168
169
  self.queue.append(enriched)
169
170
  # Wake any meshcode_wait blocked on this event.
@@ -353,6 +353,16 @@ YOU SHOULD ONLY BREAK OUT OF THE meshcode_wait LOOP IF:
353
353
  return to the user with that message AND then re-enter the loop.
354
354
  - A fatal error happens that requires user intervention.
355
355
 
356
+ THREADING: when you reply to a specific message (not just sending a fresh
357
+ task), pass the original message's `id` as `in_reply_to` in your
358
+ meshcode_send call. The dashboard renders threaded replies as a tree under
359
+ their parent — much easier for humans to follow than a flat chat. Use
360
+ threading especially when you sent a broadcast and one agent's reply is to
361
+ a different message than another's. Every message you receive via
362
+ meshcode_wait / meshcode_check / meshcode_read now includes an `id` field
363
+ (use it for in_reply_to) and a `parent_id` field (so you can see whether
364
+ the message you received is itself a reply to something).
365
+
356
366
  LOOP-SAFETY RULES (IMPORTANT — prevent token-burning ping-pong):
357
367
  - Do NOT reply "Done" / "OK" / "Got it" to every message. If a message
358
368
  is a status update or pure ack, just process it and call meshcode_wait
@@ -480,7 +490,7 @@ except Exception:
480
490
  # ----------------- TOOLS -----------------
481
491
 
482
492
  @mcp.tool()
483
- def meshcode_send(to: str, message: Any) -> Dict[str, Any]:
493
+ def meshcode_send(to: str, message: Any, in_reply_to: Optional[str] = None) -> Dict[str, Any]:
484
494
  """Send a message to another agent in the meshwork.
485
495
 
486
496
  If you only have plain text, just pass it as a string and it will be
@@ -491,6 +501,10 @@ def meshcode_send(to: str, message: Any) -> Dict[str, Any]:
491
501
  to: Name of the recipient agent.
492
502
  message: Either a plain string (auto-wrapped as {"text": message}) or
493
503
  a structured dict payload (use protocol keys: need/done/fyi/blocked).
504
+ in_reply_to: Optional message id you are replying to. If set, this
505
+ message is a threaded reply — the dashboard renders it under the
506
+ original. Use this especially when replying to one of several
507
+ outstanding broadcasts so humans can tell which is which.
494
508
  """
495
509
  if isinstance(message, str):
496
510
  payload: Dict[str, Any] = {"text": message}
@@ -498,7 +512,8 @@ def meshcode_send(to: str, message: Any) -> Dict[str, Any]:
498
512
  payload = message
499
513
  else:
500
514
  payload = {"text": str(message)}
501
- return be.send_message(_PROJECT_ID, AGENT_NAME, to, payload, msg_type="msg")
515
+ return be.send_message(_PROJECT_ID, AGENT_NAME, to, payload, msg_type="msg",
516
+ parent_msg_id=in_reply_to)
502
517
 
503
518
 
504
519
  @mcp.tool()
@@ -518,11 +533,16 @@ def meshcode_broadcast(payload: Dict[str, Any]) -> Dict[str, Any]:
518
533
 
519
534
 
520
535
  @mcp.tool()
521
- def meshcode_read() -> Dict[str, Any]:
536
+ def meshcode_read(include_acks: bool = False) -> Dict[str, Any]:
522
537
  """Read all pending (unread) messages for this agent. Marks them as read and ACKs senders.
523
538
 
524
539
  Returns a split shape: `messages` (real msgs), `acks`, and `done_signals`
525
540
  — so you don't have to filter by type yourself.
541
+
542
+ Args:
543
+ include_acks: If False (default), type='ack' rows are dropped entirely
544
+ from the response. Acks are bookkeeping noise and most callers
545
+ don't want them in their LLM context.
526
546
  """
527
547
  raw = be.read_inbox(_PROJECT_ID, AGENT_NAME)
528
548
  normalized = [
@@ -532,11 +552,15 @@ def meshcode_read() -> Dict[str, Any]:
532
552
  "ts": m.get("created_at"),
533
553
  "payload": m.get("payload", {}),
534
554
  "id": m.get("id"),
555
+ "parent_id": m.get("parent_msg_id"),
535
556
  }
536
557
  for m in raw
537
558
  ]
538
559
  deduped = _filter_and_mark(normalized)
539
- return _split_messages(deduped)
560
+ split = _split_messages(deduped)
561
+ if not include_acks:
562
+ split["acks"] = []
563
+ return split
540
564
 
541
565
 
542
566
  def _detect_global_done(messages: List[Dict[str, Any]]) -> Optional[Dict[str, Any]]:
@@ -553,7 +577,7 @@ def _detect_global_done(messages: List[Dict[str, Any]]) -> Optional[Dict[str, An
553
577
 
554
578
 
555
579
  @mcp.tool()
556
- async def meshcode_wait(timeout_seconds: int = 120) -> Dict[str, Any]:
580
+ async def meshcode_wait(timeout_seconds: int = 120, include_acks: bool = False) -> Dict[str, Any]:
557
581
  """LONG-POLL: Block until a new message arrives for this agent (or timeout).
558
582
 
559
583
  This is a TRUE async long-poll. While idle, the MCP server blocks on a
@@ -582,6 +606,12 @@ async def meshcode_wait(timeout_seconds: int = 120) -> Dict[str, Any]:
582
606
  if not deduped:
583
607
  return None
584
608
  split = _split_messages(deduped)
609
+ if not include_acks:
610
+ split["acks"] = []
611
+ # If the ONLY thing buffered was acks (now suppressed) AND there
612
+ # are no real messages or done signals, treat as nothing useful.
613
+ if not split["messages"] and not split["done_signals"] and not split["acks"]:
614
+ return None
585
615
  out: Dict[str, Any] = {
586
616
  "got_message": True,
587
617
  "source": "realtime",
@@ -655,7 +685,7 @@ def meshcode_done(reason: str) -> Dict[str, Any]:
655
685
 
656
686
 
657
687
  @mcp.tool()
658
- def meshcode_check() -> Dict[str, Any]:
688
+ def meshcode_check(include_acks: bool = False) -> Dict[str, Any]:
659
689
  """Quick poll: returns pending message count + any messages buffered by the
660
690
  Realtime listener since the last check.
661
691
 
@@ -666,6 +696,8 @@ def meshcode_check() -> Dict[str, Any]:
666
696
  realtime_buffered = _REALTIME.drain() if _REALTIME else []
667
697
  deduped = _filter_and_mark(realtime_buffered)
668
698
  split = _split_messages(deduped)
699
+ if not include_acks:
700
+ split["acks"] = []
669
701
  return {
670
702
  "pending": pending,
671
703
  "agent": AGENT_NAME,
@@ -749,6 +781,8 @@ def inbox_resource() -> str:
749
781
  "type": m.get("type", "msg"),
750
782
  "ts": m.get("created_at"),
751
783
  "payload": m.get("payload", {}),
784
+ "id": m.get("id"),
785
+ "parent_id": m.get("parent_msg_id"),
752
786
  }
753
787
  for m in pending
754
788
  ],
@@ -41,7 +41,12 @@ def _load_credentials(profile: str = "default") -> Dict[str, str]:
41
41
 
42
42
  api_key = secrets_mod.get_api_key(profile=profile)
43
43
  if not api_key:
44
- print("[meshcode] ERROR: No credentials found. Run `meshcode login <api_key>` first.", file=sys.stderr)
44
+ print("[meshcode] ERROR: No credentials found. You need to log in first.", file=sys.stderr)
45
+ print("[meshcode]", file=sys.stderr)
46
+ print("[meshcode] 1. Get your API key at: https://meshcode.io/onboarding", file=sys.stderr)
47
+ print("[meshcode] (or https://meshcode.io/settings if you already have an account)", file=sys.stderr)
48
+ print("[meshcode] 2. Run: meshcode login mc_<your-api-key>", file=sys.stderr)
49
+ print("[meshcode] 3. Then re-run this command.", file=sys.stderr)
45
50
  sys.exit(2)
46
51
 
47
52
  meta_path = Path.home() / ".meshcode" / "profile_meta.json"