meshwire 0.1.4 → 0.1.5
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/package.json +1 -1
- package/src/harness/copilot.js +42 -17
- package/src/mcp/server.js +41 -19
package/package.json
CHANGED
package/src/harness/copilot.js
CHANGED
|
@@ -16,7 +16,7 @@ const EXTENSION_FILE = join(EXTENSION_DIR, 'meshwire.mjs');
|
|
|
16
16
|
|
|
17
17
|
// The generated extension template
|
|
18
18
|
function buildExtensionSource({ meshwireUrl }) {
|
|
19
|
-
return `// MeshWire
|
|
19
|
+
return `// MeshWire ΓÇö Copilot CLI Extension
|
|
20
20
|
// Auto-generated by: meshwire init --harness copilot
|
|
21
21
|
// Reads credentials from ~/.meshwire/credentials.json
|
|
22
22
|
// Reads workspace mesh from .mesh.json in the current directory
|
|
@@ -61,7 +61,7 @@ async function api(path, method = 'GET', body = null) {
|
|
|
61
61
|
return res.json();
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
//
|
|
64
|
+
// ΓöÇΓöÇΓöÇ Tool Definitions ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ
|
|
65
65
|
|
|
66
66
|
export const tools = [
|
|
67
67
|
{
|
|
@@ -122,6 +122,31 @@ export const tools = [
|
|
|
122
122
|
},
|
|
123
123
|
},
|
|
124
124
|
|
|
125
|
+
{
|
|
126
|
+
name: 'mesh_reply_to_message',
|
|
127
|
+
description: 'Reply to a specific message in the mesh. Sends the reply to the original sender and links it via metadata.reply_to.',
|
|
128
|
+
parameters: {
|
|
129
|
+
type: 'object',
|
|
130
|
+
properties: {
|
|
131
|
+
message_id: { type: 'integer', description: 'message_id of the original message to reply to' },
|
|
132
|
+
content: { type: 'string', description: 'Reply content (max 10KB)' },
|
|
133
|
+
},
|
|
134
|
+
required: ['message_id', 'content'],
|
|
135
|
+
},
|
|
136
|
+
async execute({ message_id, content }) {
|
|
137
|
+
const creds = loadCreds();
|
|
138
|
+
const meshJson = loadMeshJson();
|
|
139
|
+
if (!meshJson?.mesh_id) throw new Error('No mesh configured. Add .mesh.json to this workspace.');
|
|
140
|
+
const sender_id = creds?.agentId || globalThis.__meshwire_agent_id__ || 'copilot-cli';
|
|
141
|
+
const reply = await api(
|
|
142
|
+
'/mesh/{meshId}/messages/' + message_id + '/reply',
|
|
143
|
+
'POST',
|
|
144
|
+
{ sender_id, content }
|
|
145
|
+
);
|
|
146
|
+
return { sent: true, reply_id: reply.message_id, reply_to: message_id, to: reply.recipient_id };
|
|
147
|
+
},
|
|
148
|
+
},
|
|
149
|
+
|
|
125
150
|
{
|
|
126
151
|
name: 'mesh_list_agents',
|
|
127
152
|
description: 'List all agents currently registered in this workspace mesh.',
|
|
@@ -163,10 +188,10 @@ export const tools = [
|
|
|
163
188
|
},
|
|
164
189
|
];
|
|
165
190
|
|
|
166
|
-
//
|
|
191
|
+
// ΓöÇΓöÇΓöÇ Hooks ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ
|
|
167
192
|
|
|
168
193
|
export const hooks = {
|
|
169
|
-
// Called on session start
|
|
194
|
+
// Called on session start ΓÇö register this agent in the mesh
|
|
170
195
|
async onSessionStart({ agentName } = {}) {
|
|
171
196
|
const creds = loadCreds();
|
|
172
197
|
const meshJson = loadMeshJson();
|
|
@@ -190,7 +215,7 @@ export const hooks = {
|
|
|
190
215
|
}
|
|
191
216
|
},
|
|
192
217
|
|
|
193
|
-
// Called periodically
|
|
218
|
+
// Called periodically ΓÇö keep heartbeat alive
|
|
194
219
|
async onHeartbeat() {
|
|
195
220
|
const creds = loadCreds();
|
|
196
221
|
const meshJson = loadMeshJson();
|
|
@@ -203,11 +228,11 @@ export const hooks = {
|
|
|
203
228
|
}
|
|
204
229
|
|
|
205
230
|
export async function setupCopilot({ meshId, agentName, meshwireUrl, workspaceName }) {
|
|
206
|
-
console.log('\n' + chalk.bold('
|
|
231
|
+
console.log('\n' + chalk.bold('⚙️ Setting up Copilot CLI harness') + '\n');
|
|
207
232
|
|
|
208
233
|
const creds = readCredentials();
|
|
209
234
|
if (!creds?.token) {
|
|
210
|
-
console.error(chalk.red('
|
|
235
|
+
console.error(chalk.red(' Γ£ù Not authenticated. Run `meshwire login` first.\n'));
|
|
211
236
|
process.exit(1);
|
|
212
237
|
}
|
|
213
238
|
|
|
@@ -221,12 +246,12 @@ export async function setupCopilot({ meshId, agentName, meshwireUrl, workspaceNa
|
|
|
221
246
|
};
|
|
222
247
|
|
|
223
248
|
if (!meshJsonData.mesh_id) {
|
|
224
|
-
console.error(chalk.red('
|
|
249
|
+
console.error(chalk.red(' Γ£ù No mesh ID. Create a mesh first: meshwire mesh create\n'));
|
|
225
250
|
process.exit(1);
|
|
226
251
|
}
|
|
227
252
|
|
|
228
253
|
writeMeshJson(meshJsonData);
|
|
229
|
-
console.log(chalk.green('
|
|
254
|
+
console.log(chalk.green(' Γ£ô .mesh.json written'));
|
|
230
255
|
console.log(chalk.dim(` mesh_id: ${meshJsonData.mesh_id}`));
|
|
231
256
|
console.log(chalk.dim(` agent_name: ${meshJsonData.agent_name}`));
|
|
232
257
|
console.log(chalk.dim(` harness: copilot`));
|
|
@@ -235,7 +260,7 @@ export async function setupCopilot({ meshId, agentName, meshwireUrl, workspaceNa
|
|
|
235
260
|
mkdirSync(EXTENSION_DIR, { recursive: true });
|
|
236
261
|
const source = buildExtensionSource({ meshwireUrl: meshwireUrl || 'https://meshwire.io' });
|
|
237
262
|
writeFileSync(EXTENSION_FILE, source, 'utf8');
|
|
238
|
-
console.log(chalk.green(`\n
|
|
263
|
+
console.log(chalk.green(`\n Γ£ô Extension installed at ${EXTENSION_FILE}`));
|
|
239
264
|
|
|
240
265
|
// 3. Register the agent in the mesh now
|
|
241
266
|
console.log(chalk.dim('\n Registering agent in mesh...'));
|
|
@@ -247,19 +272,19 @@ export async function setupCopilot({ meshId, agentName, meshwireUrl, workspaceNa
|
|
|
247
272
|
workspace: meshJsonData.workspace_name,
|
|
248
273
|
metadata: { platform: 'copilot-cli', harness: 'meshwire' },
|
|
249
274
|
});
|
|
250
|
-
console.log(chalk.green(`
|
|
275
|
+
console.log(chalk.green(` Γ£ô Registered as ${agent.name} (${agent.agent_id})`));
|
|
251
276
|
} catch (err) {
|
|
252
|
-
console.log(chalk.yellow(`
|
|
277
|
+
console.log(chalk.yellow(` ΓÜá Could not register now: ${err.message}`));
|
|
253
278
|
console.log(chalk.dim(' The extension will auto-register on next Copilot CLI session.'));
|
|
254
279
|
}
|
|
255
280
|
|
|
256
281
|
// 4. Print summary
|
|
257
|
-
console.log('\n' + chalk.bold.green('
|
|
282
|
+
console.log('\n' + chalk.bold.green(' ✅ Copilot harness ready!\n'));
|
|
258
283
|
console.log(chalk.dim(' The extension provides these tools in every Copilot session:'));
|
|
259
|
-
console.log(chalk.cyan(' mesh_send_message') + chalk.dim('
|
|
260
|
-
console.log(chalk.cyan(' mesh_get_messages') + chalk.dim('
|
|
261
|
-
console.log(chalk.cyan(' mesh_list_agents') + chalk.dim('
|
|
262
|
-
console.log(chalk.cyan(' mesh_status') + chalk.dim('
|
|
284
|
+
console.log(chalk.cyan(' mesh_send_message') + chalk.dim(' ΓÇö broadcast or target a specific agent'));
|
|
285
|
+
console.log(chalk.cyan(' mesh_get_messages') + chalk.dim(' ΓÇö poll for new messages'));
|
|
286
|
+
console.log(chalk.cyan(' mesh_list_agents') + chalk.dim(' ΓÇö discover agents in the mesh'));
|
|
287
|
+
console.log(chalk.cyan(' mesh_status') + chalk.dim(' ΓÇö check connection health'));
|
|
263
288
|
console.log('');
|
|
264
289
|
console.log(chalk.dim(' The .mesh.json file in this repo defines which mesh Copilot connects to.'));
|
|
265
290
|
console.log(chalk.dim(' Commit .mesh.json so your teammates auto-join the same mesh.\n'));
|
package/src/mcp/server.js
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
// meshwire mcp
|
|
1
|
+
// meshwire mcp ΓÇö MCP stdio server for Copilot CLI and MCP-compatible agents
|
|
2
2
|
// Starts an MCP server over stdio that exposes MeshWire tools.
|
|
3
3
|
// Usage: meshwire mcp --mesh <meshId> --agent <name>
|
|
4
4
|
//
|
|
5
5
|
// Tool inventory:
|
|
6
|
-
// meshwire_send_message
|
|
7
|
-
// meshwire_get_messages
|
|
8
|
-
// meshwire_register_agent
|
|
9
|
-
// meshwire_list_agents
|
|
10
|
-
// meshwire_heartbeat
|
|
11
|
-
// meshwire_mesh_info
|
|
6
|
+
// meshwire_send_message ΓÇö send a message to the mesh
|
|
7
|
+
// meshwire_get_messages ΓÇö long-poll for incoming messages
|
|
8
|
+
// meshwire_register_agent ΓÇö register as an agent (auto-called on start)
|
|
9
|
+
// meshwire_list_agents ΓÇö list agents in the mesh
|
|
10
|
+
// meshwire_heartbeat ΓÇö send heartbeat to stay active
|
|
11
|
+
// meshwire_mesh_info ΓÇö get mesh metadata
|
|
12
12
|
|
|
13
13
|
import chalk from 'chalk';
|
|
14
14
|
import { readConfig, writeConfig } from '../config.js';
|
|
15
15
|
import { MeshWireClient } from '../api.js';
|
|
16
16
|
|
|
17
|
-
//
|
|
17
|
+
// ΓöÇΓöÇΓöÇ MCP Protocol Helpers ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ
|
|
18
18
|
|
|
19
19
|
function mcpResponse(id, result) {
|
|
20
20
|
return JSON.stringify({ jsonrpc: '2.0', id, result });
|
|
@@ -28,7 +28,7 @@ function mcpNotification(method, params) {
|
|
|
28
28
|
return JSON.stringify({ jsonrpc: '2.0', method, params });
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
//
|
|
31
|
+
// ΓöÇΓöÇΓöÇ Tool Definitions ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ
|
|
32
32
|
|
|
33
33
|
function buildTools(meshId) {
|
|
34
34
|
return [
|
|
@@ -57,6 +57,18 @@ function buildTools(meshId) {
|
|
|
57
57
|
},
|
|
58
58
|
},
|
|
59
59
|
},
|
|
60
|
+
{
|
|
61
|
+
name: 'meshwire_reply_to_message',
|
|
62
|
+
description: `Reply to a specific message in mesh '${meshId}'. The reply is routed to the original sender and tagged with metadata.reply_to.`,
|
|
63
|
+
inputSchema: {
|
|
64
|
+
type: 'object',
|
|
65
|
+
properties: {
|
|
66
|
+
message_id: { type: 'integer', description: 'message_id of the original message' },
|
|
67
|
+
content: { type: 'string', description: 'Reply content (max 10KB)' },
|
|
68
|
+
},
|
|
69
|
+
required: ['message_id', 'content'],
|
|
70
|
+
},
|
|
71
|
+
},
|
|
60
72
|
{
|
|
61
73
|
name: 'meshwire_list_agents',
|
|
62
74
|
description: `List all agents currently registered in mesh '${meshId}'.`,
|
|
@@ -75,7 +87,7 @@ function buildTools(meshId) {
|
|
|
75
87
|
];
|
|
76
88
|
}
|
|
77
89
|
|
|
78
|
-
//
|
|
90
|
+
// ΓöÇΓöÇΓöÇ MCP Server ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ
|
|
79
91
|
|
|
80
92
|
export async function cmdMcp(opts) {
|
|
81
93
|
const config = readConfig();
|
|
@@ -106,7 +118,7 @@ export async function cmdMcp(opts) {
|
|
|
106
118
|
writeConfig({ agentId, agentName });
|
|
107
119
|
process.stderr.write(`meshwire-mcp: registered as ${agentName} (${agentId})\n`);
|
|
108
120
|
} catch (err) {
|
|
109
|
-
process.stderr.write(`meshwire-mcp: agent registration failed
|
|
121
|
+
process.stderr.write(`meshwire-mcp: agent registration failed ΓÇö ${err.message}\n`);
|
|
110
122
|
}
|
|
111
123
|
|
|
112
124
|
// Heartbeat every 20s
|
|
@@ -119,7 +131,7 @@ export async function cmdMcp(opts) {
|
|
|
119
131
|
process.on('SIGINT', () => { clearInterval(hbInterval); process.exit(0); });
|
|
120
132
|
process.on('SIGTERM', () => { clearInterval(hbInterval); process.exit(0); });
|
|
121
133
|
|
|
122
|
-
// MCP stdio protocol
|
|
134
|
+
// MCP stdio protocol ΓÇö read JSON-RPC from stdin, write to stdout
|
|
123
135
|
process.stdin.setEncoding('utf8');
|
|
124
136
|
let buf = '';
|
|
125
137
|
|
|
@@ -137,7 +149,7 @@ export async function cmdMcp(opts) {
|
|
|
137
149
|
const response = await handleMcpMessage(msg, { client, meshId, agentId, agentName });
|
|
138
150
|
if (response) process.stdout.write(response + '\n');
|
|
139
151
|
} catch (err) {
|
|
140
|
-
process.stderr.write(`meshwire-mcp: parse error
|
|
152
|
+
process.stderr.write(`meshwire-mcp: parse error ΓÇö ${err.message}\n`);
|
|
141
153
|
}
|
|
142
154
|
}
|
|
143
155
|
});
|
|
@@ -149,7 +161,7 @@ async function handleMcpMessage(msg, { client, meshId, agentId }) {
|
|
|
149
161
|
const { id, method, params } = msg;
|
|
150
162
|
|
|
151
163
|
switch (method) {
|
|
152
|
-
//
|
|
164
|
+
// ΓöÇΓöÇ Capability negotiation ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ
|
|
153
165
|
case 'initialize':
|
|
154
166
|
return mcpResponse(id, {
|
|
155
167
|
protocolVersion: '2024-11-05',
|
|
@@ -160,11 +172,11 @@ async function handleMcpMessage(msg, { client, meshId, agentId }) {
|
|
|
160
172
|
case 'notifications/initialized':
|
|
161
173
|
return null; // Notification, no response
|
|
162
174
|
|
|
163
|
-
//
|
|
175
|
+
// ΓöÇΓöÇ Tool listing ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ
|
|
164
176
|
case 'tools/list':
|
|
165
177
|
return mcpResponse(id, { tools: buildTools(meshId) });
|
|
166
178
|
|
|
167
|
-
//
|
|
179
|
+
// ΓöÇΓöÇ Tool execution ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ
|
|
168
180
|
case 'tools/call': {
|
|
169
181
|
const { name, arguments: args = {} } = params;
|
|
170
182
|
try {
|
|
@@ -202,20 +214,30 @@ async function executeTool(name, args, { client, meshId, agentId }) {
|
|
|
202
214
|
});
|
|
203
215
|
if (count === 0) return 'No new messages.';
|
|
204
216
|
return messages.map((m) =>
|
|
205
|
-
`[${m.message_id}] from:${m.sender_id} to:${m.recipient_id}
|
|
217
|
+
`[${m.message_id}] from:${m.sender_id} to:${m.recipient_id} ΓÇö ${m.content}`
|
|
206
218
|
).join('\n');
|
|
207
219
|
}
|
|
208
220
|
|
|
221
|
+
case 'meshwire_reply_to_message': {
|
|
222
|
+
if (!args.message_id) throw new Error('message_id is required');
|
|
223
|
+
if (!args.content) throw new Error('content is required');
|
|
224
|
+
const reply = await client.replyToMessage(meshId, args.message_id, {
|
|
225
|
+
senderId: agentId,
|
|
226
|
+
content: args.content,
|
|
227
|
+
});
|
|
228
|
+
return `Reply sent (id: ${reply.message_id}) to ${reply.recipient_id} (in reply to ${args.message_id})`;
|
|
229
|
+
}
|
|
230
|
+
|
|
209
231
|
case 'meshwire_list_agents': {
|
|
210
232
|
const { agents, count } = await client.listAgents(meshId);
|
|
211
233
|
if (count === 0) return 'No agents registered.';
|
|
212
234
|
return agents.map((a) =>
|
|
213
|
-
`${a.status === 'active' ? '
|
|
235
|
+
`${a.status === 'active' ? 'ΓùÅ' : 'Γùï'} ${a.name} (${a.agent_id})`
|
|
214
236
|
).join('\n');
|
|
215
237
|
}
|
|
216
238
|
|
|
217
239
|
case 'meshwire_heartbeat': {
|
|
218
|
-
if (!agentId) return 'No agent ID
|
|
240
|
+
if (!agentId) return 'No agent ID ΓÇö run meshwire init first.';
|
|
219
241
|
await client.heartbeat(meshId, agentId);
|
|
220
242
|
return `Heartbeat sent (${new Date().toISOString()})`;
|
|
221
243
|
}
|