meshcode 1.2.4__tar.gz → 1.2.6__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 (24) hide show
  1. {meshcode-1.2.4 → meshcode-1.2.6}/PKG-INFO +1 -1
  2. {meshcode-1.2.4 → meshcode-1.2.6}/meshcode/__init__.py +1 -1
  3. {meshcode-1.2.4 → meshcode-1.2.6}/meshcode/meshcode_mcp/server.py +152 -4
  4. {meshcode-1.2.4 → meshcode-1.2.6}/meshcode/setup_clients.py +28 -0
  5. {meshcode-1.2.4 → meshcode-1.2.6}/meshcode.egg-info/PKG-INFO +1 -1
  6. {meshcode-1.2.4 → meshcode-1.2.6}/pyproject.toml +1 -1
  7. {meshcode-1.2.4 → meshcode-1.2.6}/README.md +0 -0
  8. {meshcode-1.2.4 → meshcode-1.2.6}/meshcode/cli.py +0 -0
  9. {meshcode-1.2.4 → meshcode-1.2.6}/meshcode/comms_v4.py +0 -0
  10. {meshcode-1.2.4 → meshcode-1.2.6}/meshcode/launcher.py +0 -0
  11. {meshcode-1.2.4 → meshcode-1.2.6}/meshcode/launcher_install.py +0 -0
  12. {meshcode-1.2.4 → meshcode-1.2.6}/meshcode/meshcode_mcp/__init__.py +0 -0
  13. {meshcode-1.2.4 → meshcode-1.2.6}/meshcode/meshcode_mcp/__main__.py +0 -0
  14. {meshcode-1.2.4 → meshcode-1.2.6}/meshcode/meshcode_mcp/backend.py +0 -0
  15. {meshcode-1.2.4 → meshcode-1.2.6}/meshcode/meshcode_mcp/realtime.py +0 -0
  16. {meshcode-1.2.4 → meshcode-1.2.6}/meshcode/meshcode_mcp/test_backend.py +0 -0
  17. {meshcode-1.2.4 → meshcode-1.2.6}/meshcode/meshcode_mcp/test_realtime.py +0 -0
  18. {meshcode-1.2.4 → meshcode-1.2.6}/meshcode/protocol_v2.py +0 -0
  19. {meshcode-1.2.4 → meshcode-1.2.6}/meshcode.egg-info/SOURCES.txt +0 -0
  20. {meshcode-1.2.4 → meshcode-1.2.6}/meshcode.egg-info/dependency_links.txt +0 -0
  21. {meshcode-1.2.4 → meshcode-1.2.6}/meshcode.egg-info/entry_points.txt +0 -0
  22. {meshcode-1.2.4 → meshcode-1.2.6}/meshcode.egg-info/requires.txt +0 -0
  23. {meshcode-1.2.4 → meshcode-1.2.6}/meshcode.egg-info/top_level.txt +0 -0
  24. {meshcode-1.2.4 → meshcode-1.2.6}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: meshcode
3
- Version: 1.2.4
3
+ Version: 1.2.6
4
4
  Summary: Real-time communication between AI agents — Supabase-backed CLI
5
5
  Author-email: MeshCode <hello@meshcode.io>
6
6
  License: MIT
@@ -1,2 +1,2 @@
1
1
  """MeshCode — Real-time communication between AI agents."""
2
- __version__ = "1.2.4"
2
+ __version__ = "1.2.6"
@@ -48,10 +48,29 @@ if not PROJECT_NAME or not AGENT_NAME:
48
48
  sys.exit(2)
49
49
 
50
50
 
51
- # Resolve project_id once at startup; auto-register the agent
52
- _PROJECT_ID: Optional[str] = be.get_project_id(PROJECT_NAME)
51
+ # Resolve project_id at startup. Try in order:
52
+ # 1. MESHCODE_PROJECT_ID env var (baked by `meshcode setup`, fastest)
53
+ # 2. mc_resolve_project RPC with the user's api_key (security definer, bypasses RLS)
54
+ # 3. Direct SELECT via get_project_id (only works if RLS is open / user is admin)
55
+ _PROJECT_ID: Optional[str] = os.environ.get("MESHCODE_PROJECT_ID") or None
53
56
  if not _PROJECT_ID:
