@toolplex/client 0.1.41 → 0.1.44

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.
@@ -1,5 +1,5 @@
1
1
  import { LLMContext } from "../shared/mcpServerTypes.js";
2
- import { ClientPermissions, ClientFlags } from "./toolplexApi/types.js";
2
+ import { ClientPermissions, ClientFlags, AutomationContext } from "./toolplexApi/types.js";
3
3
  /**
4
4
  * Maintains client context for the ToolPlex server
5
5
  */
@@ -15,14 +15,15 @@ export declare class ClientContext {
15
15
  private _isOrgUser;
16
16
  private _clientName;
17
17
  private _userId;
18
+ private _automationContext;
18
19
  get sessionId(): string;
19
20
  set sessionId(id: string);
20
21
  get dev(): boolean;
21
22
  set dev(isDev: boolean);
22
23
  get apiKey(): string;
23
24
  set apiKey(key: string);
24
- get clientMode(): "standard" | "restricted";
25
- set clientMode(mode: "standard" | "restricted");
25
+ get clientMode(): "standard" | "restricted" | "automation";
26
+ set clientMode(mode: "standard" | "restricted" | "automation");
26
27
  get clientName(): string;
27
28
  set clientName(name: string);
28
29
  get llmContext(): LLMContext;
@@ -42,5 +43,12 @@ export declare class ClientContext {
42
43
  */
43
44
  get userId(): string | null;
44
45
  set userId(id: string | null);
46
+ /**
47
+ * Automation context for HITL (Human-in-the-Loop) support.
48
+ * Only set when clientMode is 'automation'.
49
+ * Contains allowed tools, tools requiring approval, and notification config.
50
+ */
51
+ get automationContext(): AutomationContext | null;
52
+ set automationContext(context: AutomationContext | null);
45
53
  isInitialized(): boolean;
46
54
  }
@@ -14,6 +14,7 @@ export class ClientContext {
14
14
  this._isOrgUser = null;
15
15
  this._clientName = null;
16
16
  this._userId = null; // For system keys to specify user context
17
+ this._automationContext = null; // For automation mode HITL
17
18
  }
18
19
  get sessionId() {
19
20
  if (!this._sessionId) {
@@ -116,6 +117,17 @@ export class ClientContext {
116
117
  set userId(id) {
117
118
  this._userId = id;
118
119
  }
120
+ /**
121
+ * Automation context for HITL (Human-in-the-Loop) support.
122
+ * Only set when clientMode is 'automation'.
123
+ * Contains allowed tools, tools requiring approval, and notification config.
124
+ */
125
+ get automationContext() {
126
+ return this._automationContext;
127
+ }
128
+ set automationContext(context) {
129
+ this._automationContext = context;
130
+ }
119
131
  isInitialized() {
120
132
  return !!(this._sessionId &&
121
133
  this._apiKey &&
@@ -6,7 +6,10 @@ dotenv.config();
6
6
  FileLogger.initialize("mcp-server");
7
7
  const isDev = process.env.DEV === "true";
8
8
  const apiKey = process.env.TOOLPLEX_API_KEY;
9
- const clientMode = process.env.TOOLPLEX_CLIENT_MODE || "standard";
9
+ // CLIENT_MODE can come from ai-engine (automation mode) or TOOLPLEX_CLIENT_MODE (legacy)
10
+ const clientMode = process.env.CLIENT_MODE ||
11
+ process.env.TOOLPLEX_CLIENT_MODE ||
12
+ "standard";
10
13
  const clientName = process.env.CLIENT_NAME || "unknown";
11
14
  const logLevel = process.env.LOG_LEVEL || "info";
12
15
  // Optional user ID for per-user telemetry (system API keys only)
@@ -39,6 +42,20 @@ if (process.env.TOOLPLEX_SESSION_RESUME_HISTORY) {
39
42
  FileLogger.warn(`Failed to parse session resume history: ${error}`);
40
43
  }
41
44
  }
45
+ // Parse automation context for HITL support (only in automation mode)
46
+ let automationContext;
47
+ if (process.env.AUTOMATION_CONTEXT) {
48
+ try {
49
+ automationContext = JSON.parse(process.env.AUTOMATION_CONTEXT);
50
+ FileLogger.info(`Parsed automation context - ` +
51
+ `automationId: ${automationContext?.automationId}, ` +
52
+ `runId: ${automationContext?.runId}, ` +
53
+ `toolsRequiringApproval: ${automationContext?.toolsRequiringApproval?.length || 0}`);
54
+ }
55
+ catch (error) {
56
+ FileLogger.warn(`Failed to parse automation context: ${error}`);
57
+ }
58
+ }
42
59
  if (!apiKey) {
43
60
  process.exit(1);
44
61
  }
@@ -51,6 +68,7 @@ const config = {
51
68
  bundledDependencies,
52
69
  sessionResumeHistory,
53
70
  userId,
71
+ automationContext,
54
72
  };
55
73
  serve(config).catch(() => {
56
74
  process.exit(1);
@@ -27,6 +27,32 @@ export async function handleCallTool(params) {
27
27
  validateServerIdOrThrow(params.server_id);
28
28
  // Enforce call tool policy
29
29
  policyEnforcer.enforceCallToolPolicy(params.server_id);
30
+ // Automation mode: Check if tool requires HITL approval
31
+ const automationContext = clientContext.automationContext;
32
+ if (automationContext) {
33
+ const toolKey = `${params.server_id}.${params.tool_name}`;
34
+ // Check if tool requires HITL approval
35
+ if (automationContext.toolsRequiringApproval.includes(toolKey)) {
36
+ await logger.info(`Tool ${toolKey} requires HITL approval`);
37
+ // Return HITL signal for cloud-agent to handle
38
+ return {
39
+ content: [
40
+ {
41
+ type: "text",
42
+ text: JSON.stringify({
43
+ _hitl_required: true,
44
+ _hitl_type: "tool_approval",
45
+ tool_key: toolKey,
46
+ server_id: params.server_id,
47
+ tool_name: params.tool_name,
48
+ args: params.arguments,
49
+ context: `Requesting approval to run "${params.tool_name}" on "${params.server_id}"`,
50
+ }),
51
+ },
52
+ ],
53
+ };
54
+ }
55
+ }
30
56
  const client = await findServerManagerClient(params.server_id, serverManagerClients);
31
57
  const response = await client.sendRequest("call_tool", params);
32
58
  if ("error" in response) {
@@ -0,0 +1,8 @@
1
+ import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
2
+ import { NotifyParams } from "../../shared/mcpServerTypes.js";
3
+ /**
4
+ * Handler for the notify tool.
5
+ * Only available in automation mode.
6
+ * Returns an HITL signal for the cloud-agent to process.
7
+ */
8
+ export declare function handleNotify(params: NotifyParams): Promise<CallToolResult>;
@@ -0,0 +1,86 @@
1
+ import { FileLogger } from "../../shared/fileLogger.js";
2
+ import Registry from "../registry.js";
3
+ const logger = FileLogger;
4
+ /**
5
+ * Handler for the notify tool.
6
+ * Only available in automation mode.
7
+ * Returns an HITL signal for the cloud-agent to process.
8
+ */
9
+ export async function handleNotify(params) {
10
+ const startTime = Date.now();
11
+ await logger.debug(`Handling notify request with params: ${JSON.stringify(params)}`);
12
+ const telemetryLogger = Registry.getTelemetryLogger();
13
+ const clientContext = Registry.getClientContext();
14
+ try {
15
+ // Verify we're in automation mode
16
+ if (clientContext.clientMode !== "automation") {
17
+ throw new Error("The notify tool is only available in automation mode. It allows the agent to send notifications to the automation owner.");
18
+ }
19
+ // Verify automation context is set
20
+ const automationContext = clientContext.automationContext;
21
+ if (!automationContext) {
22
+ throw new Error("Automation context not configured. Cannot send notifications without automation configuration.");
23
+ }
24
+ // Verify notification email is configured
25
+ if (!automationContext.notificationEmail) {
26
+ throw new Error("No notification email configured for this automation. Cannot send notification.");
27
+ }
28
+ // Validate multi_choice has options
29
+ if (params.response_type === "multi_choice") {
30
+ if (!params.response_options || params.response_options.length === 0) {
31
+ throw new Error("response_options is required for multi_choice response type");
32
+ }
33
+ }
34
+ await logger.info(`Sending HITL notification: ${params.title} (pause: ${params.pause_until_response})`);
35
+ await telemetryLogger.log("client_notify", {
36
+ success: true,
37
+ log_context: {
38
+ response_type: params.response_type,
39
+ pause_until_response: params.pause_until_response,
40
+ has_context: !!params.context,
41
+ },
42
+ latency_ms: Date.now() - startTime,
43
+ });
44
+ // Return HITL signal for cloud-agent to handle
45
+ // The cloud-agent will parse this and create the notification
46
+ return {
47
+ content: [
48
+ {
49
+ type: "text",
50
+ text: JSON.stringify({
51
+ _hitl_required: true,
52
+ _hitl_type: "agent_notify",
53
+ title: params.title,
54
+ content: params.content,
55
+ context: params.context,
56
+ response_type: params.response_type,
57
+ response_options: params.response_options,
58
+ pause_until_response: params.pause_until_response,
59
+ automation_id: automationContext.automationId,
60
+ run_id: automationContext.runId,
61
+ notification_email: automationContext.notificationEmail,
62
+ expiration_hours: automationContext.expirationHours,
63
+ }),
64
+ },
65
+ ],
66
+ };
67
+ }
68
+ catch (error) {
69
+ const errorMessage = error instanceof Error ? error.message : String(error);
70
+ await logger.error(`Failed to send notification: ${errorMessage}`);
71
+ await telemetryLogger.log("client_notify", {
72
+ success: false,
73
+ pii_sanitized_error_message: errorMessage,
74
+ latency_ms: Date.now() - startTime,
75
+ });
76
+ return {
77
+ isError: true,
78
+ content: [
79
+ {
80
+ type: "text",
81
+ text: `Failed to send notification: ${errorMessage}`,
82
+ },
83
+ ],
84
+ };
85
+ }
86
+ }
@@ -7,6 +7,22 @@ export interface ClientPermissions {
7
7
  allowed_mcp_servers?: string[];
8
8
  custom_prompt?: string;
9
9
  }
10
+ /**
11
+ * Automation context for HITL (Human-in-the-Loop) support.
12
+ * Passed when clientMode is 'automation' to enable tool approval and notifications.
13
+ */
14
+ export interface AutomationContext {
15
+ automationId: string;
16
+ runId: string;
17
+ /** Tools that require user approval before execution (format: "server_id.tool_name") */
18
+ toolsRequiringApproval: string[];
19
+ /** Email address for notifications */
20
+ notificationEmail?: string;
21
+ /** Hours before HITL decisions expire (default 24) */
22
+ expirationHours: number;
23
+ /** Notification instructions from automation config */
24
+ notifyInstructions?: string;
25
+ }
10
26
  export interface ClientFlags {
11
27
  desktop_commander_server_id: string;
12
28
  blocked_mcp_servers: string[];
@@ -20,9 +20,10 @@ import { handleLogPlaybookUsage } from "./toolHandlers/logPlaybookUsageHandler.j
20
20
  import { handleLookupEntityTool } from "./toolHandlers/lookupEntityHandler.js";
21
21
  import { handleSubmitFeedback } from "./toolHandlers/submitFeedbackHandler.js";
22
22
  import { handleGetServerConfig } from "./toolHandlers/getServerConfigHandler.js";
23
+ import { handleNotify } from "./toolHandlers/notifyHandler.js";
23
24
  import { StdioServerManagerClient } from "../shared/stdioServerManagerClient.js";
24
25
  import { FileLogger } from "../shared/fileLogger.js";
25
- import { CallToolParamsSchema, InitializeToolplexParamsSchema, InstallParamsSchema, ListToolsParamsSchema, SearchParamsSchema, UninstallParamsSchema, SavePlaybookParamsSchema, LogPlaybookUsageParamsSchema, LookupEntityParamsSchema, SubmitFeedbackParamsSchema, GetServerConfigParamsSchema, } from "../shared/mcpServerTypes.js";
26
+ import { CallToolParamsSchema, InitializeToolplexParamsSchema, InstallParamsSchema, ListToolsParamsSchema, SearchParamsSchema, UninstallParamsSchema, SavePlaybookParamsSchema, LogPlaybookUsageParamsSchema, LookupEntityParamsSchema, SubmitFeedbackParamsSchema, GetServerConfigParamsSchema, NotifyParamsSchema, } from "../shared/mcpServerTypes.js";
26
27
  import { version as clientVersion } from "../version.js";
27
28
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
28
29
  const logger = FileLogger;
@@ -36,6 +37,10 @@ export async function serve(config) {
36
37
  if (config.userId) {
37
38
  clientContext.userId = config.userId;
38
39
  }
40
+ // Set automation context for HITL support (only in automation mode)
41
+ if (config.automationContext) {
42
+ clientContext.automationContext = config.automationContext;
43
+ }
39
44
  await Registry.init(clientContext);
40
45
  // Store bundled dependencies in Registry for use throughout the application
41
46
  if (config.bundledDependencies) {
@@ -237,6 +242,17 @@ export async function serve(config) {
237
242
  result = await handleGetServerConfig(parsed.data);
238
243
  break;
239
244
  }
245
+ // Notify tool handler (automation mode only)
246
+ case "notify": {
247
+ await logger.debug("Handling notify request");
248
+ const parsed = NotifyParamsSchema.safeParse(params);
249
+ if (!parsed.success)
250
+ throw new Error(`Invalid notify params: ${parsed.error}`);
251
+ if (!clientContext.isInitialized())
252
+ throw new Error(`ToolPlex is not initialized`);
253
+ result = await handleNotify(parsed.data);
254
+ break;
255
+ }
240
256
  default:
241
257
  await logger.warn(`Unknown tool requested: ${name}`);
242
258
  result = {
@@ -1,5 +1,6 @@
1
1
  import { z } from "zod";
2
- export type ClientMode = "standard" | "restricted";
2
+ import type { AutomationContext } from "../mcp-server/toolplexApi/types.js";
3
+ export type ClientMode = "standard" | "restricted" | "automation";
3
4
  export type LogLevel = "error" | "warn" | "info" | "debug";
4
5
  /**
5
6
  * Paths to bundled dependencies provided by the host application (e.g., Electron).
@@ -40,6 +41,8 @@ export interface ToolplexServerConfig {
40
41
  };
41
42
  /** Optional user ID for system API keys to specify user context (per-user telemetry) */
42
43
  userId?: string;
44
+ /** Automation context for HITL support (only set in automation mode) */
45
+ automationContext?: AutomationContext;
43
46
  }
44
47
  export declare const TransportTypeSchema: z.ZodEnum<["stdio", "sse"]>;
45
48
  export type TransportType = z.infer<typeof TransportTypeSchema>;
@@ -448,3 +451,26 @@ export declare const SubmitFeedbackParamsSchema: z.ZodObject<{
448
451
  } | undefined;
449
452
  }>;
450
453
  export type SubmitFeedbackParams = z.infer<typeof SubmitFeedbackParamsSchema>;
454
+ export declare const NotifyParamsSchema: z.ZodObject<{
455
+ title: z.ZodString;
456
+ content: z.ZodString;
457
+ context: z.ZodOptional<z.ZodString>;
458
+ response_type: z.ZodEnum<["boolean", "multi_choice", "freeform"]>;
459
+ response_options: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
460
+ pause_until_response: z.ZodBoolean;
461
+ }, "strip", z.ZodTypeAny, {
462
+ title: string;
463
+ content: string;
464
+ response_type: "boolean" | "multi_choice" | "freeform";
465
+ pause_until_response: boolean;
466
+ context?: string | undefined;
467
+ response_options?: string[] | undefined;
468
+ }, {
469
+ title: string;
470
+ content: string;
471
+ response_type: "boolean" | "multi_choice" | "freeform";
472
+ pause_until_response: boolean;
473
+ context?: string | undefined;
474
+ response_options?: string[] | undefined;
475
+ }>;
476
+ export type NotifyParams = z.infer<typeof NotifyParamsSchema>;
@@ -161,3 +161,14 @@ export const SubmitFeedbackParamsSchema = z.object({
161
161
  })
162
162
  .optional(),
163
163
  });
164
+ // --------------------
165
+ // NotifyParams (for automation HITL notifications)
166
+ // --------------------
167
+ export const NotifyParamsSchema = z.object({
168
+ title: z.string().max(100),
169
+ content: z.string(),
170
+ context: z.string().optional(),
171
+ response_type: z.enum(["boolean", "multi_choice", "freeform"]),
172
+ response_options: z.array(z.string()).optional(),
173
+ pause_until_response: z.boolean(),
174
+ });
@@ -14,6 +14,7 @@ export class ClientContext {
14
14
  this._isOrgUser = null;
15
15
  this._clientName = null;
16
16
  this._userId = null; // For system keys to specify user context
17
+ this._automationContext = null; // For automation mode HITL
17
18
  }
18
19
  get sessionId() {
19
20
  if (!this._sessionId) {
@@ -116,6 +117,17 @@ export class ClientContext {
116
117
  set userId(id) {
117
118
  this._userId = id;
118
119
  }
120
+ /**
121
+ * Automation context for HITL (Human-in-the-Loop) support.
122
+ * Only set when clientMode is 'automation'.
123
+ * Contains allowed tools, tools requiring approval, and notification config.
124
+ */
125
+ get automationContext() {
126
+ return this._automationContext;
127
+ }
128
+ set automationContext(context) {
129
+ this._automationContext = context;
130
+ }
119
131
  isInitialized() {
120
132
  return !!(this._sessionId &&
121
133
  this._apiKey &&
@@ -161,3 +161,14 @@ export const SubmitFeedbackParamsSchema = z.object({
161
161
  })
162
162
  .optional(),
163
163
  });
164
+ // --------------------
165
+ // NotifyParams (for automation HITL notifications)
166
+ // --------------------
167
+ export const NotifyParamsSchema = z.object({
168
+ title: z.string().max(100),
169
+ content: z.string(),
170
+ context: z.string().optional(),
171
+ response_type: z.enum(["boolean", "multi_choice", "freeform"]),
172
+ response_options: z.array(z.string()).optional(),
173
+ pause_until_response: z.boolean(),
174
+ });
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const version = "0.1.41";
1
+ export declare const version = "0.1.44";
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const version = '0.1.41';
1
+ export const version = '0.1.44';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@toolplex/client",
3
- "version": "0.1.41",
3
+ "version": "0.1.44",
4
4
  "author": "ToolPlex LLC",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "description": "The official ToolPlex client for AI agent tool discovery and execution",