bus-agent 2.3.0
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/.env.coco +11 -0
- package/AGENTS.md +37 -0
- package/LICENSE +21 -0
- package/README.md +370 -0
- package/SKILL.md +314 -0
- package/backup.js +57 -0
- package/bin/cli.js +41 -0
- package/bridge.js +325 -0
- package/claude-mcp.json +10 -0
- package/clients/coco-client.ts +245 -0
- package/clients/coco_client.py +216 -0
- package/coco-aliases.sh +10 -0
- package/coco-cli.js +1002 -0
- package/coco-tool.js +177 -0
- package/coco.js +26 -0
- package/cursor-mcp.json +3 -0
- package/doctor.js +24 -0
- package/hermes-forwarder.js +152 -0
- package/hermes.example.json +9 -0
- package/index.js +52 -0
- package/lib/backup.js +256 -0
- package/lib/bus.js +516 -0
- package/lib/daemon.js +96 -0
- package/lib/doctor.js +333 -0
- package/lib/hermes.js +162 -0
- package/lib/mcp.js +730 -0
- package/lib/memory.js +667 -0
- package/lib/orchestrator.js +426 -0
- package/lib/scheduler.js +259 -0
- package/lib/tunnel.js +317 -0
- package/mcporter.example.json +14 -0
- package/opencode-mcp.json +10 -0
- package/package.json +76 -0
- package/scripts/install.bat +5 -0
- package/scripts/install.ps1 +100 -0
- package/setup.js +320 -0
- package/tunnel.js +66 -0
- package/webhook-gateway.js +420 -0
package/lib/mcp.js
ADDED
|
@@ -0,0 +1,730 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Protocol Handler — JSON-RPC 2.0 over stdio
|
|
3
|
+
*
|
|
4
|
+
* MCP CoCo v2.1: Agent Profiles, Discovery, Profiles, Scheduler base
|
|
5
|
+
*/
|
|
6
|
+
const readline = require('readline');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
const { HermesBridge } = require('./hermes');
|
|
9
|
+
const { AgentBus } = require('./bus');
|
|
10
|
+
const { Scheduler } = require('./scheduler');
|
|
11
|
+
const { Orchestrator } = require('./orchestrator');
|
|
12
|
+
|
|
13
|
+
class MCPServer {
|
|
14
|
+
constructor() {
|
|
15
|
+
this.bridge = new HermesBridge();
|
|
16
|
+
this.bus = new AgentBus();
|
|
17
|
+
this.agentName = 'coco';
|
|
18
|
+
|
|
19
|
+
// Auto-register CoCo on bus with full profile
|
|
20
|
+
this.bus.registerAgent('coco', 'MCP CoCo — Universal Agent Communication Hub', {
|
|
21
|
+
version: '2.1.0',
|
|
22
|
+
capabilities: [
|
|
23
|
+
'agent-registry', 'agent-discovery', 'agent-profiles',
|
|
24
|
+
'direct-messaging', 'broadcast', 'channels',
|
|
25
|
+
'system-events', 'webhook-gateway',
|
|
26
|
+
],
|
|
27
|
+
tags: ['bus', 'hub', 'orchestrator', 'bridge'],
|
|
28
|
+
tools: [],
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
this.tools = [
|
|
32
|
+
// ── Hermes Bridge Tools (legacy) ──
|
|
33
|
+
{
|
|
34
|
+
name: 'ask_hermes',
|
|
35
|
+
description: 'Send a prompt to Hermes Agent and get its response. Supports multi-turn via session_key.',
|
|
36
|
+
inputSchema: {
|
|
37
|
+
type: 'object',
|
|
38
|
+
properties: {
|
|
39
|
+
prompt: { type: 'string', description: 'The prompt/message to send' },
|
|
40
|
+
session_key: { type: 'string', description: 'Previous session key to continue (optional)' },
|
|
41
|
+
},
|
|
42
|
+
required: ['prompt'],
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
name: 'hermes_send',
|
|
47
|
+
description: 'Send a message through Hermes to a chat platform (Telegram, Discord, etc.)',
|
|
48
|
+
inputSchema: {
|
|
49
|
+
type: 'object',
|
|
50
|
+
properties: {
|
|
51
|
+
target: { type: 'string', description: 'Target platform:chat_id (e.g. telegram:6616375428)' },
|
|
52
|
+
message: { type: 'string', description: 'Message text to send' },
|
|
53
|
+
},
|
|
54
|
+
required: ['target', 'message'],
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
name: 'hermes_channels',
|
|
59
|
+
description: 'List available Hermes messaging channels/platforms',
|
|
60
|
+
inputSchema: {
|
|
61
|
+
type: 'object',
|
|
62
|
+
properties: {
|
|
63
|
+
platform: { type: 'string', description: 'Optional platform filter (telegram, discord, etc.)' },
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
name: 'coco_health',
|
|
69
|
+
description: 'Check MCP CoCo bridge and Hermes Agent health status',
|
|
70
|
+
inputSchema: { type: 'object', properties: {} },
|
|
71
|
+
},
|
|
72
|
+
|
|
73
|
+
// ── Agent Profiles & Registry ──
|
|
74
|
+
{
|
|
75
|
+
name: 'agent_register',
|
|
76
|
+
description: 'Register your agent on the bus with a rich profile. Announce join to all agents.',
|
|
77
|
+
inputSchema: {
|
|
78
|
+
type: 'object',
|
|
79
|
+
properties: {
|
|
80
|
+
name: { type: 'string', description: 'Your agent name (e.g. openclaw, hermes, claude)' },
|
|
81
|
+
description: { type: 'string', description: 'Short description of your agent' },
|
|
82
|
+
capabilities: {
|
|
83
|
+
type: 'array', items: { type: 'string' },
|
|
84
|
+
description: 'What your agent can do (e.g. ["code-review", "web-search", "chat"])',
|
|
85
|
+
},
|
|
86
|
+
model: {
|
|
87
|
+
type: 'object',
|
|
88
|
+
properties: {
|
|
89
|
+
provider: { type: 'string', description: 'e.g. opencode, anthropic, openai' },
|
|
90
|
+
name: { type: 'string', description: 'e.g. deepseek-v4, claude-opus-4' },
|
|
91
|
+
context_window: { type: 'number' },
|
|
92
|
+
},
|
|
93
|
+
description: 'AI model info',
|
|
94
|
+
},
|
|
95
|
+
tags: { type: 'array', items: { type: 'string' }, description: 'Tags like ["code", "ai", "bot"]' },
|
|
96
|
+
version: { type: 'string', description: 'Agent version string' },
|
|
97
|
+
endpoints: {
|
|
98
|
+
type: 'object',
|
|
99
|
+
properties: {
|
|
100
|
+
mcp: { type: 'string' }, http: { type: 'string' }, websocket: { type: 'string' },
|
|
101
|
+
},
|
|
102
|
+
description: 'Connection endpoints',
|
|
103
|
+
},
|
|
104
|
+
tools: { type: 'array', items: { type: 'string' }, description: 'Tool names this agent exposes' },
|
|
105
|
+
status: { type: 'string', enum: ['idle', 'busy', 'offline'], description: 'Initial status' },
|
|
106
|
+
},
|
|
107
|
+
required: ['name'],
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
name: 'agent_update_profile',
|
|
112
|
+
description: 'Update your agent profile fields (description, capabilities, status, etc.)',
|
|
113
|
+
inputSchema: {
|
|
114
|
+
type: 'object',
|
|
115
|
+
properties: {
|
|
116
|
+
name: { type: 'string', description: 'Your agent name' },
|
|
117
|
+
description: { type: 'string' },
|
|
118
|
+
capabilities: { type: 'array', items: { type: 'string' } },
|
|
119
|
+
model: { type: 'object', properties: { provider: { type: 'string' }, name: { type: 'string' }, context_window: { type: 'number' } } },
|
|
120
|
+
tags: { type: 'array', items: { type: 'string' } },
|
|
121
|
+
status: { type: 'string', enum: ['idle', 'busy', 'offline'] },
|
|
122
|
+
version: { type: 'string' },
|
|
123
|
+
endpoints: { type: 'object' },
|
|
124
|
+
tools: { type: 'array', items: { type: 'string' } },
|
|
125
|
+
},
|
|
126
|
+
required: ['name'],
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
name: 'agent_get_profile',
|
|
131
|
+
description: 'Get detailed profile for a specific agent',
|
|
132
|
+
inputSchema: {
|
|
133
|
+
type: 'object',
|
|
134
|
+
properties: {
|
|
135
|
+
name: { type: 'string', description: 'Agent name' },
|
|
136
|
+
},
|
|
137
|
+
required: ['name'],
|
|
138
|
+
},
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
name: 'agent_list',
|
|
142
|
+
description: 'List agents with optional filters (online, capability, model, tag, status, search)',
|
|
143
|
+
inputSchema: {
|
|
144
|
+
type: 'object',
|
|
145
|
+
properties: {
|
|
146
|
+
online_only: { type: 'boolean', description: 'Only show agents seen in last 5 min' },
|
|
147
|
+
capability: { type: 'string', description: 'Filter by capability (e.g. "code-review")' },
|
|
148
|
+
model: { type: 'string', description: 'Filter by model name/provider' },
|
|
149
|
+
tag: { type: 'string', description: 'Filter by exact tag match' },
|
|
150
|
+
status: { type: 'string', enum: ['idle', 'busy', 'offline'], description: 'Filter by status' },
|
|
151
|
+
search: { type: 'string', description: 'Search name/description/capabilities' },
|
|
152
|
+
},
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
name: 'agent_search',
|
|
157
|
+
description: 'Search for agents by name, capability, or description. Returns matching profiles.',
|
|
158
|
+
inputSchema: {
|
|
159
|
+
type: 'object',
|
|
160
|
+
properties: {
|
|
161
|
+
query: { type: 'string', description: 'Search query' },
|
|
162
|
+
max_results: { type: 'number', description: 'Max results (default 20)' },
|
|
163
|
+
},
|
|
164
|
+
required: ['query'],
|
|
165
|
+
},
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
name: 'agent_heartbeat',
|
|
169
|
+
description: 'Ping the bus to mark your agent as online',
|
|
170
|
+
inputSchema: {
|
|
171
|
+
type: 'object',
|
|
172
|
+
properties: {
|
|
173
|
+
name: { type: 'string', description: 'Your agent name' },
|
|
174
|
+
},
|
|
175
|
+
required: ['name'],
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
name: 'agent_set_status',
|
|
180
|
+
description: 'Set your agent status: idle, busy, or offline',
|
|
181
|
+
inputSchema: {
|
|
182
|
+
type: 'object',
|
|
183
|
+
properties: {
|
|
184
|
+
name: { type: 'string', description: 'Your agent name' },
|
|
185
|
+
status: { type: 'string', enum: ['idle', 'busy', 'offline'], description: 'New status' },
|
|
186
|
+
},
|
|
187
|
+
required: ['name', 'status'],
|
|
188
|
+
},
|
|
189
|
+
},
|
|
190
|
+
|
|
191
|
+
// ── Direct Messages ──
|
|
192
|
+
{
|
|
193
|
+
name: 'message_send',
|
|
194
|
+
description: 'Send a direct message to another agent on the bus',
|
|
195
|
+
inputSchema: {
|
|
196
|
+
type: 'object',
|
|
197
|
+
properties: {
|
|
198
|
+
from: { type: 'string', description: 'Your agent name' },
|
|
199
|
+
to: { type: 'string', description: 'Target agent name' },
|
|
200
|
+
message: { type: 'string', description: 'Message content' },
|
|
201
|
+
metadata: { type: 'object', description: 'Optional metadata (type, priority, etc.)' },
|
|
202
|
+
},
|
|
203
|
+
required: ['from', 'to', 'message'],
|
|
204
|
+
},
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
name: 'message_broadcast',
|
|
208
|
+
description: 'Send a message to ALL agents on the bus',
|
|
209
|
+
inputSchema: {
|
|
210
|
+
type: 'object',
|
|
211
|
+
properties: {
|
|
212
|
+
from: { type: 'string', description: 'Your agent name' },
|
|
213
|
+
message: { type: 'string', description: 'Message content' },
|
|
214
|
+
},
|
|
215
|
+
required: ['from', 'message'],
|
|
216
|
+
},
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
name: 'message_fetch',
|
|
220
|
+
description: 'Fetch messages from your inbox. Supports filtering by sender and pagination.',
|
|
221
|
+
inputSchema: {
|
|
222
|
+
type: 'object',
|
|
223
|
+
properties: {
|
|
224
|
+
agent_name: { type: 'string', description: 'Your agent name' },
|
|
225
|
+
limit: { type: 'number', description: 'Max messages to fetch (default 50)' },
|
|
226
|
+
after_id: { type: 'string', description: 'Fetch messages after this ID (for pagination)' },
|
|
227
|
+
from: { type: 'string', description: 'Filter messages from a specific sender' },
|
|
228
|
+
},
|
|
229
|
+
required: ['agent_name'],
|
|
230
|
+
},
|
|
231
|
+
},
|
|
232
|
+
{
|
|
233
|
+
name: 'message_wait',
|
|
234
|
+
description: 'Long-poll: wait for a new message (up to 30s), then return',
|
|
235
|
+
inputSchema: {
|
|
236
|
+
type: 'object',
|
|
237
|
+
properties: {
|
|
238
|
+
agent_name: { type: 'string', description: 'Your agent name' },
|
|
239
|
+
timeout_ms: { type: 'number', description: 'Wait timeout in ms (default 30000, max 60000)' },
|
|
240
|
+
},
|
|
241
|
+
required: ['agent_name'],
|
|
242
|
+
},
|
|
243
|
+
},
|
|
244
|
+
{
|
|
245
|
+
name: 'message_delete',
|
|
246
|
+
description: 'Delete messages from your inbox by message ID',
|
|
247
|
+
inputSchema: {
|
|
248
|
+
type: 'object',
|
|
249
|
+
properties: {
|
|
250
|
+
agent_name: { type: 'string', description: 'Your agent name' },
|
|
251
|
+
message_ids: { type: 'array', items: { type: 'string' }, description: 'Message IDs to delete' },
|
|
252
|
+
},
|
|
253
|
+
required: ['agent_name', 'message_ids'],
|
|
254
|
+
},
|
|
255
|
+
},
|
|
256
|
+
|
|
257
|
+
// ── Channels ──
|
|
258
|
+
{
|
|
259
|
+
name: 'channel_create',
|
|
260
|
+
description: 'Create a shared channel for multi-agent conversations',
|
|
261
|
+
inputSchema: {
|
|
262
|
+
type: 'object',
|
|
263
|
+
properties: {
|
|
264
|
+
channel_id: { type: 'string', description: 'Channel name (e.g. dev-chat, general)' },
|
|
265
|
+
topic: { type: 'string', description: 'Channel topic/description' },
|
|
266
|
+
created_by: { type: 'string', description: 'Your agent name' },
|
|
267
|
+
},
|
|
268
|
+
required: ['channel_id'],
|
|
269
|
+
},
|
|
270
|
+
},
|
|
271
|
+
{
|
|
272
|
+
name: 'channel_join',
|
|
273
|
+
description: 'Join an existing channel to receive its messages',
|
|
274
|
+
inputSchema: {
|
|
275
|
+
type: 'object',
|
|
276
|
+
properties: {
|
|
277
|
+
channel_id: { type: 'string', description: 'Channel name' },
|
|
278
|
+
agent_name: { type: 'string', description: 'Your agent name' },
|
|
279
|
+
},
|
|
280
|
+
required: ['channel_id', 'agent_name'],
|
|
281
|
+
},
|
|
282
|
+
},
|
|
283
|
+
{
|
|
284
|
+
name: 'channel_leave',
|
|
285
|
+
description: 'Leave a channel',
|
|
286
|
+
inputSchema: {
|
|
287
|
+
type: 'object',
|
|
288
|
+
properties: {
|
|
289
|
+
channel_id: { type: 'string', description: 'Channel name' },
|
|
290
|
+
agent_name: { type: 'string', description: 'Your agent name' },
|
|
291
|
+
},
|
|
292
|
+
required: ['channel_id', 'agent_name'],
|
|
293
|
+
},
|
|
294
|
+
},
|
|
295
|
+
{
|
|
296
|
+
name: 'channel_send',
|
|
297
|
+
description: 'Send a message to a channel (delivered to all members)',
|
|
298
|
+
inputSchema: {
|
|
299
|
+
type: 'object',
|
|
300
|
+
properties: {
|
|
301
|
+
channel_id: { type: 'string', description: 'Channel name' },
|
|
302
|
+
from: { type: 'string', description: 'Your agent name' },
|
|
303
|
+
message: { type: 'string', description: 'Message content' },
|
|
304
|
+
},
|
|
305
|
+
required: ['channel_id', 'from', 'message'],
|
|
306
|
+
},
|
|
307
|
+
},
|
|
308
|
+
{
|
|
309
|
+
name: 'channel_history',
|
|
310
|
+
description: 'Read recent messages from a channel',
|
|
311
|
+
inputSchema: {
|
|
312
|
+
type: 'object',
|
|
313
|
+
properties: {
|
|
314
|
+
channel_id: { type: 'string', description: 'Channel name' },
|
|
315
|
+
limit: { type: 'number', description: 'Max messages (default 50)' },
|
|
316
|
+
},
|
|
317
|
+
required: ['channel_id'],
|
|
318
|
+
},
|
|
319
|
+
},
|
|
320
|
+
|
|
321
|
+
// ── System Events ──
|
|
322
|
+
{
|
|
323
|
+
name: 'system_get_events',
|
|
324
|
+
description: 'Get recent system events (agent join/leave, profile changes, etc.)',
|
|
325
|
+
inputSchema: {
|
|
326
|
+
type: 'object',
|
|
327
|
+
properties: {
|
|
328
|
+
since: { type: 'string', description: 'ISO timestamp: only events after this time' },
|
|
329
|
+
category: { type: 'string', description: 'Filter by category (e.g. "system")' },
|
|
330
|
+
type: { type: 'string', description: 'Filter by event type (e.g. "agent_joined", "agent_offline")' },
|
|
331
|
+
limit: { type: 'number', description: 'Max events (default 100)' },
|
|
332
|
+
},
|
|
333
|
+
},
|
|
334
|
+
},
|
|
335
|
+
{
|
|
336
|
+
name: 'system_wait_for_event',
|
|
337
|
+
description: 'Long-poll: wait for a system event (agent join/leave, etc.)',
|
|
338
|
+
inputSchema: {
|
|
339
|
+
type: 'object',
|
|
340
|
+
properties: {
|
|
341
|
+
category: { type: 'string', description: 'Event category (e.g. "system")' },
|
|
342
|
+
type: { type: 'string', description: 'Event type (e.g. "agent_joined")' },
|
|
343
|
+
timeout_ms: { type: 'number', description: 'Wait timeout in ms (default 30000)' },
|
|
344
|
+
},
|
|
345
|
+
},
|
|
346
|
+
},
|
|
347
|
+
|
|
348
|
+
// ── Memory Layer ──
|
|
349
|
+
{
|
|
350
|
+
name: 'memory_store',
|
|
351
|
+
description: 'Store a memory for an agent. Memories persist in .bus/memory/ with keyword search index.',
|
|
352
|
+
inputSchema: {
|
|
353
|
+
type: 'object',
|
|
354
|
+
properties: {
|
|
355
|
+
agent: { type: 'string' },
|
|
356
|
+
content: { type: 'string' },
|
|
357
|
+
key: { type: 'string' },
|
|
358
|
+
namespace: { type: 'string' },
|
|
359
|
+
metadata: { type: 'object' },
|
|
360
|
+
},
|
|
361
|
+
required: ['agent', 'content'],
|
|
362
|
+
},
|
|
363
|
+
},
|
|
364
|
+
{
|
|
365
|
+
name: 'memory_search',
|
|
366
|
+
description: 'Search memories by content using TF-IDF keyword scoring + recency boost.',
|
|
367
|
+
inputSchema: {
|
|
368
|
+
type: 'object',
|
|
369
|
+
properties: {
|
|
370
|
+
agent: { type: 'string' },
|
|
371
|
+
query: { type: 'string' },
|
|
372
|
+
limit: { type: 'number' },
|
|
373
|
+
namespace: { type: 'string' },
|
|
374
|
+
threshold: { type: 'number' },
|
|
375
|
+
},
|
|
376
|
+
required: ['agent', 'query'],
|
|
377
|
+
},
|
|
378
|
+
},
|
|
379
|
+
{
|
|
380
|
+
name: 'memory_recall',
|
|
381
|
+
description: 'Recall a specific memory by ID or key.',
|
|
382
|
+
inputSchema: { type: 'object', properties: { agent: { type: 'string' }, id: { type: 'string' } }, required: ['agent', 'id'] },
|
|
383
|
+
},
|
|
384
|
+
{
|
|
385
|
+
name: 'memory_list',
|
|
386
|
+
description: 'List recent memories for an agent.',
|
|
387
|
+
inputSchema: { type: 'object', properties: { agent: { type: 'string' }, namespace: { type: 'string' }, limit: { type: 'number' }, offset: { type: 'number' } }, required: ['agent'] },
|
|
388
|
+
},
|
|
389
|
+
{
|
|
390
|
+
name: 'memory_forget',
|
|
391
|
+
description: 'Delete a specific memory by ID.',
|
|
392
|
+
inputSchema: { type: 'object', properties: { agent: { type: 'string' }, id: { type: 'string' } }, required: ['agent', 'id'] },
|
|
393
|
+
},
|
|
394
|
+
{
|
|
395
|
+
name: 'memory_stats',
|
|
396
|
+
description: 'Get memory usage statistics for an agent.',
|
|
397
|
+
inputSchema: { type: 'object', properties: { agent: { type: 'string' } }, required: ['agent'] },
|
|
398
|
+
},
|
|
399
|
+
{
|
|
400
|
+
name: 'memory_archive',
|
|
401
|
+
description: 'Archive old messages from agent inbox into memory, optionally deleting originals.',
|
|
402
|
+
inputSchema: {
|
|
403
|
+
type: 'object',
|
|
404
|
+
properties: {
|
|
405
|
+
agent: { type: 'string' },
|
|
406
|
+
max_age_days: { type: 'number' },
|
|
407
|
+
delete_after: { type: 'boolean' },
|
|
408
|
+
},
|
|
409
|
+
required: ['agent'],
|
|
410
|
+
},
|
|
411
|
+
},
|
|
412
|
+
{
|
|
413
|
+
name: 'memory_configure',
|
|
414
|
+
description: 'Configure embedding provider for semantic search (Ollama, OpenAI, Custom). Default: keyword-only.',
|
|
415
|
+
inputSchema: { type: 'object', properties: { provider: { type: 'string', enum: ['keyword', 'ollama', 'openai', 'custom'] }, api_key: { type: 'string' }, endpoint: { type: 'string' }, model: { type: 'string' } } },
|
|
416
|
+
},
|
|
417
|
+
{
|
|
418
|
+
name: 'memory_clear',
|
|
419
|
+
description: 'Clear all memories for an agent, optionally filtered by namespace.',
|
|
420
|
+
inputSchema: { type: 'object', properties: { agent: { type: 'string' }, namespace: { type: 'string' } }, required: ['agent'] },
|
|
421
|
+
},
|
|
422
|
+
{
|
|
423
|
+
name: 'memory_rebuild',
|
|
424
|
+
description: 'Rebuild all vector embeddings for an agent (requires embedding provider configured).',
|
|
425
|
+
inputSchema: { type: 'object', properties: { agent: { type: 'string' } }, required: ['agent'] },
|
|
426
|
+
},
|
|
427
|
+
];
|
|
428
|
+
|
|
429
|
+
// Init memory store
|
|
430
|
+
const { MemoryStore } = require('./memory');
|
|
431
|
+
this.memory = new MemoryStore(path.join(__dirname, '..', '.bus'));
|
|
432
|
+
|
|
433
|
+
// Init scheduler & orchestrator
|
|
434
|
+
this.scheduler = new Scheduler(this.bus);
|
|
435
|
+
this.orchestrator = new Orchestrator(this.bus);
|
|
436
|
+
|
|
437
|
+
// Spread scheduler + orchestrator tools into this.tools
|
|
438
|
+
this.tools.push(...this.scheduler.getScheduleTools());
|
|
439
|
+
this.tools.push(...this.orchestrator.getTools());
|
|
440
|
+
|
|
441
|
+
// Start scheduler
|
|
442
|
+
this.scheduler.start();
|
|
443
|
+
|
|
444
|
+
// Update CoCo's tool list in profile
|
|
445
|
+
this.bus.updateProfile('coco', { tools: this.tools.map(t => t.name) });
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
async handleRequest(msg) {
|
|
449
|
+
const method = msg.method;
|
|
450
|
+
const id = msg.id;
|
|
451
|
+
const params = msg.params || {};
|
|
452
|
+
|
|
453
|
+
switch (method) {
|
|
454
|
+
case 'initialize':
|
|
455
|
+
return this._respond(id, {
|
|
456
|
+
protocolVersion: '2024-11-05',
|
|
457
|
+
capabilities: { tools: {} },
|
|
458
|
+
serverInfo: { name: 'mcp-coco', version: '2.1.0' },
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
case 'notifications/initialized':
|
|
462
|
+
return null;
|
|
463
|
+
|
|
464
|
+
case 'tools/list':
|
|
465
|
+
return this._respond(id, { tools: this.tools });
|
|
466
|
+
|
|
467
|
+
case 'tools/call':
|
|
468
|
+
return await this._handleToolCall(id, params.name || '', params.arguments || {});
|
|
469
|
+
|
|
470
|
+
case 'shutdown':
|
|
471
|
+
return this._respond(id, null);
|
|
472
|
+
|
|
473
|
+
case 'ping':
|
|
474
|
+
return this._respond(id, { status: 'ok', agent: this.agentName });
|
|
475
|
+
|
|
476
|
+
default:
|
|
477
|
+
return this._error(id, -32601, `Method not found: ${method}`);
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
async _handleToolCall(id, tool, args) {
|
|
482
|
+
try {
|
|
483
|
+
let result;
|
|
484
|
+
|
|
485
|
+
switch (tool) {
|
|
486
|
+
// ── Hermes ──
|
|
487
|
+
case 'ask_hermes':
|
|
488
|
+
result = await this.bridge.askHermes(args.prompt || '', args.session_key || '');
|
|
489
|
+
break;
|
|
490
|
+
case 'hermes_send':
|
|
491
|
+
result = await this.bridge.sendMessage(args.target, args.message);
|
|
492
|
+
break;
|
|
493
|
+
case 'hermes_channels':
|
|
494
|
+
result = await this.bridge.listChannels(args.platform);
|
|
495
|
+
break;
|
|
496
|
+
case 'coco_health':
|
|
497
|
+
result = await this.bridge.healthCheck();
|
|
498
|
+
break;
|
|
499
|
+
|
|
500
|
+
// ── Agent Profiles & Registry ──
|
|
501
|
+
case 'agent_register': {
|
|
502
|
+
const metadata = {
|
|
503
|
+
capabilities: args.capabilities || [],
|
|
504
|
+
model: args.model || null,
|
|
505
|
+
tags: args.tags || [],
|
|
506
|
+
version: args.version || '1.0.0',
|
|
507
|
+
endpoints: args.endpoints || {},
|
|
508
|
+
tools: args.tools || [],
|
|
509
|
+
status: args.status || 'idle',
|
|
510
|
+
metadata: {},
|
|
511
|
+
};
|
|
512
|
+
if (args.description) metadata.description = args.description;
|
|
513
|
+
result = this.bus.registerAgent(args.name, args.description || 'MCP agent on CoCo bus', metadata);
|
|
514
|
+
break;
|
|
515
|
+
}
|
|
516
|
+
case 'agent_update_profile':
|
|
517
|
+
result = this.bus.updateProfile(args.name, args);
|
|
518
|
+
break;
|
|
519
|
+
case 'agent_get_profile':
|
|
520
|
+
result = { agent: this.bus.getProfile(args.name) };
|
|
521
|
+
break;
|
|
522
|
+
case 'agent_list':
|
|
523
|
+
result = { agents: this.bus.listAgents({
|
|
524
|
+
online_only: args.online_only,
|
|
525
|
+
capability: args.capability,
|
|
526
|
+
model: args.model,
|
|
527
|
+
tag: args.tag,
|
|
528
|
+
status: args.status,
|
|
529
|
+
search: args.search,
|
|
530
|
+
})};
|
|
531
|
+
break;
|
|
532
|
+
case 'agent_search': {
|
|
533
|
+
const agents = this.bus.listAgents({ search: args.query });
|
|
534
|
+
result = { query: args.query, count: agents.length, agents: agents.slice(0, args.max_results || 20) };
|
|
535
|
+
break;
|
|
536
|
+
}
|
|
537
|
+
case 'agent_heartbeat':
|
|
538
|
+
result = this.bus.heartbeat(args.name);
|
|
539
|
+
break;
|
|
540
|
+
case 'agent_set_status':
|
|
541
|
+
result = this.bus.setStatus(args.name, args.status);
|
|
542
|
+
break;
|
|
543
|
+
|
|
544
|
+
// ── Messages ──
|
|
545
|
+
case 'message_send':
|
|
546
|
+
result = this.bus.sendMessage(args.from, args.to, args.message, args.metadata);
|
|
547
|
+
// Trigger auto-reply rules after sending
|
|
548
|
+
if (result && result.sent) {
|
|
549
|
+
try {
|
|
550
|
+
this.orchestrator._checkRules({
|
|
551
|
+
id: result.message_id,
|
|
552
|
+
from: args.from,
|
|
553
|
+
to: args.to,
|
|
554
|
+
message: args.message,
|
|
555
|
+
metadata: args.metadata || {},
|
|
556
|
+
timestamp: new Date().toISOString(),
|
|
557
|
+
}, {
|
|
558
|
+
sendMessage: (from, to, msg) => this.bus.sendMessage(from, to, msg, { auto_reply: true }),
|
|
559
|
+
broadcastMessage: (from, msg) => this.bus.broadcastMessage(from, msg),
|
|
560
|
+
});
|
|
561
|
+
} catch (e) {
|
|
562
|
+
console.warn('[Auto-Reply] Error checking rules:', e.message);
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
break;
|
|
566
|
+
case 'message_broadcast':
|
|
567
|
+
result = this.bus.broadcastMessage(args.from, args.message);
|
|
568
|
+
break;
|
|
569
|
+
case 'message_fetch':
|
|
570
|
+
result = { messages: this.bus.fetchMessages(args.agent_name, args.limit, {
|
|
571
|
+
after_id: args.after_id,
|
|
572
|
+
from: args.from,
|
|
573
|
+
})};
|
|
574
|
+
break;
|
|
575
|
+
case 'message_wait':
|
|
576
|
+
this.bus.registerAgent(args.agent_name, `agent on bus`);
|
|
577
|
+
result = await this.bus.waitForMessages(args.agent_name, Math.min(args.timeout_ms || 30000, 60000));
|
|
578
|
+
break;
|
|
579
|
+
case 'message_delete':
|
|
580
|
+
result = this.bus.deleteMessages(args.agent_name, args.message_ids);
|
|
581
|
+
break;
|
|
582
|
+
|
|
583
|
+
// ── Channels ──
|
|
584
|
+
case 'channel_create':
|
|
585
|
+
result = this.bus.createChannel(args.channel_id, args.topic, args.created_by);
|
|
586
|
+
break;
|
|
587
|
+
case 'channel_join':
|
|
588
|
+
result = this.bus.joinChannel(args.channel_id, args.agent_name);
|
|
589
|
+
break;
|
|
590
|
+
case 'channel_leave':
|
|
591
|
+
result = this.bus.leaveChannel(args.channel_id, args.agent_name);
|
|
592
|
+
break;
|
|
593
|
+
case 'channel_send':
|
|
594
|
+
result = this.bus.channelSend(args.channel_id, args.from, args.message);
|
|
595
|
+
break;
|
|
596
|
+
case 'channel_history':
|
|
597
|
+
result = { messages: this.bus.channelHistory(args.channel_id, args.limit) };
|
|
598
|
+
break;
|
|
599
|
+
|
|
600
|
+
// ── System Events ──
|
|
601
|
+
case 'system_get_events':
|
|
602
|
+
result = { events: this.bus.getEvents({
|
|
603
|
+
since: args.since, category: args.category, type: args.type, limit: args.limit,
|
|
604
|
+
})};
|
|
605
|
+
break;
|
|
606
|
+
case 'system_wait_for_event':
|
|
607
|
+
result = await this.bus.waitForEvents(args.category, args.type, Math.min(args.timeout_ms || 30000, 60000));
|
|
608
|
+
break;
|
|
609
|
+
|
|
610
|
+
// ── Memory Layer ──
|
|
611
|
+
case 'memory_store':
|
|
612
|
+
result = { id: await this.memory.store(args.agent, {
|
|
613
|
+
content: args.content, key: args.key, namespace: args.namespace, metadata: args.metadata,
|
|
614
|
+
})};
|
|
615
|
+
break;
|
|
616
|
+
case 'memory_search':
|
|
617
|
+
result = { results: await this.memory.search(args.agent, args.query, {
|
|
618
|
+
limit: args.limit, namespace: args.namespace, threshold: args.threshold,
|
|
619
|
+
})};
|
|
620
|
+
break;
|
|
621
|
+
case 'memory_recall':
|
|
622
|
+
result = { memory: this.memory.recall(args.agent, args.id) };
|
|
623
|
+
break;
|
|
624
|
+
case 'memory_list':
|
|
625
|
+
result = { memories: this.memory.list(args.agent, { namespace: args.namespace, limit: args.limit, offset: args.offset }) };
|
|
626
|
+
break;
|
|
627
|
+
case 'memory_forget':
|
|
628
|
+
result = { deleted: this.memory.forget(args.agent, args.id) };
|
|
629
|
+
break;
|
|
630
|
+
case 'memory_stats':
|
|
631
|
+
result = this.memory.stats(args.agent);
|
|
632
|
+
break;
|
|
633
|
+
case 'memory_archive':
|
|
634
|
+
result = await this.memory.archive(args.agent, { maxAgeDays: args.max_age_days, deleteAfter: args.delete_after });
|
|
635
|
+
break;
|
|
636
|
+
case 'memory_configure':
|
|
637
|
+
result = this.memory.configure({ provider: args.provider, api_key: args.api_key, endpoint: args.endpoint, model: args.model });
|
|
638
|
+
break;
|
|
639
|
+
case 'memory_rebuild':
|
|
640
|
+
result = await this.memory.rebuildVectors(args.agent);
|
|
641
|
+
break;
|
|
642
|
+
case 'memory_clear':
|
|
643
|
+
result = { deleted: this.memory.clear(args.agent, args.namespace) };
|
|
644
|
+
break;
|
|
645
|
+
|
|
646
|
+
// ── Scheduler Tools ──
|
|
647
|
+
case 'scheduler_add':
|
|
648
|
+
result = this.scheduler.addJob(args);
|
|
649
|
+
break;
|
|
650
|
+
case 'scheduler_remove':
|
|
651
|
+
result = this.scheduler.removeJob(args.job_id);
|
|
652
|
+
break;
|
|
653
|
+
case 'scheduler_list':
|
|
654
|
+
result = { jobs: this.scheduler.listJobs() };
|
|
655
|
+
break;
|
|
656
|
+
|
|
657
|
+
// ── Auto-Reply Tools ──
|
|
658
|
+
case 'auto_reply_add':
|
|
659
|
+
result = this.orchestrator.addRule(args);
|
|
660
|
+
break;
|
|
661
|
+
case 'auto_reply_remove':
|
|
662
|
+
result = this.orchestrator.removeRule(args.rule_id);
|
|
663
|
+
break;
|
|
664
|
+
case 'auto_reply_list':
|
|
665
|
+
result = { rules: this.orchestrator.listRules() };
|
|
666
|
+
break;
|
|
667
|
+
|
|
668
|
+
// ── Workflow Tools ──
|
|
669
|
+
case 'workflow_create':
|
|
670
|
+
result = this.orchestrator.createWorkflow(args);
|
|
671
|
+
break;
|
|
672
|
+
case 'workflow_run':
|
|
673
|
+
result = await this.orchestrator.runWorkflow(args.workflow_id, args.input, args.timeout_ms);
|
|
674
|
+
break;
|
|
675
|
+
case 'workflow_remove':
|
|
676
|
+
result = this.orchestrator.removeWorkflow(args.workflow_id);
|
|
677
|
+
break;
|
|
678
|
+
case 'workflow_list':
|
|
679
|
+
result = { workflows: this.orchestrator.listWorkflows() };
|
|
680
|
+
break;
|
|
681
|
+
|
|
682
|
+
default:
|
|
683
|
+
return this._error(id, -32601, `Tool not found: ${tool}`);
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
return this._respond(id, {
|
|
687
|
+
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
688
|
+
isError: false,
|
|
689
|
+
});
|
|
690
|
+
} catch (err) {
|
|
691
|
+
return this._respond(id, {
|
|
692
|
+
content: [{ type: 'text', text: `Error: ${err.message}` }],
|
|
693
|
+
isError: true,
|
|
694
|
+
});
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
_respond(id, result) {
|
|
699
|
+
return { jsonrpc: '2.0', id, result };
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
_error(id, code, message) {
|
|
703
|
+
return { jsonrpc: '2.0', id, error: { code, message } };
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
async function runStdio() {
|
|
708
|
+
const server = new MCPServer();
|
|
709
|
+
const rl = readline.createInterface({ input: process.stdin });
|
|
710
|
+
|
|
711
|
+
for await (const line of rl) {
|
|
712
|
+
const trimmed = line.trim();
|
|
713
|
+
if (!trimmed) continue;
|
|
714
|
+
try {
|
|
715
|
+
const msg = JSON.parse(trimmed);
|
|
716
|
+
const resp = await server.handleRequest(msg);
|
|
717
|
+
if (resp) {
|
|
718
|
+
process.stdout.write(JSON.stringify(resp) + '\n');
|
|
719
|
+
}
|
|
720
|
+
} catch (err) {
|
|
721
|
+
const errorResp = {
|
|
722
|
+
jsonrpc: '2.0',
|
|
723
|
+
error: { code: -32700, message: `Parse error: ${err.message}` },
|
|
724
|
+
};
|
|
725
|
+
process.stdout.write(JSON.stringify(errorResp) + '\n');
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
module.exports = { MCPServer, runStdio };
|