agent-orchestrator-mcp-server 0.8.8 → 0.8.10

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/README.md CHANGED
@@ -31,6 +31,7 @@ MCP server for PulseMCP's agent-orchestrator: a Claude Code + MCP-powered agent-
31
31
  | `action_session` | sessions | write | Perform actions: follow_up, pause, restart, archive, unarchive, change_mcp_servers, change_model, fork, refresh, refresh_all, update_notes, update_title, toggle_favorite, bulk_archive |
32
32
  | `manage_enqueued_messages` | sessions | write | Manage session message queue: list, get, create, update, delete, reorder, interrupt |
33
33
  | `manage_categories` | sessions | write | Manage dashboard categories that organize sessions: list, create, update, delete, reorder, set_session_category |
34
+ | `respond_to_elicitation` | sessions | write | Accept or decline a pending elicitation by its request_id, with optional structured content on accept |
34
35
  | `get_notifications` | notifications | read | Get/list notifications and badge count |
35
36
  | `send_push_notification` | notifications | write | Send a push notification about a session needing human attention |
36
37
  | `action_notification` | notifications | write | Mark read, mark all read, dismiss, dismiss all read notifications |
@@ -57,16 +58,16 @@ This server organizes tools into groups that can be selectively enabled or disab
57
58
 
58
59
  Control which tools are available via the `TOOL_GROUPS` environment variable:
59
60
 
60
- | Group | Description |
61
- | ------------------------ | -------------------------------------------------------------------------------------------------------------------- |
62
- | `sessions` | All session tools (read + write): search, get, configs, transcript_archive, start, action, enqueued msgs, categories |
63
- | `sessions_readonly` | Session tools (read only): quick_search_sessions, get_session, get_configs, get_transcript_archive |
64
- | `notifications` | All notification tools (read + write): get, send, mark read, dismiss |
65
- | `notifications_readonly` | Notification tools (read only): get_notifications |
66
- | `triggers` | All trigger tools (read + write): search, create, update, delete, toggle |
67
- | `triggers_readonly` | Trigger tools (read only): search_triggers |
68
- | `health` | All health tools (read + write): health report, CLI status, maintenance |
69
- | `health_readonly` | Health tools (read only): get_system_health |
61
+ | Group | Description |
62
+ | ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------- |
63
+ | `sessions` | All session tools (read + write): search, get, configs, transcript_archive, start, action, enqueued msgs, categories, elicitations |
64
+ | `sessions_readonly` | Session tools (read only): quick_search_sessions, get_session, get_configs, get_transcript_archive |
65
+ | `notifications` | All notification tools (read + write): get, send, mark read, dismiss |
66
+ | `notifications_readonly` | Notification tools (read only): get_notifications |
67
+ | `triggers` | All trigger tools (read + write): search, create, update, delete, toggle |
68
+ | `triggers_readonly` | Trigger tools (read only): search_triggers |
69
+ | `health` | All health tools (read + write): health report, CLI status, maintenance |
70
+ | `health_readonly` | Health tools (read only): get_system_health |
70
71
 
71
72
  **Examples:**
72
73
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-orchestrator-mcp-server",
3
- "version": "0.8.8",
3
+ "version": "0.8.10",
4
4
  "description": "Local implementation of agent-orchestrator MCP server",
5
5
  "main": "build/index.js",
6
6
  "type": "module",
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * A client for interacting with the Agent Orchestrator REST API.
5
5
  */
