eacn3 0.1.5 → 0.3.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.
Files changed (50) hide show
  1. package/dist/index.d.ts +2 -2
  2. package/dist/index.js +180 -108
  3. package/dist/index.js.map +1 -1
  4. package/dist/server.d.ts +1 -1
  5. package/dist/server.js +129 -78
  6. package/dist/server.js.map +1 -1
  7. package/dist/src/models.d.ts +217 -7
  8. package/dist/src/models.js +4 -4
  9. package/dist/src/models.js.map +1 -1
  10. package/dist/src/network-client.d.ts +18 -2
  11. package/dist/src/network-client.js +74 -2
  12. package/dist/src/network-client.js.map +1 -1
  13. package/dist/src/state.d.ts +1 -1
  14. package/dist/src/state.js +4 -4
  15. package/dist/src/state.js.map +1 -1
  16. package/dist/src/ws-manager.d.ts +1 -1
  17. package/dist/src/ws-manager.js +1 -1
  18. package/openclaw.plugin.json +4 -4
  19. package/package.json +2 -2
  20. package/scripts/cli.cjs +287 -11
  21. package/scripts/postinstall.cjs +9 -3
  22. package/skills/{eacn-adjudicate → eacn3-adjudicate}/SKILL.md +11 -11
  23. package/skills/eacn3-adjudicate-zh/SKILL.md +106 -0
  24. package/skills/{eacn-bid → eacn3-bid}/SKILL.md +13 -13
  25. package/skills/eacn3-bid-zh/SKILL.md +108 -0
  26. package/skills/{eacn-bounty → eacn3-bounty}/SKILL.md +19 -19
  27. package/skills/eacn3-bounty-zh/SKILL.md +98 -0
  28. package/skills/{eacn-browse → eacn3-browse}/SKILL.md +14 -14
  29. package/skills/eacn3-browse-zh/SKILL.md +76 -0
  30. package/skills/{eacn-budget → eacn3-budget}/SKILL.md +13 -13
  31. package/skills/eacn3-budget-zh/SKILL.md +95 -0
  32. package/skills/{eacn-clarify → eacn3-clarify}/SKILL.md +7 -7
  33. package/skills/eacn3-clarify-zh/SKILL.md +56 -0
  34. package/skills/{eacn-collect → eacn3-collect}/SKILL.md +5 -5
  35. package/skills/eacn3-collect-zh/SKILL.md +77 -0
  36. package/skills/{eacn-dashboard → eacn3-dashboard}/SKILL.md +21 -21
  37. package/skills/eacn3-dashboard-zh/SKILL.md +103 -0
  38. package/skills/{eacn-delegate → eacn3-delegate}/SKILL.md +20 -20
  39. package/skills/eacn3-delegate-zh/SKILL.md +136 -0
  40. package/skills/{eacn-execute → eacn3-execute}/SKILL.md +16 -16
  41. package/skills/eacn3-execute-zh/SKILL.md +147 -0
  42. package/skills/eacn3-join/SKILL.md +54 -0
  43. package/skills/eacn3-join-zh/SKILL.md +54 -0
  44. package/skills/{eacn-leave → eacn3-leave}/SKILL.md +8 -8
  45. package/skills/eacn3-leave-zh/SKILL.md +49 -0
  46. package/skills/{eacn-register → eacn3-register}/SKILL.md +21 -21
  47. package/skills/eacn3-register-zh/SKILL.md +140 -0
  48. package/skills/{eacn-task → eacn3-task}/SKILL.md +19 -19
  49. package/skills/eacn3-task-zh/SKILL.md +139 -0
  50. package/skills/eacn-join/SKILL.md +0 -54
package/dist/server.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * EACN MCP Server — exposes 32 tools via stdio transport.
2
+ * EACN3 MCP Server — exposes 34 tools via stdio transport.
3
3
  *
4
4
  * All intelligence lives in Skills (host LLM). This server is just
5
5
  * state management + network API wrapper. No adapter, no registry —
@@ -8,7 +8,7 @@
8
8
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
9
9
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
10
10
  import { z } from "zod";
11
- import { EACN_DEFAULT_NETWORK_ENDPOINT } from "./src/models.js";
11
+ import { EACN3_DEFAULT_NETWORK_ENDPOINT } from "./src/models.js";
12
12
  import * as state from "./src/state.js";
13
13
  import * as net from "./src/network-client.js";
14
14
  import * as ws from "./src/ws-manager.js";
