@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 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
  {
@@ -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,CAiaN"}
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.924.1",
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": {