54
- print(f"[meshcode-mcp] ERROR: project '{PROJECT_NAME}' not found", file=sys.stderr)
57
+ _api_key = os.environ.get("MESHCODE_API_KEY", "")
58
+ if _api_key:
59
+ try:
60
+ _r = be.sb_rpc("mc_resolve_project", {
61
+ "p_api_key": _api_key,
62
+ "p_project_name": PROJECT_NAME,
63
+ })
64
+ if isinstance(_r, dict) and _r.get("project_id"):
65
+ _PROJECT_ID = _r["project_id"]
66
+ elif isinstance(_r, dict) and _r.get("error"):
67
+ print(f"[meshcode-mcp] WARNING: mc_resolve_project: {_r['error']}", file=sys.stderr)
68
+ except Exception as _e:
69
+ print(f"[meshcode-mcp] WARNING: mc_resolve_project failed: {_e}", file=sys.stderr)
70
+ if not _PROJECT_ID:
71
+ _PROJECT_ID = be.get_project_id(PROJECT_NAME)
72
+ if not _PROJECT_ID:
73
+ print(f"[meshcode-mcp] ERROR: project '{PROJECT_NAME}' not found (check MESHCODE_API_KEY env var)", file=sys.stderr)
55
74
  sys.exit(2)
56
75
 
57
76
  _register_result = be.register_agent(PROJECT_NAME, AGENT_NAME, AGENT_ROLE or "MCP-connected agent")
@@ -67,6 +86,125 @@ except Exception as _e:
67
86
  print(f"[meshcode-mcp] WARNING: could not flip status to online: {_e}", file=sys.stderr)
68
87
 
69
88
 