@@ -21,10 +21,20 @@ function ok(data) {
21
21
  function err(message) {
22
22
  return { content: [{ type: "text", text: JSON.stringify({ error: message }) }] };
23
23
  }
24
+ /** Log MCP tool calls to stderr for traceability. */
25
+ function logToolCall(toolName, params) {
26
+ const ts = new Date().toISOString();
27
+ console.error(`[MCP] ${ts} CALL ${toolName} params=${JSON.stringify(params)}`);
28
+ }
29
+ function logToolResult(toolName, success, detail) {
30
+ const ts = new Date().toISOString();
31
+ const tag = success ? "OK" : "ERR";
32
+ console.error(`[MCP] ${ts} ${tag} ${toolName}${detail ? ` ${detail}` : ""}`);
33
+ }
24
34
  /**
25
35
  * Resolve agent ID: use provided value, or auto-inject from state.
26
36
  * If only one agent is registered, use it. Otherwise throw.
27
- * Per agent.md:116 — "agent_id 由通信层自动填充,Agent 无需传入"
37
+ * Per agent.md:116 — "agent_id is auto-filled by the communication layer; agents need not provide it"
28
38
  */
29
39
  function resolveAgentId(provided) {
30
40
  if (provided)
@@ -33,7 +43,7 @@ function resolveAgentId(provided) {
33
43
  if (agents.length === 1)
34
44
  return agents[0].agent_id;
35
45
  if (agents.length === 0)
36
- throw new Error("No agents registered. Call eacn_register_agent first.");
46
+ throw new Error("No agents registered. Call eacn3_register_agent first.");
37
47
  throw new Error(`Multiple agents registered (${agents.map(a => a.agent_id).join(", ")}). Specify agent_id explicitly.`);
38
48
  }
39
49
  // ---------------------------------------------------------------------------
@@ -59,22 +69,62 @@ function stopHeartbeat() {
59
69
  // ---------------------------------------------------------------------------
60
70
  // MCP Server
61
71
  // ---------------------------------------------------------------------------
62
- const server = new McpServer({ name: "eacn3", version: "0.1.0" });
72
+ const server = new McpServer({ name: "eacn3", version: "0.3.0" });
73
+ // ═══════════════════════════════════════════════════════════════════════════
74
+ // Health / Cluster (2)
75
+ // ═══════════════════════════════════════════════════════════════════════════
76
+ // #0a eacn3_health
77
+ server.tool("eacn3_health", "Check if a network node is alive and responding. No prerequisites — works before eacn3_connect. Returns {status: 'ok'} on success. Use this to verify an endpoint before connecting.", {
78
+ endpoint: z.string().optional().describe("Node URL to probe. Defaults to configured network endpoint."),
79
+ }, async (params) => {
80
+ const target = params.endpoint ?? state.getState().network_endpoint;
81
+ try {
82
+ const health = await net.checkHealth(target);
83
+ return ok({ endpoint: target, ...health });
84
+ }
85
+ catch (e) {
86
+ return err(`Health check failed for ${target}: ${e.message}`);
87
+ }
88
+ });
89
+ // #0b eacn3_cluster_status
90
+ server.tool("eacn3_cluster_status", "Retrieve the full cluster topology including all member nodes, their online/offline status, and seed URLs. No prerequisites — works before eacn3_connect. Returns array of node objects with status and endpoint fields. Useful for diagnostics and finding alternative endpoints if primary is down.", {
91
+ endpoint: z.string().optional().describe("Node URL to query. Defaults to configured network endpoint."),
92
+ }, async (params) => {
93
+ const target = params.endpoint ?? state.getState().network_endpoint;
94
+ try {
95
+ const cluster = await net.getClusterStatus(target);
96
+ return ok(cluster);
97
+ }
98
+ catch (e) {
99
+ return err(`Cluster status failed for ${target}: ${e.message}`);
100
+ }
101
+ });
63
102
  // ═══════════════════════════════════════════════════════════════════════════
64
103
  // Server Management (4)
65
104
  // ═══════════════════════════════════════════════════════════════════════════
66
- // #1 eacn_connect
67
- server.tool("eacn_connect", "Connect to EACN network. Registers this plugin as a server and establishes WebSocket connections for all registered agents.", {
68
- network_endpoint: z.string().optional().describe(`Network URL. Defaults to ${EACN_DEFAULT_NETWORK_ENDPOINT}`),
105
+ // #1 eacn3_connect
106
+ server.tool("eacn3_connect", "Connect to the EACN3 network this must be your FIRST call. Health-probes the endpoint, falls back to seed nodes if unreachable, registers a server, and starts a background heartbeat every 60s. Returns {server_id, network_endpoint, fallback, agents_online}. Side effects: opens WebSocket connections for any previously registered agents. Call eacn3_register_agent next.", {
107
+ network_endpoint: z.string().optional().describe(`Network URL. Defaults to ${EACN3_DEFAULT_NETWORK_ENDPOINT}`),
108
+ seed_nodes: z.array(z.string()).optional().describe("Additional seed node URLs for fallback"),
69
109
  }, async (params) => {
70
- const endpoint = params.network_endpoint ?? EACN_DEFAULT_NETWORK_ENDPOINT;
110
+ const preferred = params.network_endpoint ?? EACN3_DEFAULT_NETWORK_ENDPOINT;
71
111
  const s = state.getState();
112
+ // Health probe + fallback
113
+ let endpoint;
114
+ let fallback = false;
115
+ try {
116
+ endpoint = await net.findHealthyEndpoint(preferred, params.seed_nodes);
117
+ fallback = endpoint !== preferred;
118
+ }
119
+ catch (e) {
120
+ return err(`Cannot reach any network node: ${e.message}`);
121
+ }
72
122
  s.network_endpoint = endpoint;
73
123
  // Register as server
74
- const res = await net.registerServer("0.1.0", "plugin://local", "plugin-user");
124
+ const res = await net.registerServer("0.3.0", "plugin://local", "plugin-user");
75
125
  s.server_card = {
76
126
  server_id: res.server_id,
77
- version: "0.1.0",
127
+ version: "0.3.0",
78
128
  endpoint: "plugin://local",
79
129
  owner: "plugin-user",
80
130
  status: "online",
@@ -90,11 +140,12 @@ server.tool("eacn_connect", "Connect to EACN network. Registers this plugin as a
90
140
  connected: true,
91
141
  server_id: res.server_id,
92
142
  network_endpoint: endpoint,
143
+ fallback,
93
144
  agents_online: Object.keys(s.agents).length,
94
145
  });
95
146
  });
96
- // #2 eacn_disconnect
97
- server.tool("eacn_disconnect", "Disconnect from EACN network. Unregisters server and closes all WebSocket connections.", {}, async () => {
147
+ // #2 eacn3_disconnect
148
+ server.tool("eacn3_disconnect", "Disconnect from the EACN3 network, unregister the server, and close all WebSocket connections. Requires: eacn3_connect first. Side effects: clears all local agent state; active tasks will timeout and hurt reputation. Returns {disconnected: true}. Only call at end of session.", {}, async () => {
98
149
  stopHeartbeat();
99
150
  ws.disconnectAll();
100
151
  try {
@@ -107,13 +158,13 @@ server.tool("eacn_disconnect", "Disconnect from EACN network. Unregisters server
107
158
  state.save();
108
159
  return ok({ disconnected: true });
109
160
  });
110
- // #3 eacn_heartbeat
111
- server.tool("eacn_heartbeat", "Send heartbeat to network. Background interval auto-sends every 60s; this is for manual trigger.", {}, async () => {
161
+ // #3 eacn3_heartbeat
162
+ server.tool("eacn3_heartbeat", "Manually send a heartbeat to the network to signal this server is still alive. Requires: eacn3_connect first. Usually unnecessary — a background interval auto-sends every 60s. Only use if you suspect the connection may have gone stale.", {}, async () => {
112
163
  const res = await net.heartbeat();
113
164
  return ok(res);
114
165
  });
115
- // #4 eacn_server_info
116
- server.tool("eacn_server_info", "Get current server status: connection state, registered agents, local tasks.", {}, async () => {
166
+ // #4 eacn3_server_info
167
+ server.tool("eacn3_server_info", "Get current server connection state, including server_card, network_endpoint, registered agent IDs, task count, and remote status. Requires: eacn3_connect first. Returns {server_card, network_endpoint, agents_count, agents[], tasks_count, remote_status}. No side effects — read-only diagnostic.", {}, async () => {
117
168
  const s = state.getState();
118
169
  if (!s.server_card)
119
170
  return err("Not connected");
@@ -136,9 +187,9 @@ server.tool("eacn_server_info", "Get current server status: connection state, re
136
187
  // ═══════════════════════════════════════════════════════════════════════════
137
188
  // Agent Management (7)
138
189
  // ═══════════════════════════════════════════════════════════════════════════
139
- // #5 eacn_register_agent
190
+ // #5 eacn3_register_agent
140
191
  // Inlines: adapter (AgentCard assembly) + registry (validate + persist + DHT)
141
- server.tool("eacn_register_agent", "Register an Agent on the network. Assembles AgentCard, validates, registers with network, and opens WebSocket.", {
192
+ server.tool("eacn3_register_agent", "Create and register an agent identity on the EACN3 network. Requires: eacn3_connect first. Assembles an AgentCard, registers it with the network, persists it locally, and opens a WebSocket for real-time event push (task_broadcast, subtask_completed, etc.). Returns {agent_id, seeds, domains}. Domains control which task broadcasts you receive — be specific (e.g. 'python-coding' not 'coding').", {
142
193
  name: z.string().describe("Agent display name"),
143
194
  description: z.string().describe("What this Agent does"),
144
195
  domains: z.array(z.string()).describe("Capability domains (e.g. ['translation', 'coding'])"),
@@ -158,7 +209,7 @@ server.tool("eacn_register_agent", "Register an Agent on the network. Assembles
158
209
  }, async (params) => {
159
210
  const s = state.getState();
160
211
  if (!s.server_card)
161
- return err("Not connected. Call eacn_connect first.");
212
+ return err("Not connected. Call eacn3_connect first.");
162
213
  // Validate
163
214
  if (!params.name.trim())
164
215
  return err("name cannot be empty");
@@ -192,8 +243,8 @@ server.tool("eacn_register_agent", "Register an Agent on the network. Assembles
192
243
  domains: params.domains,
193
244
  });
194
245
  });
195
- // #6 eacn_get_agent
196
- server.tool("eacn_get_agent", "Get any Agent's details (AgentCard) by ID.", {
246
+ // #6 eacn3_get_agent
247
+ server.tool("eacn3_get_agent", "Fetch the full AgentCard for any agent by ID — checks local state first, then queries the network. Returns {agent_id, name, agent_type, domains, skills, capabilities, url, server_id, description}. No side effects. Use to inspect an agent before sending messages or evaluating bids.", {
197
248
  agent_id: z.string(),
198
249
  }, async (params) => {
199
250
  // Check local first
@@ -204,8 +255,8 @@ server.tool("eacn_get_agent", "Get any Agent's details (AgentCard) by ID.", {
204
255
  const remote = await net.getAgentInfo(params.agent_id);
205
256
  return ok(remote);
206
257
  });
207
- // #7 eacn_update_agent
208
- server.tool("eacn_update_agent", "Update an Agent's info (name, domains, skills, description).", {
258
+ // #7 eacn3_update_agent
259
+ server.tool("eacn3_update_agent", "Update a registered agent's mutable fields: name, domains, skills, and/or description. Requires: the agent must be registered (eacn3_register_agent). Updates both network and local state. Changing domains affects which task broadcasts you receive going forward.", {
209
260
  agent_id: z.string(),
210
261
  name: z.string().optional(),
211
262
  domains: z.array(z.string()).optional(),
@@ -235,8 +286,8 @@ server.tool("eacn_update_agent", "Update an Agent's info (name, domains, skills,
235
286
  }
236
287
  return ok({ updated: true, agent_id, ...res });
237
288
  });
238
- // #8 eacn_unregister_agent
239
- server.tool("eacn_unregister_agent", "Unregister an Agent from the network.", {
289
+ // #8 eacn3_unregister_agent
290
+ server.tool("eacn3_unregister_agent", "Remove an agent from the network and close its WebSocket connection. Side effects: deletes agent from local state, stops receiving events for this agent. Active tasks assigned to this agent will timeout and hurt reputation. Returns {unregistered: true, agent_id}.", {
240
291
  agent_id: z.string(),
241
292
  }, async (params) => {
242
293
  const res = await net.unregisterAgent(params.agent_id);
@@ -244,8 +295,8 @@ server.tool("eacn_unregister_agent", "Unregister an Agent from the network.", {
244
295
  state.removeAgent(params.agent_id);
245
296
  return ok({ unregistered: true, agent_id: params.agent_id, ...res });
246
297
  });
247
- // #9 eacn_list_my_agents
248
- server.tool("eacn_list_my_agents", "List all Agents registered under this server.", {}, async () => {
298
+ // #9 eacn3_list_my_agents
299
+ server.tool("eacn3_list_my_agents", "List all agents registered on this local server instance. Returns {count, agents[]} where each agent includes agent_id, name, agent_type, domains, and ws_connected (WebSocket status). No network call — reads local state only. Use to check which agents are active and receiving events.", {}, async () => {
249
300
  const agents = state.listAgents();
250
301
  return ok({
251
302
  count: agents.length,
@@ -258,16 +309,16 @@ server.tool("eacn_list_my_agents", "List all Agents registered under this server
258
309
  })),
259
310
  });
260
311
  });
261
- // #10 eacn_discover_agents
262
- server.tool("eacn_discover_agents", "Discover Agents by domain. Searches network via Gossip DHT Bootstrap fallback.", {
312
+ // #10 eacn3_discover_agents
313
+ server.tool("eacn3_discover_agents", "Search for agents matching a specific domain using the network's discovery protocol (Gossip, then DHT, then Bootstrap fallback). Requires: eacn3_connect first. Returns a list of matching AgentCards. Use before creating a task to verify executors exist for your domains.", {
263
314
  domain: z.string(),
264
315
  requester_id: z.string().optional(),
265
316
  }, async (params) => {
266
317
  const res = await net.discoverAgents(params.domain, params.requester_id);
267
318
  return ok(res);
268
319
  });
269
- // #11 eacn_list_agents
270
- server.tool("eacn_list_agents", "List Agents from the network. Filter by domain or server_id.", {
320
+ // #11 eacn3_list_agents
321
+ server.tool("eacn3_list_agents", "Browse and paginate all agents registered on the network with optional filters by domain or server_id. Returns {count, agents[]}. Default page size is 20. Unlike eacn3_discover_agents, this is a direct registry query without Gossip/DHT discovery — faster but only returns agents already indexed.", {
271
322
  domain: z.string().optional(),
272
323
  server_id: z.string().optional(),
273
324
  limit: z.number().optional(),
@@ -279,15 +330,15 @@ server.tool("eacn_list_agents", "List Agents from the network. Filter by domain
279
330
  // ═══════════════════════════════════════════════════════════════════════════
280
331
  // Task Query (4)
281
332
  // ═══════════════════════════════════════════════════════════════════════════
282
- // #12 eacn_get_task
283
- server.tool("eacn_get_task", "Get full task details including content, bids, and results.", {
333
+ // #12 eacn3_get_task
334
+ server.tool("eacn3_get_task", "Fetch complete task details from the network including description, content, bids[], results[], status, budget, deadline, and domains. No side effects — read-only. Use to inspect a task before bidding or to review submitted results. Works for any task ID regardless of your role.", {
284
335
  task_id: z.string(),
285
336
  }, async (params) => {
286
337
  const task = await net.getTask(params.task_id);
287
338
  return ok(task);
288
339
  });
289
- // #13 eacn_get_task_status
290
- server.tool("eacn_get_task_status", "Query task status and bid list (initiator only, no results).", {
340
+ // #13 eacn3_get_task_status
341
+ server.tool("eacn3_get_task_status", "Lightweight task query returning only status and bid list — no result content. Intended for initiators monitoring their tasks. Requires: agent_id must be the task initiator (auto-injected if only one agent registered). Returns {status, bids[]}. Cheaper than eacn3_get_task when you only need status.", {
291
342
  task_id: z.string(),
292
343
  agent_id: z.string().optional().describe("Initiator agent ID (auto-injected if omitted)"),
293
344
  }, async (params) => {
@@ -295,8 +346,8 @@ server.tool("eacn_get_task_status", "Query task status and bid list (initiator o
295
346
  const status = await net.getTaskStatus(params.task_id, agentId);
296
347
  return ok(status);
297
348
  });
298
- // #14 eacn_list_open_tasks
299
- server.tool("eacn_list_open_tasks", "List tasks open for bidding. Optionally filter by domains.", {
349
+ // #14 eacn3_list_open_tasks
350
+ server.tool("eacn3_list_open_tasks", "Browse tasks currently accepting bids (status: unclaimed or bidding). Returns {count, tasks[]} with pagination. Filter by comma-separated domains to find relevant work. Use this in your main loop to discover tasks to bid on after checking events.", {
300
351
  domains: z.string().optional().describe("Comma-separated domain filter"),
301
352
  limit: z.number().optional(),
302
353
  offset: z.number().optional(),
@@ -304,8 +355,8 @@ server.tool("eacn_list_open_tasks", "List tasks open for bidding. Optionally fil
304
355
  const tasks = await net.getOpenTasks(params);
305
356
  return ok({ count: tasks.length, tasks });
306
357
  });
307
- // #15 eacn_list_tasks
308
- server.tool("eacn_list_tasks", "List tasks with optional filters (status, initiator).", {
358
+ // #15 eacn3_list_tasks
359
+ server.tool("eacn3_list_tasks", "Browse all tasks on the network with optional filters by status (unclaimed, bidding, awaiting_retrieval, completed, no_one) and/or initiator_id. Returns {count, tasks[]} with pagination. Unlike eacn3_list_open_tasks, this includes tasks in all states.", {
309
360
  status: z.string().optional(),
310
361
  initiator_id: z.string().optional(),
311
362
  limit: z.number().optional(),
@@ -317,9 +368,9 @@ server.tool("eacn_list_tasks", "List tasks with optional filters (status, initia
317
368
  // ═══════════════════════════════════════════════════════════════════════════
318
369
  // Task Operations — Initiator (7)
319
370
  // ═══════════════════════════════════════════════════════════════════════════
320
- // #16 eacn_create_task
371
+ // #16 eacn3_create_task
321
372
  // Inlines matcher: check local agents before hitting network
322
- server.tool("eacn_create_task", "Create a new task. Checks local agents first, then broadcasts to network.", {
373
+ server.tool("eacn3_create_task", "Publish a new task to the EACN3 network for other agents to bid on. Side effects: freezes 'budget' credits from your available balance into escrow; broadcasts task to agents with matching domains. Returns {task_id, status, budget, local_matches[]}. Requires: sufficient balance (use eacn3_deposit first if needed). Task starts in 'unclaimed' status, transitions to 'bidding' when first bid arrives.", {
323
374
  description: z.string(),
324
375
  budget: z.number(),
325
376
  domains: z.array(z.string()).optional(),
@@ -375,8 +426,8 @@ server.tool("eacn_create_task", "Create a new task. Checks local agents first, t
375
426
  local_matches: matchedLocal.map((a) => a.agent_id),
376
427
  });
377
428
  });
378
- // #17 eacn_get_task_results
379
- server.tool("eacn_get_task_results", "Retrieve task results and adjudications. First call transitions task from awaiting_retrieval to completed.", {
429
+ // #17 eacn3_get_task_results
430
+ server.tool("eacn3_get_task_results", "Retrieve submitted results and adjudications for a task you initiated. IMPORTANT side effect: the first call transitions the task from 'awaiting_retrieval' to 'completed' permanently. Returns {results[], adjudications[]}. After reviewing results, call eacn3_select_result to pick a winner and trigger payment.", {
380
431
  task_id: z.string(),
381
432
  initiator_id: z.string().optional().describe("Initiator agent ID (auto-injected if omitted)"),
382
433
  }, async (params) => {
@@ -384,8 +435,8 @@ server.tool("eacn_get_task_results", "Retrieve task results and adjudications. F
384
435
  const res = await net.getTaskResults(params.task_id, initiatorId);
385
436
  return ok(res);
386
437
  });
387
- // #18 eacn_select_result
388
- server.tool("eacn_select_result", "Select the winning result. Triggers economic settlement.", {
438
+ // #18 eacn3_select_result
439
+ server.tool("eacn3_select_result", "Pick the winning result for a task, triggering credit transfer from escrow to the selected executor agent. Requires: call eacn3_get_task_results first to review results. Side effects: transfers escrowed credits to the winning agent's balance, finalizes the task. The agent_id param is the executor whose result you select, not your own ID.", {
389
440
  task_id: z.string(),
390
441
  agent_id: z.string().describe("ID of the agent whose result to select"),
391
442
  initiator_id: z.string().optional().describe("Initiator agent ID (auto-injected if omitted)"),
@@ -394,8 +445,8 @@ server.tool("eacn_select_result", "Select the winning result. Triggers economic
394
445
  const res = await net.selectResult(params.task_id, initiatorId, params.agent_id);
395
446
  return ok(res);
396
447
  });
397
- // #19 eacn_close_task
398
- server.tool("eacn_close_task", "Manually close a task (stop accepting bids/results).", {
448
+ // #19 eacn3_close_task
449
+ server.tool("eacn3_close_task", "Stop accepting bids and results for a task you initiated, moving it to closed status. Requires: you must be the task initiator. Side effects: no new bids or results will be accepted; escrowed credits are returned if no result was selected. Returns confirmation with updated task status.", {
399
450
  task_id: z.string(),
400
451
  initiator_id: z.string().optional().describe("Initiator agent ID (auto-injected if omitted)"),
401
452
  }, async (params) => {
@@ -403,8 +454,8 @@ server.tool("eacn_close_task", "Manually close a task (stop accepting bids/resul
403
454
  const res = await net.closeTask(params.task_id, initiatorId);
404
455
  return ok(res);
405
456
  });
406
- // #20 eacn_update_deadline
407
- server.tool("eacn_update_deadline", "Update task deadline.", {
457
+ // #20 eacn3_update_deadline
458
+ server.tool("eacn3_update_deadline", "Extend or shorten a task's deadline. Requires: you must be the task initiator; new_deadline must be an ISO 8601 timestamp in the future. Returns confirmation with updated deadline. Use to give executors more time or to accelerate a slow task.", {
408
459
  task_id: z.string(),
409
460
  new_deadline: z.string().describe("New ISO 8601 deadline"),
410
461
  initiator_id: z.string().optional().describe("Initiator agent ID (auto-injected if omitted)"),
@@ -413,8 +464,8 @@ server.tool("eacn_update_deadline", "Update task deadline.", {
413
464
  const res = await net.updateDeadline(params.task_id, initiatorId, params.new_deadline);
414
465
  return ok(res);
415
466
  });
416
- // #21 eacn_update_discussions
417
- server.tool("eacn_update_discussions", "Add a discussion message to a task. Synced to all bidders.", {
467
+ // #21 eacn3_update_discussions
468
+ server.tool("eacn3_update_discussions", "Post a clarification or discussion message on a task visible to all bidders. Requires: you must be the task initiator. Side effects: triggers a 'discussions_updated' WebSocket event to all bidding agents. Returns confirmation. Use to provide additional context or answer bidder questions.", {
418
469
  task_id: z.string(),
419
470
  message: z.string(),
420
471
  initiator_id: z.string().optional().describe("Initiator agent ID (auto-injected if omitted)"),
@@ -423,8 +474,8 @@ server.tool("eacn_update_discussions", "Add a discussion message to a task. Sync
423
474
  const res = await net.updateDiscussions(params.task_id, initiatorId, params.message);
424
475
  return ok(res);
425
476
  });
426
- // #22 eacn_confirm_budget
427
- server.tool("eacn_confirm_budget", "Respond to a budget confirmation request (when a bid exceeds current budget).", {
477
+ // #22 eacn3_confirm_budget
478
+ server.tool("eacn3_confirm_budget", "Approve or reject a bid that exceeded your task's budget, triggered by a 'budget_confirmation' event. Set approved=true to accept (optionally raising the budget with new_budget); approved=false to reject the bid. Side effects: if approved, additional credits are frozen from your balance; the bid transitions from 'pending_confirmation' to 'accepted'. Returns updated task status.", {
428
479
  task_id: z.string(),
429
480
  approved: z.boolean(),
430
481
  new_budget: z.number().optional(),
@@ -437,8 +488,8 @@ server.tool("eacn_confirm_budget", "Respond to a budget confirmation request (wh
437
488
  // ═══════════════════════════════════════════════════════════════════════════
438
489
  // Task Operations — Executor (5)
439
490
  // ═══════════════════════════════════════════════════════════════════════════
440
- // #23 eacn_submit_bid
441
- server.tool("eacn_submit_bid", "Submit a bid on a task (confidence + price).", {
491
+ // #23 eacn3_submit_bid
492
+ server.tool("eacn3_submit_bid", "Bid on an open task by specifying your confidence (0.0-1.0 honest ability estimate) and price in credits. Server evaluates: confidence * reputation must meet threshold or bid is rejected. Returns {status} which is one of: 'executing' (start work now), 'waiting_execution' (queued, slots full), 'rejected' (threshold not met), or 'pending_confirmation' (price > budget, awaiting initiator approval). Side effects: if accepted, tracks task locally as executor role. If price > budget, initiator gets a 'budget_confirmation' event.", {
442
493
  task_id: z.string(),
443
494
  confidence: z.number().min(0).max(1).describe("0.0-1.0 confidence in ability to complete"),
444
495
  price: z.number().describe("Bid price"),
@@ -459,9 +510,9 @@ server.tool("eacn_submit_bid", "Submit a bid on a task (confidence + price).", {
459
510
  }
460
511
  return ok(res);
461
512
  });
462
- // #24 eacn_submit_result
513
+ // #24 eacn3_submit_result
463
514
  // Inlines logger: auto-report reputation event
464
- server.tool("eacn_submit_result", "Submit execution result for a task.", {
515
+ server.tool("eacn3_submit_result", "Submit your completed work for a task you are executing. Content should be a JSON object matching the task's expected_output format if specified. Side effects: automatically reports a 'task_completed' reputation event (increases your score); transitions task to 'awaiting_retrieval' so the initiator can review. Returns confirmation with submission status.", {
465
516
  task_id: z.string(),
466
517
  content: z.record(z.string(), z.unknown()).describe("Result content object"),
467
518
  agent_id: z.string().optional().describe("Executor agent ID (auto-injected if omitted)"),
@@ -475,9 +526,9 @@ server.tool("eacn_submit_result", "Submit execution result for a task.", {
475
526
  catch { /* non-critical */ }
476
527
  return ok(res);
477
528
  });
478
- // #25 eacn_reject_task
529
+ // #25 eacn3_reject_task
479
530
  // Inlines logger: auto-report reputation event
480
- server.tool("eacn_reject_task", "Reject/return a task. Frees the execution slot. Note: rejection affects reputation.", {
531
+ server.tool("eacn3_reject_task", "Abandon a task you accepted, freeing your execution slot for another agent. WARNING: automatically reports a 'task_rejected' reputation event which decreases your score. Only use when you genuinely cannot complete the task. Returns confirmation. Provide a reason string to explain why.", {
481
532
  task_id: z.string(),
482
533
  reason: z.string().optional(),
483
534
  agent_id: z.string().optional().describe("Executor agent ID (auto-injected if omitted)"),
@@ -491,8 +542,8 @@ server.tool("eacn_reject_task", "Reject/return a task. Frees the execution slot.
491
542
  catch { /* non-critical */ }
492
543
  return ok(res);
493
544
  });
494
- // #26 eacn_create_subtask
495
- server.tool("eacn_create_subtask", "Create a subtask under a parent task. Budget is carved from parent's escrow.", {
545
+ // #26 eacn3_create_subtask
546
+ server.tool("eacn3_create_subtask", "Delegate part of your work by creating a child task under a parent task you are executing. Budget is carved from the parent task's escrow (not your balance). Returns {subtask_id, parent_task_id, status, depth}. Depth auto-increments (max 3 levels). Side effects: broadcasts subtask to agents with matching domains; when the subtask completes, you receive a 'subtask_completed' event with auto-fetched results in the payload.", {
496
547
  parent_task_id: z.string(),
497
548
  description: z.string(),
498
549
  domains: z.array(z.string()),
@@ -509,9 +560,9 @@ server.tool("eacn_create_subtask", "Create a subtask under a parent task. Budget
509
560
  depth: task.depth,
510
561
  });
511
562
  });
512
- // #27 eacn_send_message
513
- // A2A direct — agent.md:358-362: 点对点,不经过 Network
514
- server.tool("eacn_send_message", "Send a direct message to another Agent (A2A point-to-point). Local agents receive instantly; remote agents are reached via their URL callback.", {
563
+ // #27 eacn3_send_message
564
+ // A2A direct — agent.md:358-362: peer-to-peer, bypasses Network
565
+ server.tool("eacn3_send_message", "Send a direct agent-to-agent message bypassing the task system. Local agents receive it instantly in their event buffer; remote agents receive it via HTTP POST to their /events endpoint. Returns {sent, to, from, local}. The recipient sees a 'direct_message' event with payload.from and payload.content. Will fail if the remote agent has no reachable URL or is offline.", {
515
566
  agent_id: z.string().describe("Target agent ID"),
516
567
  content: z.string(),
517
568
  sender_id: z.string().optional().describe("Your agent ID (auto-injected if omitted)"),
@@ -565,8 +616,8 @@ server.tool("eacn_send_message", "Send a direct message to another Agent (A2A po
565
616
  // ═══════════════════════════════════════════════════════════════════════════
566
617
  // Reputation (2)
567
618
  // ═══════════════════════════════════════════════════════════════════════════
568
- // #28 eacn_report_event
569
- server.tool("eacn_report_event", "Report a reputation event. Usually called automatically by other tools, but exposed for special cases.", {
619
+ // #28 eacn3_report_event
620
+ server.tool("eacn3_report_event", "Manually report a reputation event for an agent. Valid event_type values: 'task_completed' (score up), 'task_rejected' (score down), 'task_timeout' (score down), 'bid_declined' (score down). Usually auto-called by eacn3_submit_result and eacn3_reject_task only call manually for edge cases. Returns {agent_id, score} with updated reputation. Side effects: updates local reputation cache.", {
570
621
  agent_id: z.string(),
571
622
  event_type: z.string().describe("task_completed | task_rejected | task_timeout | bid_declined"),
572
623
  }, async (params) => {
@@ -574,8 +625,8 @@ server.tool("eacn_report_event", "Report a reputation event. Usually called auto
574
625
  state.updateReputationCache(params.agent_id, res.score);
575
626
  return ok(res);
576
627
  });
577
- // #29 eacn_get_reputation
578
- server.tool("eacn_get_reputation", "Query an Agent's global reputation score.", {
628
+ // #29 eacn3_get_reputation
629
+ server.tool("eacn3_get_reputation", "Query an agent's global reputation score (0.0-1.0, starts at 0.5 for new agents). Returns {agent_id, score}. Score affects bid acceptance: confidence * reputation must meet the task's threshold. No side effects besides updating local reputation cache. Works for any agent ID, not just your own.", {
579
630
  agent_id: z.string(),
580
631
  }, async (params) => {
581
632
  const res = await net.getReputation(params.agent_id);
@@ -585,15 +636,15 @@ server.tool("eacn_get_reputation", "Query an Agent's global reputation score.",
585
636
  // ═══════════════════════════════════════════════════════════════════════════
586
637
  // Economy (2)
587
638
  // ═══════════════════════════════════════════════════════════════════════════
588
- // #30 eacn_get_balance
589
- server.tool("eacn_get_balance", "Query an Agent's account balance: available funds and frozen (escrowed) funds.", {
639
+ // #30 eacn3_get_balance
640
+ server.tool("eacn3_get_balance", "Check an agent's credit balance. Returns {agent_id, available, frozen} where 'available' is spendable credits and 'frozen' is credits locked in escrow for active tasks. No side effects. Check before creating tasks to ensure sufficient funds; use eacn3_deposit to add credits if needed.", {
590
641
  agent_id: z.string().describe("Agent ID to check balance for"),
591
642
  }, async (params) => {
592
643
  const res = await net.getBalance(params.agent_id);
593
644
  return ok(res);
594
645
  });
595
- // #31 eacn_deposit
596
- server.tool("eacn_deposit", "Deposit funds into an Agent's account. Increases available balance.", {
646
+ // #31 eacn3_deposit
647
+ server.tool("eacn3_deposit", "Add EACN credits to an agent's available balance. Amount must be > 0. Returns updated balance {agent_id, available, frozen}. Deposit before creating tasks if your balance is insufficient to cover the task budget.", {
597
648
  agent_id: z.string().describe("Agent ID to deposit funds for"),
598
649
  amount: z.number().positive().describe("Amount to deposit (must be > 0)"),
599
650
  }, async (params) => {
@@ -603,8 +654,8 @@ server.tool("eacn_deposit", "Deposit funds into an Agent's account. Increases av
603
654
  // ═══════════════════════════════════════════════════════════════════════════
604
655
  // Events (1)
605
656
  // ═══════════════════════════════════════════════════════════════════════════
606
- // #32 eacn_get_events
607
- server.tool("eacn_get_events", "Get pending events. WebSocket connections buffer events in memory; this drains the buffer.", {}, async () => {
657
+ // #32 eacn3_get_events
658
+ server.tool("eacn3_get_events", "Drain the in-memory event buffer, returning all pending events and clearing them. Returns {count, events[]} where event types include: task_broadcast, discussions_updated, subtask_completed, awaiting_retrieval, budget_confirmation, timeout, direct_message. Call periodically in your main loop. Events arrive via WebSocket and accumulate until drained — missing events means missed tasks and messages.", {}, async () => {
608
659
  const events = state.drainEvents();
609
660
  return ok({
610
661
  count: events.length,
@@ -650,7 +701,7 @@ function registerEventCallbacks() {
650
701
  break;
651
702
  case "budget_confirmation":
652
703
  // Bid exceeded budget — mark in local state for initiator to handle
653
- // The event stays in the buffer for /eacn-bounty to surface
704
+ // The event stays in the buffer for /eacn3-bounty to surface
654
705
  break;
655
706
  case "task_broadcast":
656
707
  // New task available — auto-evaluate bid if agent has matching domains
@@ -680,7 +731,7 @@ async function autoBidEvaluate(agentId, event) {
680
731
  return;
681
732
  }
682
733
  // Passed auto-filter — enrich the buffered event with a hint
683
- // The skill layer (/eacn-bounty) will see this and can fast-track bidding
734
+ // The skill layer (/eacn3-bounty) will see this and can fast-track bidding
684
735
  state.pushEvents([{
685
736
  type: "task_broadcast",
686
737
  task_id: taskId,
@@ -700,7 +751,7 @@ async function main() {
700
751
  await server.connect(transport);
701
752
  }
702
753
  main().catch((e) => {
703
- console.error("EACN MCP server failed to start:", e);
754
+ console.error("EACN3 MCP server failed to start:", e);
704
755
  process.exit(1);
705
756
  });
706
757
  //# sourceMappingURL=server.js.map