6
- import type { Session, Log, SubagentTranscript, SessionsResponse, SearchSessionsResponse, SessionActionResponse, LogsResponse, SubagentTranscriptsResponse, CreateSessionRequest, UpdateSessionRequest, CreateLogRequest, UpdateLogRequest, CreateSubagentTranscriptRequest, UpdateSubagentTranscriptRequest, SessionStatus, LogLevel, SubagentStatus, MCPServerInfo, AgentRootInfo, ConfigsResponse, SendPushNotificationResponse, EnqueuedMessage, EnqueuedMessagesResponse, EnqueuedMessageInterruptResponse, EnqueuedMessageStatus, Category, CategoriesResponse, CreateCategoryRequest, UpdateCategoryRequest, SetSessionCategoryResponse, Trigger, TriggerType, TriggerStatus, TriggersResponse, TriggerResponse, TriggerChannelsResponse, CreateTriggerRequest, UpdateTriggerRequest, Notification, NotificationsResponse, NotificationBadgeResponse, NotificationMarkAllReadResponse, NotificationDismissAllReadResponse, HealthReport, HealthActionResponse, CliStatusResponse, CliActionResponse, ForkSessionResponse, RefreshSessionResponse, RefreshAllSessionsResponse, BulkArchiveResponse, TranscriptResponse, TranscriptArchiveStatusResponse } from '../types.js';
6
+ import type { Session, Log, SubagentTranscript, SessionsResponse, SearchSessionsResponse, SessionActionResponse, LogsResponse, SubagentTranscriptsResponse, CreateSessionRequest, UpdateSessionRequest, CreateLogRequest, UpdateLogRequest, CreateSubagentTranscriptRequest, UpdateSubagentTranscriptRequest, SessionStatus, LogLevel, SubagentStatus, MCPServerInfo, AgentRootInfo, ConfigsResponse, SendPushNotificationResponse, EnqueuedMessage, EnqueuedMessagesResponse, EnqueuedMessageInterruptResponse, EnqueuedMessageStatus, Category, CategoriesResponse, CreateCategoryRequest, UpdateCategoryRequest, SetSessionCategoryResponse, Trigger, TriggerType, TriggerStatus, TriggersResponse, TriggerResponse, TriggerChannelsResponse, TriggerConditionAttributes, TriggerConditionType, CreateTriggerRequest, UpdateTriggerRequest, Notification, NotificationsResponse, NotificationBadgeResponse, NotificationMarkAllReadResponse, NotificationDismissAllReadResponse, HealthReport, HealthActionResponse, CliStatusResponse, CliActionResponse, ForkSessionResponse, RefreshSessionResponse, RefreshAllSessionsResponse, BulkArchiveResponse, TranscriptResponse, TranscriptArchiveStatusResponse, ElicitationActionType, ElicitationResponse } from '../types.js';
7
7
  /** Raw agent root shape as returned by the Rails API */
8
8
  export interface RawAgentRoot {
9
9
  name: string;
@@ -25,6 +25,14 @@ export interface RawAgentRoot {
25
25
  * as they are not needed by MCP clients.
26
26
  */
27
27
  export declare function mapAgentRoot(raw: RawAgentRoot): AgentRootInfo;
28
+ /**
29
+ * Folds the ergonomic top-level `trigger_type` + `configuration` shape into the
30
+ * REST API's nested `trigger_conditions_attributes`. Mirrors the
31
+ * `trigger_type` → `condition_type` mapping that listTriggers uses for filtering.
32
+ * Returns undefined when there is no condition to build (no trigger_type), so
33
+ * callers can leave the field off the request entirely.
34
+ */
35
+ export declare function conditionAttributesFromLegacy(trigger_type: TriggerConditionType | undefined, configuration: Record<string, unknown> | undefined): TriggerConditionAttributes[] | undefined;
28
36
  /**
29
37
  * Interface for the Agent Orchestrator API client.
30
38
  * Used for dependency injection and testing.
@@ -148,6 +156,7 @@ export interface IAgentOrchestratorClient {
148
156
  url: string;
149
157
  apiKey: string;
150
158
  };
159
+ respondToElicitation(requestId: string, actionType: ElicitationActionType, content?: Record<string, unknown>): Promise<ElicitationResponse>;
151
160
  }
152
161
  /**
153
162
  * Implementation of the Agent Orchestrator API client.
@@ -255,6 +264,13 @@ export declare class AgentOrchestratorClient implements IAgentOrchestratorClient
255
264
  getTrigger(id: number): Promise<TriggerResponse>;
256
265
  createTrigger(data: CreateTriggerRequest): Promise<Trigger>;
257
266
  updateTrigger(id: number, data: UpdateTriggerRequest): Promise<Trigger>;
267
+ /**
268
+ * Builds the trigger_conditions_attributes for an update expressed with the
269
+ * ergonomic top-level `trigger_type` + `configuration` shape. Fetches the
270
+ * trigger to reuse the matching existing condition's id (so the condition is
271
+ * updated in place rather than duplicated).
272
+ */
273
+ private resolveUpdatedTriggerConditions;
258
274
  deleteTrigger(id: number): Promise<void>;
259
275
  toggleTrigger(id: number): Promise<Trigger>;
260
276
  getTriggerChannels(): Promise<TriggerChannelsResponse>;
@@ -281,5 +297,6 @@ export declare class AgentOrchestratorClient implements IAgentOrchestratorClient
281
297
  url: string;
282
298
  apiKey: string;
283
299
  };
300
+ respondToElicitation(requestId: string, actionType: ElicitationActionType, content?: Record<string, unknown>): Promise<ElicitationResponse>;
284
301
  }
