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 +11 -10
- package/package.json +1 -1
- package/shared/orchestrator-client/orchestrator-client.d.ts +18 -1
- package/shared/orchestrator-client/orchestrator-client.integration-mock.d.ts +7 -0
- package/shared/orchestrator-client/orchestrator-client.integration-mock.js +39 -16
- package/shared/orchestrator-client/orchestrator-client.js +77 -2
- package/shared/tools/action-trigger.d.ts +4 -4
- package/shared/tools/manage-categories.d.ts +2 -2
- package/shared/tools/respond-to-elicitation.d.ts +53 -0
- package/shared/tools/respond-to-elicitation.js +95 -0
- package/shared/tools.js +9 -2
- package/shared/types.d.ts +16 -0
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
|
@@ -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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
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
|
-
*
|
|
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;
|