@dudamel/mcp-agents 2026.924.1 → 2026.929.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.
- package/dist/index.js +1 -105
- package/dist/stdio-server.js +2 -121
- package/dist/tools/agents.d.ts.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -137,13 +137,7 @@ function createSurfaceServer(opts) {
|
|
|
137
137
|
const ctx = {
|
|
138
138
|
role: config.role,
|
|
139
139
|
readOnly: config.readOnly ?? false,
|
|
140
|
-
agentName: config.agentName
|
|
141
|
-
callChain: config.callChain,
|
|
142
|
-
turnId: config.turnId,
|
|
143
|
-
rootTurnId: config.rootTurnId,
|
|
144
|
-
conversationId: config.conversationId,
|
|
145
|
-
requesterUserId: config.requesterUserId,
|
|
146
|
-
responseTarget: config.responseTarget
|
|
140
|
+
agentName: config.agentName
|
|
147
141
|
};
|
|
148
142
|
const host = withReadOnlyFilter(server, ctx.readOnly);
|
|
149
143
|
for (const register of opts.registrars)
|
|
@@ -263,104 +257,6 @@ function registerAgentTools(server, client, ctx) {
|
|
|
263
257
|
return jsonResult(result);
|
|
264
258
|
}
|
|
265
259
|
);
|
|
266
|
-
server.registerTool(
|
|
267
|
-
"send-message-to-agent",
|
|
268
|
-
{
|
|
269
|
-
description: "Send a message to a PEER Dudamel agent and get their response. Peers are the agents listed in the Peer Agents section of your system prompt: separate agents with their own tools, memory and history, running in their own process. This is the ONLY way to reach them \u2014 your coding tool's own agent-messaging tool (e.g. SendMessage) sees only subagents spawned inside your session and cannot reach a peer. Use this to delegate tasks to or ask questions of another Dudamel agent.",
|
|
270
|
-
inputSchema: {
|
|
271
|
-
agentName: z.string().describe("Target agent name (as shown in the peer agents list)"),
|
|
272
|
-
message: z.string().describe("Message to send to the target agent")
|
|
273
|
-
},
|
|
274
|
-
annotations: { readOnlyHint: false, destructiveHint: false }
|
|
275
|
-
},
|
|
276
|
-
async ({ agentName, message }) => {
|
|
277
|
-
const result = await client.post(
|
|
278
|
-
`/api/agents/by-name/${encodeURIComponent(agentName)}/message`,
|
|
279
|
-
// The chain this turn arrived with, plus ourselves (Plan 43 C1). This used to send a
|
|
280
|
-
// hardcoded `depth: 0`, which made the server's depth guard decorative: nothing ever
|
|
281
|
-
// reported being deeper than zero, so A→B→C→D was unbounded and A→B→A unnoticed.
|
|
282
|
-
{
|
|
283
|
-
message,
|
|
284
|
-
callChain: [...ctx.callChain ?? [], ...ctx.agentName ? [ctx.agentName] : []],
|
|
285
|
-
fromAgent: ctx.agentName,
|
|
286
|
-
// Plan 43 C3: the callee's turn records WHICH turn asked, so a multi-agent
|
|
287
|
-
// interaction can be reconstructed as a tree instead of guessed from timestamps.
|
|
288
|
-
parentTurnId: ctx.turnId,
|
|
289
|
-
rootTurnId: ctx.rootTurnId ?? ctx.turnId
|
|
290
|
-
}
|
|
291
|
-
);
|
|
292
|
-
return jsonResult(result);
|
|
293
|
-
}
|
|
294
|
-
);
|
|
295
|
-
server.registerTool(
|
|
296
|
-
"notify-agent",
|
|
297
|
-
{
|
|
298
|
-
description: "Hand control back to the conversation that launched THIS workflow run. Use it to report an outcome or to ask the user something through the agent, from a workflow step. Only works from inside a workflow execution, and only for the conversation that launched it \u2014 pass the ids from `{{ trigger.callback }}`. Nobody is blocked waiting: the message is delivered and the agent answers in its own channel.",
|
|
299
|
-
inputSchema: {
|
|
300
|
-
conversationId: z.string().describe("From `{{ trigger.callback.conversationId }}` \u2014 the conversation to resume"),
|
|
301
|
-
message: z.string().describe("What to tell the agent (it will act on it, not echo it)")
|
|
302
|
-
},
|
|
303
|
-
annotations: { readOnlyHint: false, destructiveHint: false }
|
|
304
|
-
},
|
|
305
|
-
async ({ conversationId, message }) => {
|
|
306
|
-
const executionWorkflowId = process.env["DUDAMEL_WORKFLOW_ID"];
|
|
307
|
-
if (!executionWorkflowId) {
|
|
308
|
-
throw new Error(
|
|
309
|
-
"notify-agent requires a workflow execution context (DUDAMEL_WORKFLOW_ID) \u2014 it only works from a workflow step, not from an agent conversation."
|
|
310
|
-
);
|
|
311
|
-
}
|
|
312
|
-
const result = await client.post("/api/agents/notify", {
|
|
313
|
-
conversationId,
|
|
314
|
-
executionWorkflowId,
|
|
315
|
-
message,
|
|
316
|
-
callChain: ctx.callChain
|
|
317
|
-
});
|
|
318
|
-
return jsonResult(result);
|
|
319
|
-
}
|
|
320
|
-
);
|
|
321
|
-
server.registerTool(
|
|
322
|
-
"ask-agent",
|
|
323
|
-
{
|
|
324
|
-
description: 'Ask the human a question THROUGH the agent, from a workflow step, and park this run until they answer. Pair it with a `waitForSignal` step on the same `signalName`. Unlike a fixed approval prompt, the agent turns a free-form answer ("s\xED dale, pero solo staging") into the structured payload your signal expects. Only works inside a workflow execution, and only for the conversation that launched it \u2014 pass the id from `{{ trigger.callback.conversationId }}`.',
|
|
325
|
-
inputSchema: {
|
|
326
|
-
conversationId: z.string().describe("From `{{ trigger.callback.conversationId }}` \u2014 where to ask"),
|
|
327
|
-
question: z.string().describe("What to ask the human, in their language"),
|
|
328
|
-
signalName: z.string().optional().describe("Signal your waitForSignal step listens on (default: humanApproval)")
|
|
329
|
-
},
|
|
330
|
-
annotations: { readOnlyHint: false, destructiveHint: false }
|
|
331
|
-
},
|
|
332
|
-
async ({ conversationId, question, signalName }) => {
|
|
333
|
-
const executionWorkflowId = process.env["DUDAMEL_WORKFLOW_ID"];
|
|
334
|
-
if (!executionWorkflowId) {
|
|
335
|
-
throw new Error(
|
|
336
|
-
"ask-agent requires a workflow execution context (DUDAMEL_WORKFLOW_ID) \u2014 it only works from a workflow step, not from an agent conversation."
|
|
337
|
-
);
|
|
338
|
-
}
|
|
339
|
-
const result = await client.post("/api/conversation-waits", {
|
|
340
|
-
conversationId,
|
|
341
|
-
executionWorkflowId,
|
|
342
|
-
question,
|
|
343
|
-
signalName: signalName ?? "humanApproval",
|
|
344
|
-
callChain: ctx.callChain
|
|
345
|
-
});
|
|
346
|
-
return jsonResult(result);
|
|
347
|
-
}
|
|
348
|
-
);
|
|
349
|
-
server.registerTool(
|
|
350
|
-
"answer-workflow",
|
|
351
|
-
{
|
|
352
|
-
description: "Unblock a workflow that is waiting on the human, with the answer turned into the payload it expects. Call it once you actually have the answer \u2014 translating what the person said into structured fields is your job here, and the reason a workflow asks through you instead of posting a fixed form.",
|
|
353
|
-
inputSchema: {
|
|
354
|
-
waitId: z.string().describe("The waitId the workflow gave you when it asked"),
|
|
355
|
-
payload: z.record(z.unknown()).optional().describe('Structured answer for the signal, e.g. {"approved": true, "env": "staging"}')
|
|
356
|
-
},
|
|
357
|
-
annotations: { readOnlyHint: false, destructiveHint: false }
|
|
358
|
-
},
|
|
359
|
-
async ({ waitId, payload }) => {
|
|
360
|
-
const result = await client.post("/api/conversation-waits/answer", { waitId, payload });
|
|
361
|
-
return jsonResult(result);
|
|
362
|
-
}
|
|
363
|
-
);
|
|
364
260
|
server.registerTool(
|
|
365
261
|
"update-agent",
|
|
366
262
|
{
|
package/dist/stdio-server.js
CHANGED
|
@@ -168,13 +168,7 @@ function createSurfaceServer(opts) {
|
|
|
168
168
|
const ctx2 = {
|
|
169
169
|
role: config.role,
|
|
170
170
|
readOnly: config.readOnly ?? false,
|
|
171
|
-
agentName: config.agentName
|
|
172
|
-
callChain: config.callChain,
|
|
173
|
-
turnId: config.turnId,
|
|
174
|
-
rootTurnId: config.rootTurnId,
|
|
175
|
-
conversationId: config.conversationId,
|
|
176
|
-
requesterUserId: config.requesterUserId,
|
|
177
|
-
responseTarget: config.responseTarget
|
|
171
|
+
agentName: config.agentName
|
|
178
172
|
};
|
|
179
173
|
const host = withReadOnlyFilter(server2, ctx2.readOnly);
|
|
180
174
|
for (const register of opts.registrars)
|
|
@@ -184,28 +178,13 @@ function createSurfaceServer(opts) {
|
|
|
184
178
|
|
|
185
179
|
// ../common/dist/stdio.js
|
|
186
180
|
function resolveStdioContext() {
|
|
187
|
-
let responseTarget;
|
|
188
|
-
const rawTarget = process.env["DUDAMEL_RESPONSE_TARGET"];
|
|
189
|
-
if (rawTarget) {
|
|
190
|
-
try {
|
|
191
|
-
responseTarget = JSON.parse(rawTarget);
|
|
192
|
-
} catch {
|
|
193
|
-
process.stderr.write("Warning: DUDAMEL_RESPONSE_TARGET is not valid JSON \u2014 ignoring.\n");
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
181
|
return {
|
|
197
182
|
apiUrl: process.env["DUDAMEL_API_URL"] ?? "http://localhost:3000",
|
|
198
183
|
apiKey: process.env["DUDAMEL_API_KEY"],
|
|
199
184
|
envRole: process.env["DUDAMEL_USER_ROLE"],
|
|
200
185
|
// read/write access is per agent-assignment (Plan 52); the spawner injects the mode.
|
|
201
186
|
readOnly: process.env["DUDAMEL_MCP_MODE"] === "readonly",
|
|
202
|
-
agentName: process.env["DUDAMEL_AGENT_NAME"]
|
|
203
|
-
callChain: (process.env["DUDAMEL_CALL_CHAIN"] ?? "").split(",").map((s) => s.trim()).filter(Boolean),
|
|
204
|
-
turnId: process.env["DUDAMEL_TURN_ID"],
|
|
205
|
-
rootTurnId: process.env["DUDAMEL_ROOT_TURN_ID"],
|
|
206
|
-
conversationId: process.env["DUDAMEL_CONVERSATION_ID"],
|
|
207
|
-
requesterUserId: process.env["DUDAMEL_REQUESTER_USER_ID"],
|
|
208
|
-
responseTarget
|
|
187
|
+
agentName: process.env["DUDAMEL_AGENT_NAME"]
|
|
209
188
|
};
|
|
210
189
|
}
|
|
211
190
|
|
|
@@ -321,104 +300,6 @@ function registerAgentTools(server2, client, ctx2) {
|
|
|
321
300
|
return jsonResult(result);
|
|
322
301
|
}
|
|
323
302
|
);
|
|
324
|
-
server2.registerTool(
|
|
325
|
-
"send-message-to-agent",
|
|
326
|
-
{
|
|
327
|
-
description: "Send a message to a PEER Dudamel agent and get their response. Peers are the agents listed in the Peer Agents section of your system prompt: separate agents with their own tools, memory and history, running in their own process. This is the ONLY way to reach them \u2014 your coding tool's own agent-messaging tool (e.g. SendMessage) sees only subagents spawned inside your session and cannot reach a peer. Use this to delegate tasks to or ask questions of another Dudamel agent.",
|
|
328
|
-
inputSchema: {
|
|
329
|
-
agentName: z.string().describe("Target agent name (as shown in the peer agents list)"),
|
|
330
|
-
message: z.string().describe("Message to send to the target agent")
|
|
331
|
-
},
|
|
332
|
-
annotations: { readOnlyHint: false, destructiveHint: false }
|
|
333
|
-
},
|
|
334
|
-
async ({ agentName, message }) => {
|
|
335
|
-
const result = await client.post(
|
|
336
|
-
`/api/agents/by-name/${encodeURIComponent(agentName)}/message`,
|
|
337
|
-
// The chain this turn arrived with, plus ourselves (Plan 43 C1). This used to send a
|
|
338
|
-
// hardcoded `depth: 0`, which made the server's depth guard decorative: nothing ever
|
|
339
|
-
// reported being deeper than zero, so A→B→C→D was unbounded and A→B→A unnoticed.
|
|
340
|
-
{
|
|
341
|
-
message,
|
|
342
|
-
callChain: [...ctx2.callChain ?? [], ...ctx2.agentName ? [ctx2.agentName] : []],
|
|
343
|
-
fromAgent: ctx2.agentName,
|
|
344
|
-
// Plan 43 C3: the callee's turn records WHICH turn asked, so a multi-agent
|
|
345
|
-
// interaction can be reconstructed as a tree instead of guessed from timestamps.
|
|
346
|
-
parentTurnId: ctx2.turnId,
|
|
347
|
-
rootTurnId: ctx2.rootTurnId ?? ctx2.turnId
|
|
348
|
-
}
|
|
349
|
-
);
|
|
350
|
-
return jsonResult(result);
|
|
351
|
-
}
|
|
352
|
-
);
|
|
353
|
-
server2.registerTool(
|
|
354
|
-
"notify-agent",
|
|
355
|
-
{
|
|
356
|
-
description: "Hand control back to the conversation that launched THIS workflow run. Use it to report an outcome or to ask the user something through the agent, from a workflow step. Only works from inside a workflow execution, and only for the conversation that launched it \u2014 pass the ids from `{{ trigger.callback }}`. Nobody is blocked waiting: the message is delivered and the agent answers in its own channel.",
|
|
357
|
-
inputSchema: {
|
|
358
|
-
conversationId: z.string().describe("From `{{ trigger.callback.conversationId }}` \u2014 the conversation to resume"),
|
|
359
|
-
message: z.string().describe("What to tell the agent (it will act on it, not echo it)")
|
|
360
|
-
},
|
|
361
|
-
annotations: { readOnlyHint: false, destructiveHint: false }
|
|
362
|
-
},
|
|
363
|
-
async ({ conversationId, message }) => {
|
|
364
|
-
const executionWorkflowId = process.env["DUDAMEL_WORKFLOW_ID"];
|
|
365
|
-
if (!executionWorkflowId) {
|
|
366
|
-
throw new Error(
|
|
367
|
-
"notify-agent requires a workflow execution context (DUDAMEL_WORKFLOW_ID) \u2014 it only works from a workflow step, not from an agent conversation."
|
|
368
|
-
);
|
|
369
|
-
}
|
|
370
|
-
const result = await client.post("/api/agents/notify", {
|
|
371
|
-
conversationId,
|
|
372
|
-
executionWorkflowId,
|
|
373
|
-
message,
|
|
374
|
-
callChain: ctx2.callChain
|
|
375
|
-
});
|
|
376
|
-
return jsonResult(result);
|
|
377
|
-
}
|
|
378
|
-
);
|
|
379
|
-
server2.registerTool(
|
|
380
|
-
"ask-agent",
|
|
381
|
-
{
|
|
382
|
-
description: 'Ask the human a question THROUGH the agent, from a workflow step, and park this run until they answer. Pair it with a `waitForSignal` step on the same `signalName`. Unlike a fixed approval prompt, the agent turns a free-form answer ("s\xED dale, pero solo staging") into the structured payload your signal expects. Only works inside a workflow execution, and only for the conversation that launched it \u2014 pass the id from `{{ trigger.callback.conversationId }}`.',
|
|
383
|
-
inputSchema: {
|
|
384
|
-
conversationId: z.string().describe("From `{{ trigger.callback.conversationId }}` \u2014 where to ask"),
|
|
385
|
-
question: z.string().describe("What to ask the human, in their language"),
|
|
386
|
-
signalName: z.string().optional().describe("Signal your waitForSignal step listens on (default: humanApproval)")
|
|
387
|
-
},
|
|
388
|
-
annotations: { readOnlyHint: false, destructiveHint: false }
|
|
389
|
-
},
|
|
390
|
-
async ({ conversationId, question, signalName }) => {
|
|
391
|
-
const executionWorkflowId = process.env["DUDAMEL_WORKFLOW_ID"];
|
|
392
|
-
if (!executionWorkflowId) {
|
|
393
|
-
throw new Error(
|
|
394
|
-
"ask-agent requires a workflow execution context (DUDAMEL_WORKFLOW_ID) \u2014 it only works from a workflow step, not from an agent conversation."
|
|
395
|
-
);
|
|
396
|
-
}
|
|
397
|
-
const result = await client.post("/api/conversation-waits", {
|
|
398
|
-
conversationId,
|
|
399
|
-
executionWorkflowId,
|
|
400
|
-
question,
|
|
401
|
-
signalName: signalName ?? "humanApproval",
|
|
402
|
-
callChain: ctx2.callChain
|
|
403
|
-
});
|
|
404
|
-
return jsonResult(result);
|
|
405
|
-
}
|
|
406
|
-
);
|
|
407
|
-
server2.registerTool(
|
|
408
|
-
"answer-workflow",
|
|
409
|
-
{
|
|
410
|
-
description: "Unblock a workflow that is waiting on the human, with the answer turned into the payload it expects. Call it once you actually have the answer \u2014 translating what the person said into structured fields is your job here, and the reason a workflow asks through you instead of posting a fixed form.",
|
|
411
|
-
inputSchema: {
|
|
412
|
-
waitId: z.string().describe("The waitId the workflow gave you when it asked"),
|
|
413
|
-
payload: z.record(z.unknown()).optional().describe('Structured answer for the signal, e.g. {"approved": true, "env": "staging"}')
|
|
414
|
-
},
|
|
415
|
-
annotations: { readOnlyHint: false, destructiveHint: false }
|
|
416
|
-
},
|
|
417
|
-
async ({ waitId, payload }) => {
|
|
418
|
-
const result = await client.post("/api/conversation-waits/answer", { waitId, payload });
|
|
419
|
-
return jsonResult(result);
|
|
420
|
-
}
|
|
421
|
-
);
|
|
422
303
|
server2.registerTool(
|
|
423
304
|
"update-agent",
|
|
424
305
|
{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agents.d.ts","sourceRoot":"","sources":["../../src/tools/agents.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAGvD,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,gBAAgB,EACxB,GAAG,EAAE,WAAW,GACf,IAAI,
|
|
1
|
+
{"version":3,"file":"agents.d.ts","sourceRoot":"","sources":["../../src/tools/agents.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAGvD,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,gBAAgB,EACxB,GAAG,EAAE,WAAW,GACf,IAAI,CAmRN"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dudamel/mcp-agents",
|
|
3
|
-
"version": "2026.
|
|
3
|
+
"version": "2026.929.1",
|
|
4
4
|
"description": "Dudamel agents MCP — stdio client for Dudamel's agents management surface over its REST API.",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE.md",
|
|
6
6
|
"type": "module",
|
|
@@ -32,8 +32,8 @@
|
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
34
|
"esbuild": "^0.25.0",
|
|
35
|
-
"@dudamel/mcp-common": "0.1.0",
|
|
36
35
|
"@dudamel/contracts": "0.1.0",
|
|
36
|
+
"@dudamel/mcp-common": "0.1.0",
|
|
37
37
|
"@dudamel/mcp-tool-contract": "0.1.0"
|
|
38
38
|
},
|
|
39
39
|
"scripts": {
|