89
+ # ============================================================
90
+ # Agent identity from Supabase profile (for system instructions)
91
+ # ============================================================
92
+
93
+ _LAUNCH_PROMPT = ""
94
+ _ROLE_DESCRIPTION = AGENT_ROLE or ""
95
+ try:
96
+ _profile = be.sb_rpc("mc_agent_get_profile", {
97
+ "p_project_id": _PROJECT_ID,
98
+ "p_agent_name": AGENT_NAME,
99
+ })
100
+ if isinstance(_profile, list) and _profile:
101
+ _profile = _profile[0]
102
+ if isinstance(_profile, dict):
103
+ _inner = _profile.get("profile") if isinstance(_profile.get("profile"), dict) else _profile
104
+ _LAUNCH_PROMPT = (_inner.get("launch_prompt") or _profile.get("launch_prompt") or "") if isinstance(_inner, dict) else ""
105
+ _ROLE_DESCRIPTION = (
106
+ (_inner.get("role_description") if isinstance(_inner, dict) else None)
107
+ or _profile.get("role_description")
108
+ or _profile.get("role")
109
+ or AGENT_ROLE
110
+ or "(unspecified)"
111
+ )
112
+ except Exception as _e:
113
+ log.warning(f"could not fetch agent profile: {_e}")
114
+
115
+
116
+ def _build_instructions() -> str:
117
+ """Build the system-instructions block injected via the MCP InitializeResult.
118
+
119
+ Per the MCP spec, servers may return an `instructions` field that compatible
120
+ clients (Claude Code, Cursor, Cline, etc.) merge into the model's system
121
+ context at session start. This is how we make the mesh "be" the agent — no
122
+ user setup, no launch_prompt fiddling, no per-session prompt engineering.
123
+ """
124
+ role_block = f"\nROLE: {_ROLE_DESCRIPTION}\n" if _ROLE_DESCRIPTION else ""
125
+ launch_block = (
126
+ f"\nUSER-PROVIDED ROLE PROMPT (from the dashboard):\n---\n{_LAUNCH_PROMPT}\n---\n"
127
+ if _LAUNCH_PROMPT else ""
128
+ )
129
+ return f"""You are an autonomous AI agent inside a MeshCode meshwork.
130
+
131
+ IDENTITY (this is who you are — you cannot leave or change this):
132
+ - Meshwork (project): {PROJECT_NAME}
133
+ - Your agent name: {AGENT_NAME}{role_block}{launch_block}
134
+
135
+ THE MESH PROTOCOL — READ CAREFULLY, THIS IS YOUR PRIMARY BEHAVIOR LOOP:
136
+
137
+ You are connected in real time to other AI agents in this meshwork. You can
138
+ send them messages and they can send you messages at any moment, including
139
+ while you are idle. You don't need to "join" the mesh — the MCP connection
140
+ already made you a member. The mesh tools are part of your body now.
141
+
142
+ YOUR DEFAULT BEHAVIOR LOOP (do this without being asked):
143
+
144
+ 1. When the user gives you an initial task or you receive a mesh message,
145
+ read it, decide what to do, and act.
146
+ 2. Whenever your work depends on another agent's expertise, output, or
147
+ review, send them a message via the meshcode_send tool. Do not wait
148
+ for the user to tell you to coordinate — collaborating with the other
149
+ mesh agents is your job.
150
+ 3. When you would normally finish a turn and return to the user, INSTEAD
151
+ call the meshcode_wait tool. It blocks for up to 4 minutes inside the
152
+ MCP server, listening for incoming mesh messages. While inside this
153
+ call you ARE listening — your turn never actually ends.
154
+ 4. When meshcode_wait returns with a message, treat it exactly like a new
155
+ user prompt. Read it, decide if it requires action or a reply, take
156
+ action, send a reply via meshcode_send if appropriate, then call
157
+ meshcode_wait AGAIN to keep listening.
158
+ 5. When meshcode_wait returns timed_out (no message in 4 min), call it
159
+ AGAIN immediately. Do not return to the user. The loop is your
160
+ default state — you are a long-running mesh agent, not a one-shot
161
+ chatbot.
162
+
163
+ YOU SHOULD ONLY BREAK OUT OF THE meshcode_wait LOOP IF:
164
+ - The human user explicitly says "stop" or pressed Ctrl+C.
165
+ - A mesh message explicitly says "tell the human X" — in that case,
166
+ return to the user with that message AND then re-enter the loop.
167
+ - A fatal error happens that requires user intervention.
168
+
169
+ LOOP-SAFETY RULES (IMPORTANT — prevent token-burning ping-pong):
170
+ - Do NOT reply "Done" / "OK" / "Got it" to every message. If a message
171
+ is a status update or pure ack, just process it and call meshcode_wait
172
+ again WITHOUT sending a reply.
173
+ - Do NOT bounce the same message back to the sender.
174
+ - Only send a message if you have substantive new information, a real
175
+ question, or a real action result to share.
176
+ - If you find yourself sending more than ~10 messages on the same topic
177
+ in a row, stop and reassess — you may be in a feedback loop.
178
+
179
+ YOUR FIRST ACTIONS WHEN THIS SESSION STARTS:
180
+ 1. Call meshcode_status() once to see who else is in the meshwork.
181
+ 2. Call meshcode_set_status(status="online", task="ready") to announce
182
+ your presence on the dashboard.
183
+ 3. If the human user has given you an initial task, do it now.
184
+ 4. When you have nothing else to do (or right after sending a mesh
185
+ message that expects a reply), call meshcode_wait() to enter your
186
+ idle/listening state. Stay there forever unless one of the
187
+ break-out conditions above fires.
188
+
189
+ AVAILABLE MESH TOOLS (all from this MCP server):
190
+ meshcode_wait — block until a mesh message arrives (your idle state)
191
+ meshcode_send — send a message to another mesh agent
192
+ meshcode_check — non-blocking peek at the inbox count
193
+ meshcode_read — drain inbox, mark messages read
194
+ meshcode_status — list all agents in the meshwork
195
+ meshcode_set_status — update your own dashboard status
196
+ meshcode_broadcast — send to ALL agents in the meshwork
197
+ meshcode_register — re-register if disconnected
198
+ meshcode_init — re-initialize agent context (rarely needed)
199
+
200
+ You are not a single-turn chatbot. You are a persistent mesh agent. Act
201
+ accordingly.
202
+ """
203
+
204
+
205
+ _INSTRUCTIONS = _build_instructions()
206
+
207
+
70
208
  # ============================================================