285
302
  //# sourceMappingURL=orchestrator-client.d.ts.map
@@ -5,11 +5,18 @@
5
5
  */
6
6
  import type { IAgentOrchestratorClient } from './orchestrator-client.js';
7
7
  import type { Session, Log, SubagentTranscript, Category } from '../types.js';
8
+ /** Minimal elicitation record used to simulate the respond endpoint. */
9
+ export interface MockElicitation {
10
+ request_id: string;
11
+ status: 'pending' | 'accept' | 'decline';
12
+ content?: Record<string, unknown> | null;
13
+ }
8
14
  interface MockData {
9
15
  sessions?: Session[];
10
16
  logs?: Log[];
11
17
  subagentTranscripts?: SubagentTranscript[];
12
18
  categories?: Category[];
19
+ elicitations?: MockElicitation[];
13
20
  }
14
21
  /**
15
22
  * Creates a mock Agent Orchestrator client for integration testing.
@@ -3,6 +3,25 @@
3
3
  *
4
4
  * Used in integration tests to simulate API responses without hitting real endpoints.
5
5
  */
6
+ import { conditionAttributesFromLegacy } from './orchestrator-client.js';
7
+ /**
8
+ * Builds mock TriggerCondition rows from a create/update request, mirroring the
9
+ * real client: fold the ergonomic top-level trigger_type + configuration shape
10
+ * into nested condition attributes when the caller didn't supply them directly.
11
+ */
12
+ function buildMockTriggerConditions(data) {
13
+ const conditionAttributes = data.trigger_conditions_attributes ??
14
+ conditionAttributesFromLegacy(data.trigger_type, data.configuration) ??
15
+ [];
16
+ return conditionAttributes.map((c, i) => ({
17
+ id: i + 1,
18
+ condition_type: c.condition_type,
19
+ configuration: c.configuration,
20
+ description: `${c.condition_type} condition`,
21
+ last_triggered_at: null,
22
+ last_polled_at: null,
23
+ }));
24
+ }
6
25
  /**
7
26
  * Creates a mock Agent Orchestrator client for integration testing.
8
27
  */
@@ -76,6 +95,7 @@ export function createIntegrationMockOrchestratorClient(initialMockData) {
76
95
  updated_at: '2025-01-15T14:00:00Z',
77
96
  },
78
97
  ],
98
+ elicitations: initialMockData?.elicitations || [],
79
99
  };
80
100
  let sessionIdCounter = mockData.sessions?.length || 1;
81
101
  let logIdCounter = mockData.logs?.length || 1;
@@ -750,14 +770,7 @@ export function createIntegrationMockOrchestratorClient(initialMockData) {
750
770
  return { trigger, recent_sessions: [] };
751
771
  },
