agent-orchestrator-mcp-server 0.6.7 → 0.7.3
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/build/index.js +1 -1
- package/package.json +2 -2
- package/shared/orchestrator-client/orchestrator-client.d.ts +10 -2
- package/shared/orchestrator-client/orchestrator-client.integration-mock.js +23 -3
- package/shared/orchestrator-client/orchestrator-client.js +10 -2
- package/shared/resources.js +1 -0
- package/shared/server.d.ts +11 -0
- package/shared/tools/action-session.d.ts +65 -0
- package/shared/tools/action-session.js +169 -5
- package/shared/tools/action-trigger.d.ts +2 -2
- package/shared/tools/action-trigger.js +5 -1
- package/shared/tools/manage-enqueued-messages.d.ts +4 -4
- package/shared/tools/manage-enqueued-messages.js +57 -14
- package/shared/tools/wake-me-up-later.d.ts +61 -0
- package/shared/tools/wake-me-up-later.js +148 -0
- package/shared/tools.d.ts +3 -2
- package/shared/tools.js +114 -19
package/build/index.js
CHANGED
|
@@ -31,7 +31,7 @@ function validateEnvironment() {
|
|
|
31
31
|
const optional = [
|
|
32
32
|
{
|
|
33
33
|
name: 'TOOL_GROUPS',
|
|
34
|
-
description: 'Comma-separated list of tool groups to enable (sessions,sessions_readonly,notifications,notifications_readonly,triggers,triggers_readonly,health,health_readonly)',
|
|
34
|
+
description: 'Comma-separated list of tool groups to enable (sessions,sessions_readonly,notifications,notifications_readonly,triggers,triggers_readonly,health,health_readonly,self_session)',
|
|
35
35
|
defaultValue: 'all groups enabled',
|
|
36
36
|
},
|
|
37
37
|
{
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-orchestrator-mcp-server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.3",
|
|
4
4
|
"description": "Local implementation of agent-orchestrator MCP server",
|
|
5
5
|
"main": "build/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"stage-publish": "npm version"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
32
|
+
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
33
33
|
"zod": "^3.24.1"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
@@ -50,8 +50,12 @@ export interface IAgentOrchestratorClient {
|
|
|
50
50
|
deleteSession(id: string | number): Promise<void>;
|
|
51
51
|
archiveSession(id: string | number): Promise<Session>;
|
|
52
52
|
unarchiveSession(id: string | number): Promise<Session>;
|
|
53
|
-
followUp(id: string | number, prompt: string
|
|
53
|
+
followUp(id: string | number, prompt: string, options?: {
|
|
54
|
+
force_immediate?: boolean;
|
|
55
|
+
stop_condition?: string;
|
|
56
|
+
}): Promise<SessionActionResponse>;
|
|
54
57
|
pauseSession(id: string | number): Promise<SessionActionResponse>;
|
|
58
|
+
sleepSession(id: string | number): Promise<Session>;
|
|
55
59
|
restartSession(id: string | number): Promise<SessionActionResponse>;
|
|
56
60
|
changeMcpServers(id: string | number, mcp_servers: string[]): Promise<Session>;
|
|
57
61
|
changeModel(id: string | number, model: string): Promise<Session>;
|
|
@@ -173,8 +177,12 @@ export declare class AgentOrchestratorClient implements IAgentOrchestratorClient
|
|
|
173
177
|
deleteSession(id: string | number): Promise<void>;
|
|
174
178
|
archiveSession(id: string | number): Promise<Session>;
|
|
175
179
|
unarchiveSession(id: string | number): Promise<Session>;
|
|
176
|
-
followUp(id: string | number, prompt: string
|
|
180
|
+
followUp(id: string | number, prompt: string, options?: {
|
|
181
|
+
force_immediate?: boolean;
|
|
182
|
+
stop_condition?: string;
|
|
183
|
+
}): Promise<SessionActionResponse>;
|
|
177
184
|
pauseSession(id: string | number): Promise<SessionActionResponse>;
|
|
185
|
+
sleepSession(id: string | number): Promise<Session>;
|
|
178
186
|
restartSession(id: string | number): Promise<SessionActionResponse>;
|
|
179
187
|
changeMcpServers(id: string | number, mcp_servers: string[]): Promise<Session>;
|
|
180
188
|
changeModel(id: string | number, model: string): Promise<Session>;
|
|
@@ -204,19 +204,27 @@ export function createIntegrationMockOrchestratorClient(initialMockData) {
|
|
|
204
204
|
session.updated_at = new Date().toISOString();
|
|
205
205
|
return session;
|
|
206
206
|
},
|
|
207
|
-
async followUp(id, prompt) {
|
|
207
|
+
async followUp(id, prompt, options) {
|
|
208
208
|
const session = mockData.sessions?.find((s) => s.id === Number(id) || s.slug === String(id));
|
|
209
209
|
if (!session) {
|
|
210
210
|
throw new Error(`API Error (404): Session not found`);
|
|
211
211
|
}
|
|
212
|
-
if (
|
|
212
|
+
if (options?.force_immediate) {
|
|
213
|
+
if (session.status === 'failed' || session.status === 'archived') {
|
|
214
|
+
throw new Error(`API Error (422): Session cannot receive follow-up in ${session.status} status`);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
else if (session.status !== 'needs_input') {
|
|
213
218
|
throw new Error(`API Error (422): Session is not in needs_input status`);
|
|
214
219
|
}
|
|
215
220
|
session.prompt = prompt;
|
|
216
221
|
session.status = 'running';
|
|
217
222
|
session.running_job_id = `job_${Date.now()}`;
|
|
218
223
|
session.updated_at = new Date().toISOString();
|
|
219
|
-
|
|
224
|
+
const message = options?.force_immediate
|
|
225
|
+
? 'Follow-up prompt sent immediately'
|
|
226
|
+
: 'Follow-up prompt sent';
|
|
227
|
+
return { session, message };
|
|
220
228
|
},
|
|
221
229
|
async pauseSession(id) {
|
|
222
230
|
const session = mockData.sessions?.find((s) => s.id === Number(id) || s.slug === String(id));
|
|
@@ -228,6 +236,18 @@ export function createIntegrationMockOrchestratorClient(initialMockData) {
|
|
|
228
236
|
session.updated_at = new Date().toISOString();
|
|
229
237
|
return { session, message: 'Session paused' };
|
|
230
238
|
},
|
|
239
|
+
async sleepSession(id) {
|
|
240
|
+
const session = mockData.sessions?.find((s) => s.id === Number(id) || s.slug === String(id));
|
|
241
|
+
if (!session) {
|
|
242
|
+
throw new Error(`API Error (404): Session not found`);
|
|
243
|
+
}
|
|
244
|
+
if (session.status !== 'needs_input') {
|
|
245
|
+
throw new Error(`API Error (422): Session must be in needs_input state to sleep`);
|
|
246
|
+
}
|
|
247
|
+
session.status = 'waiting';
|
|
248
|
+
session.updated_at = new Date().toISOString();
|
|
249
|
+
return session;
|
|
250
|
+
},
|
|
231
251
|
async restartSession(id) {
|
|
232
252
|
const session = mockData.sessions?.find((s) => s.id === Number(id) || s.slug === String(id));
|
|
233
253
|
if (!session) {
|
|
@@ -204,12 +204,20 @@ export class AgentOrchestratorClient {
|
|
|
204
204
|
const response = await this.request('POST', `/sessions/${id}/unarchive`);
|
|
205
205
|
return response.session;
|
|
206
206
|
}
|
|
207
|
-
async followUp(id, prompt) {
|
|
208
|
-
return this.request('POST', `/sessions/${id}/follow_up`, {
|
|
207
|
+
async followUp(id, prompt, options) {
|
|
208
|
+
return this.request('POST', `/sessions/${id}/follow_up`, {
|
|
209
|
+
prompt,
|
|
210
|
+
...(options?.force_immediate && { force_immediate: true }),
|
|
211
|
+
...(options?.stop_condition && { stop_condition: options.stop_condition }),
|
|
212
|
+
});
|
|
209
213
|
}
|
|
210
214
|
async pauseSession(id) {
|
|
211
215
|
return this.request('POST', `/sessions/${id}/pause`);
|
|
212
216
|
}
|
|
217
|
+
async sleepSession(id) {
|
|
218
|
+
const response = await this.request('POST', `/sessions/${id}/sleep`);
|
|
219
|
+
return response.session;
|
|
220
|
+
}
|
|
213
221
|
async restartSession(id) {
|
|
214
222
|
return this.request('POST', `/sessions/${id}/restart`);
|
|
215
223
|
}
|
package/shared/resources.js
CHANGED
|
@@ -74,6 +74,7 @@ export function createRegisterResources(clientFactory) {
|
|
|
74
74
|
triggers_readonly: 'Trigger tools (read only): search_triggers',
|
|
75
75
|
health: 'All health tools (read + write): health report, CLI status, maintenance actions',
|
|
76
76
|
health_readonly: 'Health tools (read only): get_system_health',
|
|
77
|
+
self_session: 'Self-management tools for auto-injected servers: get_session, get_configs (read), action_session (filtered: update_notes, update_title, archive), send_push_notification',
|
|
77
78
|
},
|
|
78
79
|
};
|
|
79
80
|
return {
|
package/shared/server.d.ts
CHANGED
|
@@ -13,6 +13,9 @@ export declare function createMCPServer(options: CreateMCPServerOptions): {
|
|
|
13
13
|
_meta?: {
|
|
14
14
|
[x: string]: unknown;
|
|
15
15
|
progressToken?: string | number | undefined;
|
|
16
|
+
"io.modelcontextprotocol/related-task"?: {
|
|
17
|
+
taskId: string;
|
|
18
|
+
} | undefined;
|
|
16
19
|
} | undefined;
|
|
17
20
|
} | undefined;
|
|
18
21
|
}, {
|
|
@@ -21,12 +24,20 @@ export declare function createMCPServer(options: CreateMCPServerOptions): {
|
|
|
21
24
|
[x: string]: unknown;
|
|
22
25
|
_meta?: {
|
|
23
26
|
[x: string]: unknown;
|
|
27
|
+
progressToken?: string | number | undefined;
|
|
28
|
+
"io.modelcontextprotocol/related-task"?: {
|
|
29
|
+
taskId: string;
|
|
30
|
+
} | undefined;
|
|
24
31
|
} | undefined;
|
|
25
32
|
} | undefined;
|
|
26
33
|
}, {
|
|
27
34
|
[x: string]: unknown;
|
|
28
35
|
_meta?: {
|
|
29
36
|
[x: string]: unknown;
|
|
37
|
+
progressToken?: string | number | undefined;
|
|
38
|
+
"io.modelcontextprotocol/related-task"?: {
|
|
39
|
+
taskId: string;
|
|
40
|
+
} | undefined;
|
|
30
41
|
} | undefined;
|
|
31
42
|
}>;
|
|
32
43
|
registerHandlers: (server: Server, clientFactory?: ClientFactory) => Promise<void>;
|
|
@@ -5,6 +5,7 @@ export declare const ActionSessionSchema: z.ZodObject<{
|
|
|
5
5
|
session_id: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNumber]>>;
|
|
6
6
|
action: z.ZodEnum<["follow_up", "pause", "restart", "archive", "unarchive", "change_mcp_servers", "change_model", "fork", "refresh", "refresh_all", "update_notes", "update_title", "toggle_favorite", "bulk_archive"]>;
|
|
7
7
|
prompt: z.ZodOptional<z.ZodString>;
|
|
8
|
+
force_immediate: z.ZodOptional<z.ZodBoolean>;
|
|
8
9
|
mcp_servers: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
9
10
|
model: z.ZodOptional<z.ZodString>;
|
|
10
11
|
message_index: z.ZodOptional<z.ZodNumber>;
|
|
@@ -17,6 +18,7 @@ export declare const ActionSessionSchema: z.ZodObject<{
|
|
|
17
18
|
title?: string | undefined;
|
|
18
19
|
mcp_servers?: string[] | undefined;
|
|
19
20
|
session_id?: string | number | undefined;
|
|
21
|
+
force_immediate?: boolean | undefined;
|
|
20
22
|
model?: string | undefined;
|
|
21
23
|
message_index?: number | undefined;
|
|
22
24
|
session_notes?: string | undefined;
|
|
@@ -27,6 +29,7 @@ export declare const ActionSessionSchema: z.ZodObject<{
|
|
|
27
29
|
title?: string | undefined;
|
|
28
30
|
mcp_servers?: string[] | undefined;
|
|
29
31
|
session_id?: string | number | undefined;
|
|
32
|
+
force_immediate?: boolean | undefined;
|
|
30
33
|
model?: string | undefined;
|
|
31
34
|
message_index?: number | undefined;
|
|
32
35
|
session_notes?: string | undefined;
|
|
@@ -53,6 +56,10 @@ export declare function actionSessionTool(_server: Server, clientFactory: () =>
|
|
|
53
56
|
type: string;
|
|
54
57
|
description: "Required for \"follow_up\" action. The prompt to send to the agent. Not used for other actions.";
|
|
55
58
|
};
|
|
59
|
+
force_immediate: {
|
|
60
|
+
type: string;
|
|
61
|
+
description: "Optional for \"follow_up\" action. When true, interrupts a running session to deliver the prompt immediately instead of queuing it. Not used for other actions.";
|
|
62
|
+
};
|
|
56
63
|
mcp_servers: {
|
|
57
64
|
type: string;
|
|
58
65
|
items: {
|
|
@@ -100,4 +107,62 @@ export declare function actionSessionTool(_server: Server, clientFactory: () =>
|
|
|
100
107
|
isError?: undefined;
|
|
101
108
|
}>;
|
|
102
109
|
};
|
|
110
|
+
export declare const SelfSessionActionSessionSchema: z.ZodObject<{
|
|
111
|
+
session_id: z.ZodUnion<[z.ZodString, z.ZodNumber]>;
|
|
112
|
+
action: z.ZodEnum<["update_notes", "update_title", "archive"]>;
|
|
113
|
+
session_notes: z.ZodOptional<z.ZodString>;
|
|
114
|
+
title: z.ZodOptional<z.ZodString>;
|
|
115
|
+
}, "strip", z.ZodTypeAny, {
|
|
116
|
+
session_id: string | number;
|
|
117
|
+
action: "archive" | "update_notes" | "update_title";
|
|
118
|
+
title?: string | undefined;
|
|
119
|
+
session_notes?: string | undefined;
|
|
120
|
+
}, {
|
|
121
|
+
session_id: string | number;
|
|
122
|
+
action: "archive" | "update_notes" | "update_title";
|
|
123
|
+
title?: string | undefined;
|
|
124
|
+
session_notes?: string | undefined;
|
|
125
|
+
}>;
|
|
126
|
+
export declare function selfSessionActionSessionTool(_server: Server, clientFactory: () => IAgentOrchestratorClient): {
|
|
127
|
+
name: string;
|
|
128
|
+
description: string;
|
|
129
|
+
inputSchema: {
|
|
130
|
+
type: "object";
|
|
131
|
+
properties: {
|
|
132
|
+
session_id: {
|
|
133
|
+
oneOf: {
|
|
134
|
+
type: string;
|
|
135
|
+
}[];
|
|
136
|
+
description: "Session ID (numeric) or slug (string). Required for most actions. Not required for \"refresh_all\" and \"bulk_archive\".";
|
|
137
|
+
};
|
|
138
|
+
action: {
|
|
139
|
+
type: string;
|
|
140
|
+
enum: readonly ["update_notes", "update_title", "archive"];
|
|
141
|
+
description: string;
|
|
142
|
+
};
|
|
143
|
+
session_notes: {
|
|
144
|
+
type: string;
|
|
145
|
+
description: "Required for \"update_notes\" action. The notes text to set on the session.";
|
|
146
|
+
};
|
|
147
|
+
title: {
|
|
148
|
+
type: string;
|
|
149
|
+
description: "Required for \"update_title\" action. The new title for the session.";
|
|
150
|
+
};
|
|
151
|
+
};
|
|
152
|
+
required: string[];
|
|
153
|
+
};
|
|
154
|
+
handler: (args: unknown) => Promise<{
|
|
155
|
+
content: {
|
|
156
|
+
type: string;
|
|
157
|
+
text: string;
|
|
158
|
+
}[];
|
|
159
|
+
isError: boolean;
|
|
160
|
+
} | {
|
|
161
|
+
content: {
|
|
162
|
+
type: string;
|
|
163
|
+
text: string;
|
|
164
|
+
}[];
|
|
165
|
+
isError?: undefined;
|
|
166
|
+
}>;
|
|
167
|
+
};
|
|
103
168
|
//# sourceMappingURL=action-session.d.ts.map
|
|
@@ -4,6 +4,7 @@ const PARAM_DESCRIPTIONS = {
|
|
|
4
4
|
session_id: 'Session ID (numeric) or slug (string). Required for most actions. Not required for "refresh_all" and "bulk_archive".',
|
|
5
5
|
action: 'Action to perform: "follow_up", "pause", "restart", "archive", "unarchive", "change_mcp_servers", "change_model", "fork", "refresh", "refresh_all", "update_notes", "update_title", "toggle_favorite", "bulk_archive"',
|
|
6
6
|
prompt: 'Required for "follow_up" action. The prompt to send to the agent. Not used for other actions.',
|
|
7
|
+
force_immediate: 'Optional for "follow_up" action. When true, interrupts a running session to deliver the prompt immediately instead of queuing it. Not used for other actions.',
|
|
7
8
|
mcp_servers: 'Required for "change_mcp_servers" action. Array of MCP server names to set for the session.',
|
|
8
9
|
model: 'Required for "change_model" action. The model identifier to use (e.g., "opus", "sonnet").',
|
|
9
10
|
message_index: 'Required for "fork" action. The transcript message index to fork from.',
|
|
@@ -31,6 +32,7 @@ export const ActionSessionSchema = z.object({
|
|
|
31
32
|
session_id: z.union([z.string(), z.number()]).optional().describe(PARAM_DESCRIPTIONS.session_id),
|
|
32
33
|
action: z.enum(ACTION_ENUM).describe(PARAM_DESCRIPTIONS.action),
|
|
33
34
|
prompt: z.string().optional().describe(PARAM_DESCRIPTIONS.prompt),
|
|
35
|
+
force_immediate: z.boolean().optional().describe(PARAM_DESCRIPTIONS.force_immediate),
|
|
34
36
|
mcp_servers: z.array(z.string()).optional().describe(PARAM_DESCRIPTIONS.mcp_servers),
|
|
35
37
|
model: z.string().optional().describe(PARAM_DESCRIPTIONS.model),
|
|
36
38
|
message_index: z.number().optional().describe(PARAM_DESCRIPTIONS.message_index),
|
|
@@ -41,7 +43,7 @@ export const ActionSessionSchema = z.object({
|
|
|
41
43
|
const TOOL_DESCRIPTION = `Perform an action on an agent session.
|
|
42
44
|
|
|
43
45
|
**Actions:**
|
|
44
|
-
- **follow_up**: Send a follow-up prompt to
|
|
46
|
+
- **follow_up**: Send a follow-up prompt to a session (requires "prompt"; optional "force_immediate" to interrupt a running session). Without "force_immediate", uses smart routing: sends immediately if idle, auto-queues if running. Alternative: use manage_enqueued_messages "send_now" for one-step immediate delivery with stop_condition support.
|
|
45
47
|
- **pause**: Pause a running session, transitioning it to idle "needs_input" status
|
|
46
48
|
- **restart**: Restart an idle or failed session without providing new input
|
|
47
49
|
- **archive**: Archive a session (marks as completed)
|
|
@@ -82,6 +84,10 @@ export function actionSessionTool(_server, clientFactory) {
|
|
|
82
84
|
type: 'string',
|
|
83
85
|
description: PARAM_DESCRIPTIONS.prompt,
|
|
84
86
|
},
|
|
87
|
+
force_immediate: {
|
|
88
|
+
type: 'boolean',
|
|
89
|
+
description: PARAM_DESCRIPTIONS.force_immediate,
|
|
90
|
+
},
|
|
85
91
|
mcp_servers: {
|
|
86
92
|
type: 'array',
|
|
87
93
|
items: { type: 'string' },
|
|
@@ -115,7 +121,7 @@ export function actionSessionTool(_server, clientFactory) {
|
|
|
115
121
|
try {
|
|
116
122
|
const validatedArgs = ActionSessionSchema.parse(args);
|
|
117
123
|
const client = clientFactory();
|
|
118
|
-
const { session_id, action, prompt, mcp_servers, model, message_index, session_notes, session_ids, title, } = validatedArgs;
|
|
124
|
+
const { session_id, action, prompt, force_immediate, mcp_servers, model, message_index, session_notes, session_ids, title, } = validatedArgs;
|
|
119
125
|
// Actions that require session_id
|
|
120
126
|
const requiresSessionId = [
|
|
121
127
|
'follow_up',
|
|
@@ -241,9 +247,13 @@ export function actionSessionTool(_server, clientFactory) {
|
|
|
241
247
|
let result;
|
|
242
248
|
switch (action) {
|
|
243
249
|
case 'follow_up': {
|
|
244
|
-
const response = await client.followUp(session_id, prompt
|
|
250
|
+
const response = await client.followUp(session_id, prompt, {
|
|
251
|
+
force_immediate,
|
|
252
|
+
});
|
|
253
|
+
const immediate = response.message?.toLowerCase().includes('immediately');
|
|
254
|
+
const heading = immediate ? 'Follow-up Sent Immediately' : 'Follow-up Sent';
|
|
245
255
|
const lines = [
|
|
246
|
-
`##
|
|
256
|
+
`## ${heading}`,
|
|
247
257
|
'',
|
|
248
258
|
`- **Session ID:** ${response.session.id}`,
|
|
249
259
|
`- **Title:** ${response.session.title}`,
|
|
@@ -393,7 +403,9 @@ export function actionSessionTool(_server, clientFactory) {
|
|
|
393
403
|
break;
|
|
394
404
|
}
|
|
395
405
|
case 'update_title': {
|
|
396
|
-
const session = await client.updateSession(session_id, {
|
|
406
|
+
const session = await client.updateSession(session_id, {
|
|
407
|
+
title: title,
|
|
408
|
+
});
|
|
397
409
|
const lines = [
|
|
398
410
|
`## Session Title Updated`,
|
|
399
411
|
'',
|
|
@@ -463,3 +475,155 @@ export function actionSessionTool(_server, clientFactory) {
|
|
|
463
475
|
},
|
|
464
476
|
};
|
|
465
477
|
}
|
|
478
|
+
// =============================================================================
|
|
479
|
+
// SELF-SESSION VARIANT
|
|
480
|
+
// =============================================================================
|
|
481
|
+
// Restricted version of action_session for the self_session composite group.
|
|
482
|
+
// Only allows self-management actions: update_notes, update_title, archive.
|
|
483
|
+
// =============================================================================
|
|
484
|
+
const SELF_SESSION_ACTION_ENUM = ['update_notes', 'update_title', 'archive'];
|
|
485
|
+
export const SelfSessionActionSessionSchema = z.object({
|
|
486
|
+
session_id: z.union([z.string(), z.number()]).describe(PARAM_DESCRIPTIONS.session_id),
|
|
487
|
+
action: z
|
|
488
|
+
.enum(SELF_SESSION_ACTION_ENUM)
|
|
489
|
+
.describe('Action to perform: "update_notes", "update_title", "archive"'),
|
|
490
|
+
session_notes: z.string().optional().describe(PARAM_DESCRIPTIONS.session_notes),
|
|
491
|
+
title: z.string().optional().describe(PARAM_DESCRIPTIONS.title),
|
|
492
|
+
});
|
|
493
|
+
const SELF_SESSION_TOOL_DESCRIPTION = `Perform a self-management action on a session.
|
|
494
|
+
|
|
495
|
+
**Actions (limited to self-management):**
|
|
496
|
+
- **update_notes**: Update the notes on a session (requires "session_notes")
|
|
497
|
+
- **update_title**: Update the title of a session (requires "title")
|
|
498
|
+
- **archive**: Archive a session (marks as completed)
|
|
499
|
+
|
|
500
|
+
**Use cases:**
|
|
501
|
+
- Update session notes to record progress or context
|
|
502
|
+
- Set a meaningful session title
|
|
503
|
+
- Archive the session when work is complete`;
|
|
504
|
+
export function selfSessionActionSessionTool(_server, clientFactory) {
|
|
505
|
+
return {
|
|
506
|
+
name: 'action_session',
|
|
507
|
+
description: SELF_SESSION_TOOL_DESCRIPTION,
|
|
508
|
+
inputSchema: {
|
|
509
|
+
type: 'object',
|
|
510
|
+
properties: {
|
|
511
|
+
session_id: {
|
|
512
|
+
oneOf: [{ type: 'string' }, { type: 'number' }],
|
|
513
|
+
description: PARAM_DESCRIPTIONS.session_id,
|
|
514
|
+
},
|
|
515
|
+
action: {
|
|
516
|
+
type: 'string',
|
|
517
|
+
enum: SELF_SESSION_ACTION_ENUM,
|
|
518
|
+
description: 'Action to perform: "update_notes", "update_title", "archive"',
|
|
519
|
+
},
|
|
520
|
+
session_notes: {
|
|
521
|
+
type: 'string',
|
|
522
|
+
description: PARAM_DESCRIPTIONS.session_notes,
|
|
523
|
+
},
|
|
524
|
+
title: {
|
|
525
|
+
type: 'string',
|
|
526
|
+
description: PARAM_DESCRIPTIONS.title,
|
|
527
|
+
},
|
|
528
|
+
},
|
|
529
|
+
required: ['session_id', 'action'],
|
|
530
|
+
},
|
|
531
|
+
handler: async (args) => {
|
|
532
|
+
try {
|
|
533
|
+
const validatedArgs = SelfSessionActionSessionSchema.parse(args);
|
|
534
|
+
const client = clientFactory();
|
|
535
|
+
const { session_id, action, session_notes, title } = validatedArgs;
|
|
536
|
+
// Validate update_notes requires session_notes
|
|
537
|
+
if (action === 'update_notes' && session_notes === undefined) {
|
|
538
|
+
return {
|
|
539
|
+
content: [
|
|
540
|
+
{
|
|
541
|
+
type: 'text',
|
|
542
|
+
text: 'Error: The "session_notes" parameter is required for the "update_notes" action.',
|
|
543
|
+
},
|
|
544
|
+
],
|
|
545
|
+
isError: true,
|
|
546
|
+
};
|
|
547
|
+
}
|
|
548
|
+
// Validate update_title requires title
|
|
549
|
+
if (action === 'update_title' && !title) {
|
|
550
|
+
return {
|
|
551
|
+
content: [
|
|
552
|
+
{
|
|
553
|
+
type: 'text',
|
|
554
|
+
text: 'Error: The "title" parameter is required for the "update_title" action.',
|
|
555
|
+
},
|
|
556
|
+
],
|
|
557
|
+
isError: true,
|
|
558
|
+
};
|
|
559
|
+
}
|
|
560
|
+
let result;
|
|
561
|
+
switch (action) {
|
|
562
|
+
case 'archive': {
|
|
563
|
+
const session = await client.archiveSession(session_id);
|
|
564
|
+
const lines = [
|
|
565
|
+
`## Session Archived`,
|
|
566
|
+
'',
|
|
567
|
+
`- **Session ID:** ${session.id}`,
|
|
568
|
+
`- **Title:** ${session.title}`,
|
|
569
|
+
`- **New Status:** ${session.status}`,
|
|
570
|
+
`- **Archived At:** ${session.archived_at}`,
|
|
571
|
+
];
|
|
572
|
+
result = lines.join('\n');
|
|
573
|
+
break;
|
|
574
|
+
}
|
|
575
|
+
case 'update_notes': {
|
|
576
|
+
const session = await client.updateSessionNotes(session_id, session_notes);
|
|
577
|
+
const lines = [
|
|
578
|
+
`## Session Notes Updated`,
|
|
579
|
+
'',
|
|
580
|
+
`- **Session ID:** ${session.id}`,
|
|
581
|
+
`- **Title:** ${session.title}`,
|
|
582
|
+
];
|
|
583
|
+
result = lines.join('\n');
|
|
584
|
+
break;
|
|
585
|
+
}
|
|
586
|
+
case 'update_title': {
|
|
587
|
+
const session = await client.updateSession(session_id, {
|
|
588
|
+
title: title,
|
|
589
|
+
});
|
|
590
|
+
const lines = [
|
|
591
|
+
`## Session Title Updated`,
|
|
592
|
+
'',
|
|
593
|
+
`- **Session ID:** ${session.id}`,
|
|
594
|
+
`- **Title:** ${session.title}`,
|
|
595
|
+
];
|
|
596
|
+
result = lines.join('\n');
|
|
597
|
+
break;
|
|
598
|
+
}
|
|
599
|
+
default: {
|
|
600
|
+
const _exhaustiveCheck = action;
|
|
601
|
+
return {
|
|
602
|
+
content: [
|
|
603
|
+
{
|
|
604
|
+
type: 'text',
|
|
605
|
+
text: `Error: Unknown action "${_exhaustiveCheck}"`,
|
|
606
|
+
},
|
|
607
|
+
],
|
|
608
|
+
isError: true,
|
|
609
|
+
};
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
return {
|
|
613
|
+
content: [{ type: 'text', text: result }],
|
|
614
|
+
};
|
|
615
|
+
}
|
|
616
|
+
catch (error) {
|
|
617
|
+
return {
|
|
618
|
+
content: [
|
|
619
|
+
{
|
|
620
|
+
type: 'text',
|
|
621
|
+
text: `Error performing action: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
622
|
+
},
|
|
623
|
+
],
|
|
624
|
+
isError: true,
|
|
625
|
+
};
|
|
626
|
+
}
|
|
627
|
+
},
|
|
628
|
+
};
|
|
629
|
+
}
|
|
@@ -19,8 +19,8 @@ export declare const ActionTriggerSchema: z.ZodObject<{
|
|
|
19
19
|
stop_condition?: string | undefined;
|
|
20
20
|
mcp_servers?: string[] | undefined;
|
|
21
21
|
trigger_type?: "slack" | "schedule" | undefined;
|
|
22
|
-
name?: string | undefined;
|
|
23
22
|
id?: number | undefined;
|
|
23
|
+
name?: string | undefined;
|
|
24
24
|
agent_root_name?: string | undefined;
|
|
25
25
|
prompt_template?: string | undefined;
|
|
26
26
|
reuse_session?: boolean | undefined;
|
|
@@ -31,8 +31,8 @@ export declare const ActionTriggerSchema: z.ZodObject<{
|
|
|
31
31
|
stop_condition?: string | undefined;
|
|
32
32
|
mcp_servers?: string[] | undefined;
|
|
33
33
|
trigger_type?: "slack" | "schedule" | undefined;
|
|
34
|
-
name?: string | undefined;
|
|
35
34
|
id?: number | undefined;
|
|
35
|
+
name?: string | undefined;
|
|
36
36
|
agent_root_name?: string | undefined;
|
|
37
37
|
prompt_template?: string | undefined;
|
|
38
38
|
reuse_session?: boolean | undefined;
|
|
@@ -24,7 +24,11 @@ const TOOL_DESCRIPTION = `Create, update, delete, or toggle automation triggers.
|
|
|
24
24
|
|
|
25
25
|
**Trigger types:**
|
|
26
26
|
- **slack**: Triggered by Slack events (requires configuration with channel_id)
|
|
27
|
-
- **schedule**: Triggered on a
|
|
27
|
+
- **schedule**: Triggered on a recurring or one-time schedule
|
|
28
|
+
|
|
29
|
+
**Schedule configuration:**
|
|
30
|
+
- **Recurring**: \`{"interval": 2, "unit": "hours", "timezone": "UTC"}\` — fires every N units
|
|
31
|
+
- **One-time**: \`{"scheduled_at": "2026-04-15T14:30:00", "timezone": "America/New_York"}\` — fires once at the specified datetime (ISO 8601), then auto-disables
|
|
28
32
|
|
|
29
33
|
Use search_triggers first to see available triggers and Slack channels.`;
|
|
30
34
|
export function actionTriggerTool(_server, clientFactory) {
|
|
@@ -3,7 +3,7 @@ import { z } from 'zod';
|
|
|
3
3
|
import type { IAgentOrchestratorClient } from '../orchestrator-client/orchestrator-client.js';
|
|
4
4
|
export declare const ManageEnqueuedMessagesSchema: z.ZodObject<{
|
|
5
5
|
session_id: z.ZodUnion<[z.ZodString, z.ZodNumber]>;
|
|
6
|
-
action: z.ZodEnum<["list", "get", "create", "update", "delete", "reorder", "interrupt"]>;
|
|
6
|
+
action: z.ZodEnum<["list", "get", "create", "update", "delete", "reorder", "interrupt", "send_now"]>;
|
|
7
7
|
message_id: z.ZodOptional<z.ZodNumber>;
|
|
8
8
|
content: z.ZodOptional<z.ZodString>;
|
|
9
9
|
stop_condition: z.ZodOptional<z.ZodString>;
|
|
@@ -12,7 +12,7 @@ export declare const ManageEnqueuedMessagesSchema: z.ZodObject<{
|
|
|
12
12
|
per_page: z.ZodOptional<z.ZodNumber>;
|
|
13
13
|
}, "strip", z.ZodTypeAny, {
|
|
14
14
|
session_id: string | number;
|
|
15
|
-
action: "list" | "get" | "create" | "update" | "delete" | "reorder" | "interrupt";
|
|
15
|
+
action: "list" | "get" | "create" | "update" | "delete" | "reorder" | "interrupt" | "send_now";
|
|
16
16
|
per_page?: number | undefined;
|
|
17
17
|
page?: number | undefined;
|
|
18
18
|
stop_condition?: string | undefined;
|
|
@@ -21,7 +21,7 @@ export declare const ManageEnqueuedMessagesSchema: z.ZodObject<{
|
|
|
21
21
|
position?: number | undefined;
|
|
22
22
|
}, {
|
|
23
23
|
session_id: string | number;
|
|
24
|
-
action: "list" | "get" | "create" | "update" | "delete" | "reorder" | "interrupt";
|
|
24
|
+
action: "list" | "get" | "create" | "update" | "delete" | "reorder" | "interrupt" | "send_now";
|
|
25
25
|
per_page?: number | undefined;
|
|
26
26
|
page?: number | undefined;
|
|
27
27
|
stop_condition?: string | undefined;
|
|
@@ -43,7 +43,7 @@ export declare function manageEnqueuedMessagesTool(_server: Server, clientFactor
|
|
|
43
43
|
};
|
|
44
44
|
action: {
|
|
45
45
|
type: string;
|
|
46
|
-
enum: readonly ["list", "get", "create", "update", "delete", "reorder", "interrupt"];
|
|
46
|
+
enum: readonly ["list", "get", "create", "update", "delete", "reorder", "interrupt", "send_now"];
|
|
47
47
|
description: string;
|
|
48
48
|
};
|
|
49
49
|
message_id: {
|
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
const ACTION_ENUM = [
|
|
2
|
+
const ACTION_ENUM = [
|
|
3
|
+
'list',
|
|
4
|
+
'get',
|
|
5
|
+
'create',
|
|
6
|
+
'update',
|
|
7
|
+
'delete',
|
|
8
|
+
'reorder',
|
|
9
|
+
'interrupt',
|
|
10
|
+
'send_now',
|
|
11
|
+
];
|
|
3
12
|
export const ManageEnqueuedMessagesSchema = z.object({
|
|
4
13
|
session_id: z.union([z.string(), z.number()]),
|
|
5
14
|
action: z.enum(ACTION_ENUM),
|
|
@@ -10,18 +19,23 @@ export const ManageEnqueuedMessagesSchema = z.object({
|
|
|
10
19
|
page: z.number().min(1).optional(),
|
|
11
20
|
per_page: z.number().min(1).max(100).optional(),
|
|
12
21
|
});
|
|
13
|
-
const TOOL_DESCRIPTION = `
|
|
22
|
+
const TOOL_DESCRIPTION = `Queue messages for later delivery or send messages immediately to an agent session.
|
|
14
23
|
|
|
15
|
-
**
|
|
16
|
-
- **
|
|
17
|
-
- **
|
|
18
|
-
|
|
19
|
-
|
|
24
|
+
**Quick guide — two ways to send a message:**
|
|
25
|
+
- **send_now**: Interrupt the session and deliver a message immediately, even if the session is running. One step — just provide "content".
|
|
26
|
+
- **create**: Add a message to the queue for delivery when the session finishes its current work. Does NOT send immediately.
|
|
27
|
+
|
|
28
|
+
Use "send_now" when you need the agent to act on something urgently. Use "create" when the message can wait until the session is idle.
|
|
29
|
+
|
|
30
|
+
**All actions:**
|
|
31
|
+
- **send_now**: Interrupt the session and deliver a message immediately (requires "content"). The session is paused, the message is sent, and the session resumes with this message. Works regardless of session state.
|
|
32
|
+
- **create**: Add a new message to the end of the queue for later delivery (requires "content"). The message waits until the session becomes idle.
|
|
33
|
+
- **list**: List all enqueued messages for a session (supports pagination with "page" and "per_page")
|
|
34
|
+
- **get**: Get a specific enqueued message by ID (requires "message_id")
|
|
35
|
+
- **update**: Update an existing queued message's content or stop condition (requires "message_id")
|
|
20
36
|
- **delete**: Remove a message from the queue (requires "message_id")
|
|
21
37
|
- **reorder**: Change a message's position in the queue (requires "message_id" and "position")
|
|
22
|
-
- **interrupt**: Pause the session and send
|
|
23
|
-
|
|
24
|
-
Enqueued messages are follow-up prompts queued to be sent to the agent in order.`;
|
|
38
|
+
- **interrupt**: Pause the session and send an existing queued message immediately (requires "message_id"). Prefer "send_now" for new messages — "interrupt" is for promoting an already-queued message.`;
|
|
25
39
|
export function manageEnqueuedMessagesTool(_server, clientFactory) {
|
|
26
40
|
return {
|
|
27
41
|
name: 'manage_enqueued_messages',
|
|
@@ -36,7 +50,7 @@ export function manageEnqueuedMessagesTool(_server, clientFactory) {
|
|
|
36
50
|
action: {
|
|
37
51
|
type: 'string',
|
|
38
52
|
enum: ACTION_ENUM,
|
|
39
|
-
description: 'Action to perform
|
|
53
|
+
description: 'Action to perform. Use "send_now" to interrupt and deliver immediately. Use "create" to queue for later.',
|
|
40
54
|
},
|
|
41
55
|
message_id: {
|
|
42
56
|
type: 'number',
|
|
@@ -44,11 +58,11 @@ export function manageEnqueuedMessagesTool(_server, clientFactory) {
|
|
|
44
58
|
},
|
|
45
59
|
content: {
|
|
46
60
|
type: 'string',
|
|
47
|
-
description: 'Message content. Required for create. Optional for update.',
|
|
61
|
+
description: 'Message content. Required for create and send_now. Optional for update.',
|
|
48
62
|
},
|
|
49
63
|
stop_condition: {
|
|
50
64
|
type: 'string',
|
|
51
|
-
description: 'Stop condition for this message. Optional for create and update.',
|
|
65
|
+
description: 'Stop condition for this message. Optional for create, send_now, and update.',
|
|
52
66
|
},
|
|
53
67
|
position: {
|
|
54
68
|
type: 'number',
|
|
@@ -132,7 +146,9 @@ export function manageEnqueuedMessagesTool(_server, clientFactory) {
|
|
|
132
146
|
stop_condition: stop_condition || undefined,
|
|
133
147
|
});
|
|
134
148
|
result = [
|
|
135
|
-
'## Message
|
|
149
|
+
'## Message Queued',
|
|
150
|
+
'',
|
|
151
|
+
`Message added to queue — it will be delivered when the session becomes idle.`,
|
|
136
152
|
'',
|
|
137
153
|
`- **ID:** ${msg.id}`,
|
|
138
154
|
`- **Position:** ${msg.position}`,
|
|
@@ -233,6 +249,33 @@ export function manageEnqueuedMessagesTool(_server, clientFactory) {
|
|
|
233
249
|
].join('\n');
|
|
234
250
|
break;
|
|
235
251
|
}
|
|
252
|
+
case 'send_now': {
|
|
253
|
+
if (!content) {
|
|
254
|
+
return {
|
|
255
|
+
content: [
|
|
256
|
+
{
|
|
257
|
+
type: 'text',
|
|
258
|
+
text: 'Error: "content" is required for the "send_now" action.',
|
|
259
|
+
},
|
|
260
|
+
],
|
|
261
|
+
isError: true,
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
const response = await client.followUp(session_id, content, {
|
|
265
|
+
force_immediate: true,
|
|
266
|
+
stop_condition: stop_condition || undefined,
|
|
267
|
+
});
|
|
268
|
+
result = [
|
|
269
|
+
'## Message Sent Immediately',
|
|
270
|
+
'',
|
|
271
|
+
`The session was interrupted and the message was delivered.`,
|
|
272
|
+
'',
|
|
273
|
+
`- **Session ID:** ${response.session.id}`,
|
|
274
|
+
`- **Session Status:** ${response.session.status}`,
|
|
275
|
+
`- **Result:** ${response.message}`,
|
|
276
|
+
].join('\n');
|
|
277
|
+
break;
|
|
278
|
+
}
|
|
236
279
|
default: {
|
|
237
280
|
const _exhaustiveCheck = action;
|
|
238
281
|
return {
|
|
@@ -0,0 +1,61 @@
|
|
|
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 WakeMeUpLaterSchema: z.ZodObject<{
|
|
5
|
+
session_id: z.ZodUnion<[z.ZodString, z.ZodNumber]>;
|
|
6
|
+
wake_at: z.ZodString;
|
|
7
|
+
timezone: z.ZodOptional<z.ZodString>;
|
|
8
|
+
prompt: z.ZodString;
|
|
9
|
+
}, "strip", z.ZodTypeAny, {
|
|
10
|
+
prompt: string;
|
|
11
|
+
session_id: string | number;
|
|
12
|
+
wake_at: string;
|
|
13
|
+
timezone?: string | undefined;
|
|
14
|
+
}, {
|
|
15
|
+
prompt: string;
|
|
16
|
+
session_id: string | number;
|
|
17
|
+
wake_at: string;
|
|
18
|
+
timezone?: string | undefined;
|
|
19
|
+
}>;
|
|
20
|
+
export declare function wakeMeUpLaterTool(_server: Server, clientFactory: () => IAgentOrchestratorClient): {
|
|
21
|
+
name: string;
|
|
22
|
+
description: string;
|
|
23
|
+
inputSchema: {
|
|
24
|
+
type: "object";
|
|
25
|
+
properties: {
|
|
26
|
+
session_id: {
|
|
27
|
+
oneOf: {
|
|
28
|
+
type: string;
|
|
29
|
+
}[];
|
|
30
|
+
description: string;
|
|
31
|
+
};
|
|
32
|
+
wake_at: {
|
|
33
|
+
type: string;
|
|
34
|
+
description: string;
|
|
35
|
+
};
|
|
36
|
+
timezone: {
|
|
37
|
+
type: string;
|
|
38
|
+
description: string;
|
|
39
|
+
};
|
|
40
|
+
prompt: {
|
|
41
|
+
type: string;
|
|
42
|
+
description: string;
|
|
43
|
+
};
|
|
44
|
+
};
|
|
45
|
+
required: string[];
|
|
46
|
+
};
|
|
47
|
+
handler: (args: unknown) => Promise<{
|
|
48
|
+
content: {
|
|
49
|
+
type: string;
|
|
50
|
+
text: string;
|
|
51
|
+
}[];
|
|
52
|
+
isError: boolean;
|
|
53
|
+
} | {
|
|
54
|
+
content: {
|
|
55
|
+
type: string;
|
|
56
|
+
text: string;
|
|
57
|
+
}[];
|
|
58
|
+
isError?: undefined;
|
|
59
|
+
}>;
|
|
60
|
+
};
|
|
61
|
+
//# sourceMappingURL=wake-me-up-later.d.ts.map
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { parseAllowedAgentRoots } from '../allowed-agent-roots.js';
|
|
3
|
+
export const WakeMeUpLaterSchema = z.object({
|
|
4
|
+
session_id: z.union([z.string(), z.number()]),
|
|
5
|
+
wake_at: z.string(),
|
|
6
|
+
timezone: z.string().optional(),
|
|
7
|
+
prompt: z.string(),
|
|
8
|
+
});
|
|
9
|
+
function buildToolDescription() {
|
|
10
|
+
const now = new Date();
|
|
11
|
+
const utcNow = now.toISOString().replace(/\.\d{3}Z$/, 'Z');
|
|
12
|
+
return `Schedule this session to be woken up at a specific time. The session will be put to sleep (waiting status) and a one-time trigger will fire at the specified time to resume it with the given prompt. If the session is manually resumed before the scheduled time, the trigger will be silently dropped.
|
|
13
|
+
|
|
14
|
+
**Current server time:** ${utcNow} (UTC). Use this as your reference point when calculating wake-up times.
|
|
15
|
+
|
|
16
|
+
**Timezone handling:**
|
|
17
|
+
- The \`wake_at\` parameter is interpreted in the timezone specified by \`timezone\` (default: "UTC").
|
|
18
|
+
- To schedule "30 minutes from now": take the current UTC time above, add 30 minutes, and pass that as \`wake_at\` with timezone "UTC" (or omit timezone).
|
|
19
|
+
- To schedule at a wall-clock time in a specific timezone (e.g., "9am Eastern"): pass \`wake_at\` as "2026-04-15T09:00:00" with timezone "America/New_York". The server converts to UTC internally.
|
|
20
|
+
- Use IANA timezone names (e.g., "America/New_York", "Europe/London", "Asia/Tokyo"). Do NOT pass UTC offsets like "+05:00" in the timezone parameter.
|
|
21
|
+
- If you omit timezone, wake_at is treated as UTC.
|
|
22
|
+
|
|
23
|
+
**Parameters:**
|
|
24
|
+
- **session_id**: The session to wake up (must be in needs_input state)
|
|
25
|
+
- **wake_at**: ISO 8601 datetime without offset for when to wake up (e.g., "2026-04-15T14:30:00")
|
|
26
|
+
- **timezone**: IANA timezone for interpreting wake_at (default: "UTC", e.g., "America/New_York")
|
|
27
|
+
- **prompt**: The prompt to send when waking up the session
|
|
28
|
+
|
|
29
|
+
**What happens:**
|
|
30
|
+
1. Validates the session is in needs_input state
|
|
31
|
+
2. Puts the session to sleep (waiting status)
|
|
32
|
+
3. Creates a one-time schedule trigger that fires at the specified time
|
|
33
|
+
4. The trigger resumes the session with the provided prompt`;
|
|
34
|
+
}
|
|
35
|
+
export function wakeMeUpLaterTool(_server, clientFactory) {
|
|
36
|
+
return {
|
|
37
|
+
name: 'wake_me_up_later',
|
|
38
|
+
description: buildToolDescription(),
|
|
39
|
+
inputSchema: {
|
|
40
|
+
type: 'object',
|
|
41
|
+
properties: {
|
|
42
|
+
session_id: {
|
|
43
|
+
oneOf: [{ type: 'string' }, { type: 'number' }],
|
|
44
|
+
description: 'Session ID (numeric) or slug (string). Must be in needs_input state.',
|
|
45
|
+
},
|
|
46
|
+
wake_at: {
|
|
47
|
+
type: 'string',
|
|
48
|
+
description: 'ISO 8601 datetime for when to wake up (e.g., "2026-04-15T14:30:00").',
|
|
49
|
+
},
|
|
50
|
+
timezone: {
|
|
51
|
+
type: 'string',
|
|
52
|
+
description: 'Timezone for the wake_at datetime. Default: "UTC".',
|
|
53
|
+
},
|
|
54
|
+
prompt: {
|
|
55
|
+
type: 'string',
|
|
56
|
+
description: 'The prompt to send when waking up the session.',
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
required: ['session_id', 'wake_at', 'prompt'],
|
|
60
|
+
},
|
|
61
|
+
handler: async (args) => {
|
|
62
|
+
try {
|
|
63
|
+
const validated = WakeMeUpLaterSchema.parse(args);
|
|
64
|
+
const client = clientFactory();
|
|
65
|
+
const { session_id, wake_at, prompt } = validated;
|
|
66
|
+
const timezone = validated.timezone || 'UTC';
|
|
67
|
+
if (parseAllowedAgentRoots() !== null) {
|
|
68
|
+
return {
|
|
69
|
+
content: [
|
|
70
|
+
{
|
|
71
|
+
type: 'text',
|
|
72
|
+
text: 'Error: wake_me_up_later is not allowed when ALLOWED_AGENT_ROOTS is set. Triggers cannot be created because sessions are restricted to specific preconfigured agent roots.',
|
|
73
|
+
},
|
|
74
|
+
],
|
|
75
|
+
isError: true,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
const session = await client.getSession(session_id);
|
|
79
|
+
if (session.status !== 'needs_input') {
|
|
80
|
+
return {
|
|
81
|
+
content: [
|
|
82
|
+
{
|
|
83
|
+
type: 'text',
|
|
84
|
+
text: `Error: Session must be in "needs_input" state to sleep (current: "${session.status}"). Only idle sessions can be scheduled for a delayed wake-up.`,
|
|
85
|
+
},
|
|
86
|
+
],
|
|
87
|
+
isError: true,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
const sleepingSession = await client.sleepSession(session_id);
|
|
91
|
+
let trigger;
|
|
92
|
+
try {
|
|
93
|
+
trigger = await client.createTrigger({
|
|
94
|
+
name: `Wake session #${sleepingSession.id} at ${wake_at}`,
|
|
95
|
+
trigger_type: 'schedule',
|
|
96
|
+
agent_root_name: session.agent_type,
|
|
97
|
+
prompt_template: prompt,
|
|
98
|
+
reuse_session: true,
|
|
99
|
+
configuration: {
|
|
100
|
+
scheduled_at: wake_at,
|
|
101
|
+
timezone,
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
catch (triggerError) {
|
|
106
|
+
return {
|
|
107
|
+
content: [
|
|
108
|
+
{
|
|
109
|
+
type: 'text',
|
|
110
|
+
text: `Error: Session ${sleepingSession.id} was put to sleep (waiting status) but trigger creation failed: ${triggerError instanceof Error ? triggerError.message : 'Unknown error'}. The session is now in "waiting" state and needs manual intervention (use action_session with "restart" or "follow_up" to recover).`,
|
|
111
|
+
},
|
|
112
|
+
],
|
|
113
|
+
isError: true,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
await client.updateSession(session_id, {
|
|
117
|
+
custom_metadata: {
|
|
118
|
+
...session.custom_metadata,
|
|
119
|
+
wake_trigger_id: trigger.id,
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
const lines = [
|
|
123
|
+
'## Session Scheduled for Wake-Up',
|
|
124
|
+
'',
|
|
125
|
+
`- **Session ID:** ${sleepingSession.id}`,
|
|
126
|
+
`- **Session Status:** ${sleepingSession.status}`,
|
|
127
|
+
`- **Wake At:** ${wake_at} (${timezone})`,
|
|
128
|
+
`- **Trigger ID:** ${trigger.id}`,
|
|
129
|
+
`- **Trigger Name:** ${trigger.name}`,
|
|
130
|
+
'',
|
|
131
|
+
'The session is now sleeping. It will be automatically resumed at the scheduled time with the provided prompt.',
|
|
132
|
+
];
|
|
133
|
+
return { content: [{ type: 'text', text: lines.join('\n') }] };
|
|
134
|
+
}
|
|
135
|
+
catch (error) {
|
|
136
|
+
return {
|
|
137
|
+
content: [
|
|
138
|
+
{
|
|
139
|
+
type: 'text',
|
|
140
|
+
text: `Error scheduling wake-up: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
141
|
+
},
|
|
142
|
+
],
|
|
143
|
+
isError: true,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
};
|
|
148
|
+
}
|
package/shared/tools.d.ts
CHANGED
|
@@ -2,9 +2,10 @@ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
|
2
2
|
import { ClientFactory } from './server.js';
|
|
3
3
|
/**
|
|
4
4
|
* Available tool groups for agent-orchestrator.
|
|
5
|
-
*
|
|
5
|
+
* - Domain groups: each domain has a base group (full access) and a _readonly variant (read-only)
|
|
6
|
+
* - Composite groups: curated cross-domain tool sets (e.g., self_session)
|
|
6
7
|
*/
|
|
7
|
-
export type ToolGroup = 'sessions' | 'sessions_readonly' | 'notifications' | 'notifications_readonly' | 'triggers' | 'triggers_readonly' | 'health' | 'health_readonly';
|
|
8
|
+
export type ToolGroup = 'sessions' | 'sessions_readonly' | 'notifications' | 'notifications_readonly' | 'triggers' | 'triggers_readonly' | 'health' | 'health_readonly' | 'self_session';
|
|
8
9
|
/**
|
|
9
10
|
* Parse enabled tool groups from environment variable or parameter.
|
|
10
11
|
* @param enabledGroupsParam - Comma-separated list of groups (e.g., "sessions,notifications")
|
package/shared/tools.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { ListToolsRequestSchema, CallToolRequestSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
2
|
-
//
|
|
2
|
+
// 15 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';
|
|
6
|
-
import { actionSessionTool } from './tools/action-session.js';
|
|
6
|
+
import { actionSessionTool, selfSessionActionSessionTool } from './tools/action-session.js';
|
|
7
7
|
import { getConfigsTool } from './tools/get-configs.js';
|
|
8
8
|
import { manageEnqueuedMessagesTool } from './tools/manage-enqueued-messages.js';
|
|
9
9
|
import { sendPushNotificationTool } from './tools/send-push-notification.js';
|
|
@@ -11,11 +11,12 @@ import { getNotificationsTool } from './tools/get-notifications.js';
|
|
|
11
11
|
import { actionNotificationTool } from './tools/action-notification.js';
|
|
12
12
|
import { searchTriggersTool } from './tools/search-triggers.js';
|
|
13
13
|
import { actionTriggerTool } from './tools/action-trigger.js';
|
|
14
|
+
import { wakeMeUpLaterTool } from './tools/wake-me-up-later.js';
|
|
14
15
|
import { getSystemHealthTool } from './tools/get-system-health.js';
|
|
15
16
|
import { actionHealthTool } from './tools/action-health.js';
|
|
16
17
|
import { getTranscriptArchiveTool } from './tools/get-transcript-archive.js';
|
|
17
18
|
/**
|
|
18
|
-
* All valid tool groups (
|
|
19
|
+
* All valid tool groups (domain groups, their _readonly variants, and composite groups)
|
|
19
20
|
*/
|
|
20
21
|
const VALID_TOOL_GROUPS = [
|
|
21
22
|
'sessions',
|
|
@@ -26,6 +27,7 @@ const VALID_TOOL_GROUPS = [
|
|
|
26
27
|
'triggers_readonly',
|
|
27
28
|
'health',
|
|
28
29
|
'health_readonly',
|
|
30
|
+
'self_session',
|
|
29
31
|
];
|
|
30
32
|
/**
|
|
31
33
|
* Base groups (without _readonly suffix) - used for default "all groups" behavior
|
|
@@ -58,41 +60,91 @@ export function parseEnabledToolGroups(enabledGroupsParam) {
|
|
|
58
60
|
/**
|
|
59
61
|
* All available tools with their group assignments.
|
|
60
62
|
*
|
|
61
|
-
*
|
|
63
|
+
* 15 tools across 4 domains + 1 composite group:
|
|
62
64
|
* - quick_search_sessions: Quick title-based search/list/get sessions by ID (sessions, read)
|
|
63
|
-
* - get_session: Get detailed session info with optional logs/transcripts (sessions, read)
|
|
64
|
-
* - get_configs: Fetch all static configuration (sessions, read)
|
|
65
|
+
* - get_session: Get detailed session info with optional logs/transcripts (sessions, read; self_session)
|
|
66
|
+
* - get_configs: Fetch all static configuration (sessions, read; self_session)
|
|
65
67
|
* - get_transcript_archive: Get transcript archive download URL and metadata (sessions, read)
|
|
66
68
|
* - start_session: Create a new session (sessions, write)
|
|
67
|
-
* - action_session: Perform session actions (sessions, write)
|
|
69
|
+
* - action_session: Perform session actions (sessions, write; self_session: filtered to update_notes, update_title, archive)
|
|
68
70
|
* - manage_enqueued_messages: Manage session message queue (sessions, write)
|
|
69
71
|
* - get_notifications: Get/list notifications and badge count (notifications, read)
|
|
70
|
-
* - send_push_notification: Send a push notification (notifications, write)
|
|
72
|
+
* - send_push_notification: Send a push notification (notifications, write; self_session)
|
|
71
73
|
* - action_notification: Mark read, dismiss notifications (notifications, write)
|
|
72
74
|
* - search_triggers: Search/list automation triggers (triggers, read)
|
|
73
75
|
* - action_trigger: Create, update, delete, toggle triggers (triggers, write)
|
|
76
|
+
* - wake_me_up_later: Schedule a session to be woken up at a specific time (triggers, write; self_session)
|
|
74
77
|
* - get_system_health: Get system health report and CLI status (health, read)
|
|
75
78
|
* - action_health: System maintenance actions (health, write)
|
|
76
79
|
*/
|
|
77
80
|
const ALL_TOOLS = [
|
|
78
81
|
// Session tools - read operations
|
|
79
|
-
{
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
82
|
+
{
|
|
83
|
+
factory: quickSearchSessionsTool,
|
|
84
|
+
group: 'sessions',
|
|
85
|
+
isWriteOperation: false,
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
factory: getSessionTool,
|
|
89
|
+
group: 'sessions',
|
|
90
|
+
isWriteOperation: false,
|
|
91
|
+
compositeGroups: ['self_session'],
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
factory: getConfigsTool,
|
|
95
|
+
group: 'sessions',
|
|
96
|
+
isWriteOperation: false,
|
|
97
|
+
compositeGroups: ['self_session'],
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
factory: getTranscriptArchiveTool,
|
|
101
|
+
group: 'sessions',
|
|
102
|
+
isWriteOperation: false,
|
|
103
|
+
},
|
|
83
104
|
// Session tools - write operations
|
|
84
105
|
{ factory: startSessionTool, group: 'sessions', isWriteOperation: true },
|
|
85
|
-
{
|
|
86
|
-
|
|
106
|
+
{
|
|
107
|
+
factory: actionSessionTool,
|
|
108
|
+
group: 'sessions',
|
|
109
|
+
isWriteOperation: true,
|
|
110
|
+
compositeGroups: ['self_session'],
|
|
111
|
+
compositeGroupFactoryOverrides: {
|
|
112
|
+
self_session: selfSessionActionSessionTool,
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
factory: manageEnqueuedMessagesTool,
|
|
117
|
+
group: 'sessions',
|
|
118
|
+
isWriteOperation: true,
|
|
119
|
+
},
|
|
87
120
|
// Notification tools - read operations
|
|
88
|
-
{
|
|
121
|
+
{
|
|
122
|
+
factory: getNotificationsTool,
|
|
123
|
+
group: 'notifications',
|
|
124
|
+
isWriteOperation: false,
|
|
125
|
+
},
|
|
89
126
|
// Notification tools - write operations
|
|
90
|
-
{
|
|
91
|
-
|
|
127
|
+
{
|
|
128
|
+
factory: sendPushNotificationTool,
|
|
129
|
+
group: 'notifications',
|
|
130
|
+
isWriteOperation: true,
|
|
131
|
+
compositeGroups: ['self_session'],
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
factory: actionNotificationTool,
|
|
135
|
+
group: 'notifications',
|
|
136
|
+
isWriteOperation: true,
|
|
137
|
+
},
|
|
92
138
|
// Trigger tools - read operations
|
|
93
139
|
{ factory: searchTriggersTool, group: 'triggers', isWriteOperation: false },
|
|
94
140
|
// Trigger tools - write operations
|
|
95
141
|
{ factory: actionTriggerTool, group: 'triggers', isWriteOperation: true },
|
|
142
|
+
{
|
|
143
|
+
factory: wakeMeUpLaterTool,
|
|
144
|
+
group: 'triggers',
|
|
145
|
+
isWriteOperation: true,
|
|
146
|
+
compositeGroups: ['self_session'],
|
|
147
|
+
},
|
|
96
148
|
// Health tools - read operations
|
|
97
149
|
{ factory: getSystemHealthTool, group: 'health', isWriteOperation: false },
|
|
98
150
|
// Health tools - write operations
|
|
@@ -115,8 +167,48 @@ function shouldIncludeTool(toolDef, enabledGroups) {
|
|
|
115
167
|
if (enabledGroups.includes(readonlyGroup) && !toolDef.isWriteOperation) {
|
|
116
168
|
return true;
|
|
117
169
|
}
|
|
170
|
+
// Check if any composite group that includes this tool is enabled
|
|
171
|
+
if (toolDef.compositeGroups) {
|
|
172
|
+
for (const compositeGroup of toolDef.compositeGroups) {
|
|
173
|
+
if (enabledGroups.includes(compositeGroup)) {
|
|
174
|
+
return true;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
118
178
|
return false;
|
|
119
179
|
}
|
|
180
|
+
/**
|
|
181
|
+
* Determine which factory to use for a tool based on enabled groups.
|
|
182
|
+
* Domain groups (base/readonly) use the default factory. Composite groups
|
|
183
|
+
* may have factory overrides (e.g., restricted action_session for self_session).
|
|
184
|
+
* Domain base group takes precedence over composite group overrides.
|
|
185
|
+
*
|
|
186
|
+
* Priority: base group > readonly group > composite group override > default factory.
|
|
187
|
+
* A write operation with only the readonly group enabled is NOT covered by the
|
|
188
|
+
* domain group — it falls through to composite group override logic.
|
|
189
|
+
*/
|
|
190
|
+
function getToolFactory(toolDef, enabledGroups) {
|
|
191
|
+
const baseGroup = toolDef.group;
|
|
192
|
+
const readonlyGroup = `${baseGroup}_readonly`;
|
|
193
|
+
// If included via base group (full access), always use the default factory
|
|
194
|
+
if (enabledGroups.includes(baseGroup)) {
|
|
195
|
+
return toolDef.factory;
|
|
196
|
+
}
|
|
197
|
+
// If included via readonly group AND this is a read operation, use the default factory
|
|
198
|
+
if (enabledGroups.includes(readonlyGroup) && !toolDef.isWriteOperation) {
|
|
199
|
+
return toolDef.factory;
|
|
200
|
+
}
|
|
201
|
+
// Otherwise, the tool is included via a composite group — check for factory overrides
|
|
202
|
+
if (toolDef.compositeGroupFactoryOverrides && toolDef.compositeGroups) {
|
|
203
|
+
for (const compositeGroup of toolDef.compositeGroups) {
|
|
204
|
+
if (enabledGroups.includes(compositeGroup) &&
|
|
205
|
+
toolDef.compositeGroupFactoryOverrides[compositeGroup]) {
|
|
206
|
+
return toolDef.compositeGroupFactoryOverrides[compositeGroup];
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
return toolDef.factory;
|
|
211
|
+
}
|
|
120
212
|
/**
|
|
121
213
|
* Creates a function to register all tools with the server.
|
|
122
214
|
* This pattern uses individual tool files for better modularity and testability.
|
|
@@ -133,8 +225,11 @@ export function createRegisterTools(clientFactory, enabledGroups) {
|
|
|
133
225
|
const enabledToolGroups = parseEnabledToolGroups(enabledGroups);
|
|
134
226
|
// Filter tools based on enabled groups
|
|
135
227
|
const enabledTools = ALL_TOOLS.filter((toolDef) => shouldIncludeTool(toolDef, enabledToolGroups));
|
|
136
|
-
// Create tool instances
|
|
137
|
-
const tools = enabledTools.map((toolDef) =>
|
|
228
|
+
// Create tool instances (using factory overrides for composite groups when applicable)
|
|
229
|
+
const tools = enabledTools.map((toolDef) => {
|
|
230
|
+
const factory = getToolFactory(toolDef, enabledToolGroups);
|
|
231
|
+
return factory(server, clientFactory);
|
|
232
|
+
});
|
|
138
233
|
// List available tools
|
|
139
234
|
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
140
235
|
return {
|