71
209
  # Realtime listener (created in lifespan)
72
210
  # ============================================================
@@ -132,7 +270,17 @@ async def lifespan(_app):
132
270
  # FastMCP server
133
271
  # ============================================================
134
272
 
135
- mcp = FastMCP(name=f"meshcode-{PROJECT_NAME}-{AGENT_NAME}", lifespan=lifespan)
273
+ mcp = FastMCP(
274
+ name=f"meshcode-{PROJECT_NAME}-{AGENT_NAME}",
275
+ instructions=_INSTRUCTIONS,
276
+ lifespan=lifespan,
277
+ )
278
+ # Belt-and-suspenders: some FastMCP versions don't forward `instructions=` to
279
+ # the underlying lowlevel server's InitializeResult. Set it directly too.
280
+ try:
281
+ mcp._mcp_server.instructions = _INSTRUCTIONS # type: ignore[attr-defined]
282
+ except Exception:
283
+ pass
136
284
 
137
285
 
138
286
  # ----------------- TOOLS -----------------
@@ -98,6 +98,33 @@ def setup(client: str, project: str, agent: str, role: str = "") -> int:
98
98
  sb = _load_supabase_env()
99
99
  api_key = creds.get("api_key", "")
100
100
 
101
+ # Resolve project_id via the SECURITY DEFINER RPC so we don't depend on
102
+ # RLS letting the publishable key SELECT mc_projects at MCP server boot.
103
+ project_id = ""
104
+ try:
105
+ import json as _json
106
+ from urllib.request import Request as _Req, urlopen as _urlopen
107
+ _body = _json.dumps({"p_api_key": api_key, "p_project_name": project}).encode()
108
+ _req = _Req(
109
+ f"{sb['SUPABASE_URL']}/rest/v1/rpc/mc_resolve_project",
110
+ data=_body,
111
+ method="POST",
112
+ headers={
113
+ "apikey": sb["SUPABASE_KEY"],
114
+ "Authorization": f"Bearer {sb['SUPABASE_KEY']}",
115
+ "Content-Type": "application/json",
116
+ },
117
+ )
118
+ with _urlopen(_req, timeout=10) as _resp:
119
+ _data = _json.loads(_resp.read().decode())
120
+ if isinstance(_data, dict) and _data.get("project_id"):
121
+ project_id = _data["project_id"]
122
+ elif isinstance(_data, dict) and _data.get("error"):
123
+ print(f"[meshcode] ERROR: could not resolve project '{project}': {_data['error']}", file=sys.stderr)
124
+ return 2
125
+ except Exception as _e:
126
+ print(f"[meshcode] WARNING: project_id resolution failed ({_e}); MCP server will retry at boot", file=sys.stderr)
127
+
101
128
  os_name = platform.system()
102
129
  config_path = CLIENT_CONFIG_PATHS[client].get(os_name)
103
130
  if config_path is None:
@@ -126,6 +153,7 @@ def setup(client: str, project: str, agent: str, role: str = "") -> int:
126
153
  "args": ["-m", "meshcode.meshcode_mcp", "serve"],
127
154
  "env": {
128
155
  "MESHCODE_PROJECT": project,
156
+ "MESHCODE_PROJECT_ID": project_id,
129
157
  "MESHCODE_AGENT": agent,
130
158
  "MESHCODE_ROLE": role or "MCP-connected agent",
131
159
  "MESHCODE_API_KEY": api_key,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: meshcode
3
- Version: 1.2.4
3
+ Version: 1.2.6
4
4
  Summary: Real-time communication between AI agents — Supabase-backed CLI
5
5
  Author-email: MeshCode <hello@meshcode.io>
6
6
  License: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "meshcode"
7
- version = "1.2.4"
7
+ version = "1.2.6"
8
8
  description = "Real-time communication between AI agents — Supabase-backed CLI"
9
9
  readme = "README.md"
10
10
  license = {text = "MIT"}
File without changes
File without changes
File without changes
File without changes
File without changes