752
772
  async createTrigger(data) {
753
- const conditions = (data.trigger_conditions_attributes ?? []).map((c, i) => ({
754
- id: i + 1,
755
- condition_type: c.condition_type,
756
- configuration: c.configuration,
757
- description: `${c.condition_type} condition`,
758
- last_triggered_at: null,
759
- last_polled_at: null,
760
- }));
773
+ const conditions = buildMockTriggerConditions(data);
761
774
  return {
762
775
  id: 1,
763
776
  name: data.name,
@@ -776,14 +789,7 @@ export function createIntegrationMockOrchestratorClient(initialMockData) {
776
789
  };
777
790
  },
778
791
  async updateTrigger(id, data) {
779
- const conditions = (data.trigger_conditions_attributes ?? []).map((c, i) => ({
780
- id: i + 1,
781
- condition_type: c.condition_type,
782
- configuration: c.configuration,
783
- description: `${c.condition_type} condition`,
784
- last_triggered_at: null,
785
- last_polled_at: null,
786
- }));
792
+ const conditions = buildMockTriggerConditions(data);
787
793
  return {
788
794
  id,
789
795
  name: data.name || 'Updated Trigger',
@@ -912,5 +918,22 @@ export function createIntegrationMockOrchestratorClient(initialMockData) {
912
918
  apiKey: 'test-api-key',
913
919
  };
914
920
  },
921
+ // Elicitations
922
+ async respondToElicitation(requestId, actionType, content) {
923
+ const elicitation = mockData.elicitations?.find((e) => e.request_id === requestId);
924
+ if (!elicitation) {
925
+ throw new Error(`API Error (404): Elicitation not found for request_id: ${requestId}`);
926
+ }
927
+ if (elicitation.status !== 'pending') {
928
+ throw new Error(`API Error (422): Elicitation ${requestId} is not pending (status: ${elicitation.status})`);
929
+ }
930
+ elicitation.status = actionType;
931
+ elicitation.content = actionType === 'accept' ? (content ?? null) : null;
932
+ return {
933
+ action: actionType,
934
+ content: elicitation.content,
935
+ _meta: { 'com.pulsemcp/request-id': requestId },
936
+ };
937
+ },
915
938
  };
916
939
  }
@@ -22,6 +22,19 @@ export function mapAgentRoot(raw) {
22
22
  default_model: raw.default_model,
23
23
  };
24
24
  }
25
+ /**
26
+ * Folds the ergonomic top-level `trigger_type` + `configuration` shape into the
27
+ * REST API's nested `trigger_conditions_attributes`. Mirrors the
28
+ * `trigger_type` → `condition_type` mapping that listTriggers uses for filtering.
29
+ * Returns undefined when there is no condition to build (no trigger_type), so
30
+ * callers can leave the field off the request entirely.
31
+ */
32
+ export function conditionAttributesFromLegacy(trigger_type, configuration) {
33
+ if (trigger_type === undefined) {
34
+ return undefined;
35
+ }
36
+ return [{ condition_type: trigger_type, configuration: configuration ?? {} }];
37
+ }
25
38
  /** Default timeout for API requests in milliseconds */
26
39
  const DEFAULT_TIMEOUT_MS = 30000;
27
40
  /**
@@ -387,13 +400,65 @@ export class AgentOrchestratorClient {
387
400
  return this.request('GET', `/triggers/${id}`);
388
401
  }
389
402
  async createTrigger(data) {
390
- const response = await this.request('POST', '/triggers', data);
403
+ const { trigger_type, configuration, trigger_conditions_attributes, ...rest } = data;
404
+ const body = { ...rest };
405
+ // The Rails v1 /triggers endpoint permits neither a top-level `trigger_type`
406
+ // nor a top-level `configuration`; the condition must be nested as
407
+ // `trigger_conditions_attributes: [{ condition_type:, configuration: }]`.
408
+ // An explicitly-supplied nested array (e.g. from wake_me_up_* tools) wins;
409
+ // otherwise fold the ergonomic top-level shape into a single condition.
410
+ const conditions = trigger_conditions_attributes ?? conditionAttributesFromLegacy(trigger_type, configuration);
411
+ if (conditions !== undefined) {
412
+ body.trigger_conditions_attributes = conditions;
413
+ }
414
+ const response = await this.request('POST', '/triggers', body);
391
415
  return response.trigger;
392
416
  }
393
417
  async updateTrigger(id, data) {
394
- const response = await this.request('PATCH', `/triggers/${id}`, data);
418
+ const { trigger_type, configuration, trigger_conditions_attributes, ...rest } = data;
419
+ const body = { ...rest };
420
+ if (trigger_conditions_attributes !== undefined) {
421
+ // Caller supplied the nested shape directly — forward verbatim.
422
+ body.trigger_conditions_attributes = trigger_conditions_attributes;
423
+ }
424
+ else if (trigger_type !== undefined || configuration !== undefined) {
425
+ // Ergonomic top-level shape. Resolve the existing condition's id so the
426
+ // condition is modified in place instead of a duplicate being appended
427
+ // (Rails uses accepts_nested_attributes_for). Metadata-only updates that
428
+ // omit both fields skip this entirely and never touch conditions.
429
+ body.trigger_conditions_attributes = await this.resolveUpdatedTriggerConditions(id, trigger_type, configuration);
430
+ }
431
+ const response = await this.request('PATCH', `/triggers/${id}`, body);
395
432
  return response.trigger;
396
433
  }
434
+ /**
435
+ * Builds the trigger_conditions_attributes for an update expressed with the
436
+ * ergonomic top-level `trigger_type` + `configuration` shape. Fetches the
437
+ * trigger to reuse the matching existing condition's id (so the condition is
438
+ * updated in place rather than duplicated).
439
+ */
440
+ async resolveUpdatedTriggerConditions(id, trigger_type, configuration) {
441
+ const { trigger } = await this.getTrigger(id);
442
+ const existing = trigger.conditions ?? [];
443
+ // Pick the condition to modify: the one matching the requested type, or the
444
+ // sole existing condition when no type was provided.
445
+ const target = trigger_type !== undefined
446
+ ? existing.find((c) => c.condition_type === trigger_type)
447
+ : existing.length === 1
448
+ ? existing[0]
449
+ : undefined;
450
+ const conditionType = trigger_type ?? target?.condition_type;
451
+ if (conditionType === undefined) {
452
+ throw new Error('Cannot update trigger configuration without a trigger_type when the trigger has zero or multiple conditions.');
453
+ }
454
+ return [
455
+ {
456
+ ...(target?.id !== undefined && { id: target.id }),
457
+ condition_type: conditionType,
458
+ configuration: configuration ?? target?.configuration ?? {},
459
+ },
460
+ ];
461
+ }
397
462
  async deleteTrigger(id) {
398
463
  await this.request('DELETE', `/triggers/${id}`);
399
464
  }
@@ -461,4 +526,14 @@ export class AgentOrchestratorClient {
461
526
  apiKey: this.apiKey,
462
527
  };
463
528
  }
529
+ // Elicitations
530
+ async respondToElicitation(requestId, actionType, content) {
531
+ const body = {
532
+ action_type: actionType,
533
+ };
534
+ if (content !== undefined) {
535
+ body.content = content;
536
+ }
537
+ return this.request('PATCH', `/elicitations/${encodeURIComponent(requestId)}/respond`, body);
538
+ }
464
539
  }
@@ -19,24 +19,24 @@ export declare const ActionTriggerSchema: z.ZodObject<{
19
19
  goal?: string | undefined;
20
20
  mcp_servers?: string[] | undefined;
21
21
  trigger_type?: "slack" | "schedule" | undefined;
22
+ configuration?: Record<string, unknown> | undefined;
22
23
  name?: string | undefined;
23
- id?: number | undefined;
24
24
  agent_root_name?: string | undefined;
25
25
  prompt_template?: string | undefined;
26
26
  reuse_session?: boolean | undefined;
27
- configuration?: Record<string, unknown> | undefined;
27
+ id?: number | undefined;
28
28
  }, {
29
29
  action: "create" | "update" | "delete" | "toggle";
30
30
  status?: "enabled" | "disabled" | undefined;
31
31
  goal?: string | undefined;
32
32
  mcp_servers?: string[] | undefined;
33
33
  trigger_type?: "slack" | "schedule" | undefined;
34
+ configuration?: Record<string, unknown> | undefined;
34
35
  name?: string | undefined;
35
- id?: number | undefined;
36
36
  agent_root_name?: string | undefined;
37
37
  prompt_template?: string | undefined;
38
38
  reuse_session?: boolean | undefined;
39
- configuration?: Record<string, unknown> | undefined;
39
+ id?: number | undefined;
40
40
  }>;
41
41
  export declare function actionTriggerTool(_server: Server, clientFactory: () => IAgentOrchestratorClient): {
42
42
  name: string;
@@ -11,16 +11,16 @@ export declare const ManageCategoriesSchema: z.ZodObject<{
11
11
  session_id: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNumber]>>;
12
12
  }, "strip", z.ZodTypeAny, {
13
13
  action: "list" | "create" | "update" | "delete" | "reorder" | "set_session_category";
14
- description?: string | undefined;
15
14
  name?: string | undefined;
15
+ description?: string | undefined;
16
16
  session_id?: string | number | undefined;
17
17
  category_id?: number | null | undefined;
18
18
  is_frozen?: boolean | undefined;
19
19
  ids?: (number | "uncategorized")[] | undefined;
20
20
  }, {
21
21
  action: "list" | "create" | "update" | "delete" | "reorder" | "set_session_category";
22
- description?: string | undefined;
23
22
  name?: string | undefined;
23
+ description?: string | undefined;
24
24
  session_id?: string | number | undefined;
25
25
  category_id?: number | null | undefined;
26
26
  is_frozen?: boolean | undefined;
@@ -0,0 +1,53 @@
1
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
2
+ import { z } from 'zod';
3
+ import type { IAgentOrchestratorClient } from '../orchestrator-client/orchestrator-client.js';
4
+ export declare const RespondToElicitationSchema: z.ZodObject<{
5
+ request_id: z.ZodString;
6
+ action_type: z.ZodEnum<["accept", "decline"]>;
7
+ content: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
8
+ }, "strip", z.ZodTypeAny, {
9
+ request_id: string;
10
+ action_type: "accept" | "decline";
11
+ content?: Record<string, unknown> | undefined;
12
+ }, {
13
+ request_id: string;
14
+ action_type: "accept" | "decline";
15
+ content?: Record<string, unknown> | undefined;
16
+ }>;
17
+ export declare function respondToElicitationTool(_server: Server, clientFactory: () => IAgentOrchestratorClient): {
18
+ name: string;
19
+ description: string;
20
+ inputSchema: {
21
+ type: "object";
22
+ properties: {
23
+ request_id: {
24
+ type: string;
25
+ description: "The elicitation `request_id` — the public identifier the MCP server assigned when it created the elicitation (the `com.pulsemcp/request-id` value, surfaced in the poll URL). This is NOT the database primary key.";
26
+ };
27
+ action_type: {
28
+ type: string;
29
+ enum: string[];
30
+ description: "How to resolve the elicitation. \"accept\" approves the request and lets the paused MCP flow continue (optionally with structured `content`); \"decline\" rejects it and unblocks the flow with a declined outcome.";
31
+ };
32
+ content: {
33
+ type: string;
34
+ description: "Optional structured JSON object supplied with an \"accept\" response (e.g. the form fields the elicitation requested). Ignored for \"decline\". Must be a JSON object, not a scalar or array.";
35
+ };
36
+ };
37
+ required: string[];
38
+ };
39
+ handler: (args: unknown) => Promise<{
40
+ content: {
41
+ type: string;
42
+ text: string;
43
+ }[];
44
+ isError?: undefined;
45
+ } | {
46
+ content: {
47
+ type: string;
48
+ text: string;
49
+ }[];
50
+ isError: boolean;
51
+ }>;
52
+ };
53
+ //# sourceMappingURL=respond-to-elicitation.d.ts.map
@@ -0,0 +1,95 @@
1
+ import { z } from 'zod';
2
+ const PARAM_DESCRIPTIONS = {
3
+ request_id: 'The elicitation `request_id` — the public identifier the MCP server assigned when it created the elicitation (the `com.pulsemcp/request-id` value, surfaced in the poll URL). This is NOT the database primary key.',
4
+ action_type: 'How to resolve the elicitation. "accept" approves the request and lets the paused MCP flow continue (optionally with structured `content`); "decline" rejects it and unblocks the flow with a declined outcome.',
5
+ content: 'Optional structured JSON object supplied with an "accept" response (e.g. the form fields the elicitation requested). Ignored for "decline". Must be a JSON object, not a scalar or array.',
6
+ };
7
+ export const RespondToElicitationSchema = z.object({
8
+ request_id: z.string().min(1).describe(PARAM_DESCRIPTIONS.request_id),
9
+ action_type: z.enum(['accept', 'decline']).describe(PARAM_DESCRIPTIONS.action_type),
10
+ content: z.record(z.unknown()).optional().describe(PARAM_DESCRIPTIONS.content),
11
+ });
12
+ const TOOL_DESCRIPTION = `Respond to a pending Agent Orchestrator elicitation request — programmatically accept or decline it so the paused MCP flow that is waiting on it can continue.
13
+
14
+ **Context:** When an MCP server needs human approval (e.g. a write-class action), it creates an *elicitation* and blocks, polling Agent Orchestrator until someone responds. Normally a human clicks "accept" / "decline" in the AO web UI. This tool exposes that same resolution over the API, so an agent or automated test can unblock the flow without a human in the loop.
15
+
16
+ **What it does:**
17
+ - Looks up the elicitation by its public \`request_id\` (the \`com.pulsemcp/request-id\` identifier, not the DB primary key).
18
+ - Records an \`accept\` (optionally with a structured \`content\` payload) or \`decline\`.
19
+ - Returns the elicitation's resulting poll-response so you can confirm the outcome.
20
+
21
+ **Example response:**
22
+ \`\`\`
23
+ ## Elicitation Accepted
24
+
25
+ - **Request ID:** req-abc123
26
+ - **Action:** accept
27
+ - **Content:** { "approved": true }
28
+ \`\`\`
29
+
30
+ **Enum — action_type:**
31
+ - **accept** — Approve the request; the waiting MCP flow proceeds. Pass optional \`content\` to supply the structured data the elicitation asked for.
32
+ - **decline** — Reject the request; the waiting MCP flow unblocks with a declined outcome. \`content\` is ignored.
33
+
34
+ **Use cases:**
35
+ - Closed-loop testing of an MCP server's elicitation-gated behavior without a human clicking in the UI.
36
+ - Automating approval of a known, expected elicitation as part of a larger orchestrated task.
37
+
38
+ **Errors:**
39
+ - Unknown \`request_id\` → 404 (elicitation not found).
40
+ - Elicitation already resolved / not pending, or an invalid \`action_type\` → 422.`;
41
+ export function respondToElicitationTool(_server, clientFactory) {
42
+ return {
43
+ name: 'respond_to_elicitation',
44
+ description: TOOL_DESCRIPTION,
45
+ inputSchema: {
46
+ type: 'object',
47
+ properties: {
48
+ request_id: {
49
+ type: 'string',
50
+ description: PARAM_DESCRIPTIONS.request_id,
51
+ },
52
+ action_type: {
53
+ type: 'string',
54
+ enum: ['accept', 'decline'],
55
+ description: PARAM_DESCRIPTIONS.action_type,
56
+ },
57
+ content: {
58
+ type: 'object',
59
+ description: PARAM_DESCRIPTIONS.content,
60
+ },
61
+ },
62
+ required: ['request_id', 'action_type'],
63
+ },
64
+ handler: async (args) => {
65
+ try {
66
+ const { request_id, action_type, content } = RespondToElicitationSchema.parse(args);
67
+ const client = clientFactory();
68
+ const response = await client.respondToElicitation(request_id, action_type, content);
69
+ const lines = [
70
+ `## Elicitation ${action_type === 'accept' ? 'Accepted' : 'Declined'}`,
71
+ '',
72
+ `- **Request ID:** ${request_id}`,
73
+ `- **Action:** ${response.action}`,
74
+ ];
75
+ if (response.content != null) {
76
+ lines.push(`- **Content:** ${JSON.stringify(response.content)}`);
77
+ }
78
+ return {
79
+ content: [{ type: 'text', text: lines.join('\n') }],
80
+ };
81
+ }
82
+ catch (error) {
83
+ return {
84
+ content: [
85
+ {
86
+ type: 'text',
87
+ text: `Error responding to elicitation: ${error instanceof Error ? error.message : 'Unknown error'}`,
88
+ },
89
+ ],
90
+ isError: true,
91
+ };
92
+ }
93
+ },
94
+ };
95
+ }
package/shared/tools.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { ListToolsRequestSchema, CallToolRequestSchema } from '@modelcontextprotocol/sdk/types.js';
2
- // 17 tools across 4 domains + 1 composite group
2
+ // 18 tools across 4 domains + 1 composite group
3
3
  import { quickSearchSessionsTool } from './tools/search-sessions.js';
4
4
  import { startSessionTool } from './tools/start-session.js';
5
5
  import { getSessionTool } from './tools/get-session.js';
@@ -17,6 +17,7 @@ import { wakeMeUpWhenSessionChangesStateTool } from './tools/wake-me-up-when-ses
17
17
  import { getSystemHealthTool } from './tools/get-system-health.js';
18
18
  import { actionHealthTool } from './tools/action-health.js';
19
19
  import { getTranscriptArchiveTool } from './tools/get-transcript-archive.js';
20
+ import { respondToElicitationTool } from './tools/respond-to-elicitation.js';
20
21
  /**
21
22
  * All valid tool groups (domain groups, their _readonly variants, and composite groups)
22
23
  */
@@ -62,7 +63,7 @@ export function parseEnabledToolGroups(enabledGroupsParam) {
62
63
  /**
63
64
  * All available tools with their group assignments.
64
65
  *
65
- * 17 tools across 4 domains + 1 composite group:
66
+ * 18 tools across 4 domains + 1 composite group:
66
67
  * - quick_search_sessions: Quick title-based search/list/get sessions by ID (sessions, read)
67
68
  * - get_session: Get detailed session info with optional logs/transcripts (sessions, read; self_session)
68
69
  * - get_configs: Fetch all static configuration (sessions, read; self_session)
@@ -71,6 +72,7 @@ export function parseEnabledToolGroups(enabledGroupsParam) {
71
72
  * - action_session: Perform session actions (sessions, write; self_session: filtered to update_notes, update_title, archive)
72
73
  * - manage_enqueued_messages: Manage session message queue (sessions, write)
73
74
  * - manage_categories: Manage dashboard categories and session assignment (sessions, write)
75
+ * - respond_to_elicitation: Accept or decline a pending elicitation by request_id (sessions, write)
74
76
  * - get_notifications: Get/list notifications and badge count (notifications, read)
75
77
  * - send_push_notification: Send a push notification (notifications, write; self_session)
76
78
  * - action_notification: Mark read, dismiss notifications (notifications, write)
@@ -126,6 +128,11 @@ const ALL_TOOLS = [
126
128
  group: 'sessions',
127
129
  isWriteOperation: true,
128
130
  },
131
+ {
132
+ factory: respondToElicitationTool,
133
+ group: 'sessions',
134
+ isWriteOperation: true,
135
+ },
129
136
  // Notification tools - read operations
130
137
  {
131
138
  factory: getNotificationsTool,
package/shared/types.d.ts CHANGED
@@ -298,6 +298,7 @@ export interface TriggerChannelsResponse {
298
298
  }>;
299
299
  }
300
300
  export interface TriggerConditionAttributes {
301
+ id?: number;
301
302
  condition_type: TriggerConditionType;
302
303
  configuration: Record<string, unknown>;
303
304
  }
@@ -402,6 +403,21 @@ export interface BulkArchiveResponse {
402
403
  export interface TranscriptResponse {
403
404
  transcript_text: string;
404
405
  }
406
+ /** Action taken when responding to a pending elicitation. */
407
+ export type ElicitationActionType = 'accept' | 'decline';
408
+ /**
409
+ * Poll-response shape returned by the elicitation endpoints.
410
+ *
411
+ * `action` is `"pending"` while the elicitation is still open, or the resolved
412
+ * status (e.g. `"accept"` / `"decline"`) once a response has been recorded.
413
+ * `content` carries the structured payload supplied with an accepted response
414
+ * (null when pending or when no content was provided).
415
+ */
416
+ export interface ElicitationResponse {
417
+ action: string;
418
+ content: Record<string, unknown> | null;
419
+ _meta?: Record<string, unknown>;
420
+ }
405
421
  export interface TranscriptArchiveStatusResponse {
406
422
  generated_at: string;
407
423
  session_count: number;