@toolplex/client 0.1.26 → 0.1.28

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.
@@ -20,6 +20,22 @@ const bundledDependencies = {
20
20
  uvx: process.env.TOOLPLEX_UVX_PATH,
21
21
  git: process.env.TOOLPLEX_GIT_PATH,
22
22
  };
23
+ // Parse session resume history for restored chat sessions
24
+ // This allows the enforcement layer to validate save_playbook and submit_feedback
25
+ // based on historical tool usage from the database
26
+ let sessionResumeHistory;
27
+ if (process.env.TOOLPLEX_SESSION_RESUME_HISTORY) {
28
+ try {
29
+ sessionResumeHistory = JSON.parse(process.env.TOOLPLEX_SESSION_RESUME_HISTORY);
30
+ FileLogger.info(`Parsed session resume history - ` +
31
+ `${sessionResumeHistory?.tool_calls.length || 0} tool calls, ` +
32
+ `${sessionResumeHistory?.installs.length || 0} installs, ` +
33
+ `${sessionResumeHistory?.uninstalls.length || 0} uninstalls`);
34
+ }
35
+ catch (error) {
36
+ FileLogger.warn(`Failed to parse session resume history: ${error}`);
37
+ }
38
+ }
23
39
  if (!apiKey) {
24
40
  process.exit(1);
25
41
  }
@@ -30,6 +46,7 @@ const config = {
30
46
  clientName,
31
47
  logLevel,
32
48
  bundledDependencies,
49
+ sessionResumeHistory,
33
50
  };
34
51
  serve(config).catch(() => {
35
52
  process.exit(1);
@@ -2,6 +2,10 @@ declare class CallToolObserver {
2
2
  private serverToolCalls;
3
3
  constructor();
4
4
  recordCall(serverId: string, toolName: string): void;
5
+ seedHistory(history: Array<{
6
+ server_id: string;
7
+ tool_name: string;
8
+ }>): void;
5
9
  wasServerCalled(serverId: string): boolean;
6
10
  wasToolCalled(serverId: string, toolName: string): boolean;
7
11
  clear(): void;
@@ -9,6 +9,12 @@ class CallToolObserver {
9
9
  }
10
10
  this.serverToolCalls.get(serverId).add(toolName);
11
11
  }
12
+ // Seed the observer with historical tool calls (for session resume)
13
+ seedHistory(history) {
14
+ history.forEach(({ server_id, tool_name }) => {
15
+ this.recordCall(server_id, tool_name);
16
+ });
17
+ }
12
18
  // Check if a server was called at all
13
19
  wasServerCalled(serverId) {
14
20
  return (this.serverToolCalls.has(serverId) &&
@@ -3,6 +3,11 @@ declare class InstallObserver {
3
3
  constructor();
4
4
  recordInstall(serverId: string): void;
5
5
  recordUninstall(serverId: string): void;
6
+ seedHistory(installs: Array<{
7
+ server_id: string;
8
+ }>, uninstalls: Array<{
9
+ server_id: string;
10
+ }>): void;
6
11
  wasServerInstalled(serverId: string): boolean;
7
12
  wasServerUninstalled(serverId: string): boolean;
8
13
  clear(): void;
@@ -10,6 +10,15 @@ class InstallObserver {
10
10
  recordUninstall(serverId) {
11
11
  this.recordAction(serverId, "uninstall");
12
12
  }
13
+ // Seed the observer with historical install/uninstall actions (for session resume)
14
+ seedHistory(installs, uninstalls) {
15
+ installs.forEach(({ server_id }) => {
16
+ this.recordInstall(server_id);
17
+ });
18
+ uninstalls.forEach(({ server_id }) => {
19
+ this.recordUninstall(server_id);
20
+ });
21
+ }
13
22
  // Check if a server has been installed
14
23
  wasServerInstalled(serverId) {
15
24
  return (this.serverInstallActions.has(serverId) &&
@@ -6,7 +6,7 @@ import { PromptsCache } from "./promptsCache.js";
6
6
  import { ToolDefinitionsCache } from "./toolDefinitionsCache.js";
7
7
  import { ServersCache } from "./serversCache.js";
8
8
  import { PolicyEnforcer } from "./policy/policyEnforcer.js";
9
- import { BundledDependencies } from "../shared/mcpServerTypes.js";
9
+ import { BundledDependencies, ToolplexServerConfig } from "../shared/mcpServerTypes.js";
10
10
  /**
11
11
  * In-memory global registry for the ToolPlex client.
12
12
  * Maintains singleton instances of core services and clients used throughout the application.
@@ -21,6 +21,7 @@ declare class Registry {
21
21
  private static _serversCache;
22
22
  private static _policyEnforcer;
23
23
  private static _bundledDependencies;
24
+ private static _serverConfig;
24
25
  static init(clientContext: ClientContext): Promise<void>;
25
26
  static getClientContext(): ClientContext;
26
27
  static getToolplexApiService(): ToolplexApiService;
@@ -46,6 +47,14 @@ declare class Registry {
46
47
  * Returns undefined if the dependency is not available.
47
48
  */
48
49
  static getBundledDependencyPath(depName: "node" | "python" | "git" | "uvx" | "npx"): string | undefined;
50
+ /**
51
+ * Set the server configuration (includes session resume history).
52
+ */
53
+ static setServerConfig(config: ToolplexServerConfig): void;
54
+ /**
55
+ * Get the server configuration.
56
+ */
57
+ static getServerConfig(): ToolplexServerConfig;
49
58
  static reset(): void;
50
59
  }
51
60
  export default Registry;
@@ -101,6 +101,21 @@ class Registry {
101
101
  static getBundledDependencyPath(depName) {
102
102
  return this._bundledDependencies[depName];
103
103
  }
104
+ /**
105
+ * Set the server configuration (includes session resume history).
106
+ */
107
+ static setServerConfig(config) {
108
+ this._serverConfig = config;
109
+ }
110
+ /**
111
+ * Get the server configuration.
112
+ */
113
+ static getServerConfig() {
114
+ if (!this._serverConfig) {
115
+ throw new Error("ServerConfig not set in Registry");
116
+ }
117
+ return this._serverConfig;
118
+ }
104
119
  static reset() {
105
120
  this._clientContext = null;
106
121
  this._toolplexApiService = null;
@@ -120,6 +135,7 @@ class Registry {
120
135
  }
121
136
  this._policyEnforcer = null;
122
137
  this._bundledDependencies = {};
138
+ this._serverConfig = null;
123
139
  }
124
140
  }
125
141
  Registry._clientContext = null;
@@ -131,4 +147,5 @@ Registry._toolDefinitionsCache = null;
131
147
  Registry._serversCache = null;
132
148
  Registry._policyEnforcer = null;
133
149
  Registry._bundledDependencies = {};
150
+ Registry._serverConfig = null;
134
151
  export default Registry;
@@ -100,6 +100,21 @@ export async function handleInitialize(params) {
100
100
  promptsCache.init(processedPrompts);
101
101
  // Init PolicyEnforce after setting permissions and flags
102
102
  policyEnforcer.init(clientContext);
103
+ // Seed enforcement history if provided (for restored sessions)
104
+ const serverConfig = Registry.getServerConfig();
105
+ if (serverConfig.sessionResumeHistory) {
106
+ const { tool_calls, installs, uninstalls } = serverConfig.sessionResumeHistory;
107
+ await logger.info(`Seeding enforcement history from restored session - ` +
108
+ `${tool_calls.length} tool calls, ${installs.length} installs, ${uninstalls.length} uninstalls`);
109
+ const callToolObserver = policyEnforcer.getCallToolObserver();
110
+ callToolObserver.seedHistory(tool_calls);
111
+ const installObserver = policyEnforcer.getInstallObserver();
112
+ installObserver.seedHistory(installs, uninstalls);
113
+ await logger.info("Enforcement history seeded successfully");
114
+ }
115
+ else {
116
+ await logger.debug("No session resume history provided (new session)");
117
+ }
103
118
  const allSucceeded = serverManagerInitResults.succeeded;
104
119
  const allFailures = serverManagerInitResults.failures;
105
120
  // Initialize the serversCache with the succeeded servers
@@ -68,7 +68,7 @@ function sanitizeServerConfig(config) {
68
68
  env_count: config.env ? Object.keys(config.env).length : 0,
69
69
  };
70
70
  }
71
- async function installServer(serverId, serverName, description, serverManagerClient, serverConfig) {
71
+ async function installServer(serverId, serverName, description, serverManagerClient, serverConfig, timeoutMs) {
72
72
  await logger.info(`Starting installation of tool ${serverId}: ${serverName}`);
73
73
  await logger.debug(`Server config: ${JSON.stringify(serverConfig)}, Server ID: ${serverId}`);
74
74
  const response = await serverManagerClient.sendRequest("install", {
@@ -76,7 +76,7 @@ async function installServer(serverId, serverName, description, serverManagerCli
76
76
  server_name: serverName,
77
77
  description: description,
78
78
  config: serverConfig,
79
- });
79
+ }, timeoutMs);
80
80
  if ("error" in response) {
81
81
  const error = `Server installation failed: ${response.error.message}`;
82
82
  await logger.error(error);
@@ -147,7 +147,7 @@ export async function handleInstallServer(params) {
147
147
  throw new Error(`Unsupported runtime: ${runtime}`);
148
148
  }
149
149
  // Install server
150
- const installResult = await installServer(server_id, server_name, description, client, config);
150
+ const installResult = await installServer(server_id, server_name, description, client, config, params.timeout_ms);
151
151
  // After successful install, refresh the servers cache
152
152
  await serversCache.refreshCache(serverManagerClients);
153
153
  // List tools on the newly installed server
@@ -39,6 +39,8 @@ export async function serve(config) {
39
39
  Registry.setBundledDependencies(config.bundledDependencies);
40
40
  await logger.debug(`Bundled dependencies registered: ${JSON.stringify(config.bundledDependencies)}`);
41
41
  }
42
+ // Store server config in Registry (includes session resume history)
43
+ Registry.setServerConfig(config);
42
44
  await logger.info(`Starting Toolplex server in ${config.dev ? "development" : "production"} mode`);
43
45
  const server = new Server({
44
46
  name: "toolplex-server",
@@ -21,6 +21,18 @@ export interface ToolplexServerConfig {
21
21
  clientName: string;
22
22
  logLevel: LogLevel;
23
23
  bundledDependencies?: BundledDependencies;
24
+ sessionResumeHistory?: {
25
+ tool_calls: Array<{
26
+ server_id: string;
27
+ tool_name: string;
28
+ }>;
29
+ installs: Array<{
30
+ server_id: string;
31
+ }>;
32
+ uninstalls: Array<{
33
+ server_id: string;
34
+ }>;
35
+ };
24
36
  }
25
37
  export declare const TransportTypeSchema: z.ZodEnum<["stdio", "sse"]>;
26
38
  export type TransportType = z.infer<typeof TransportTypeSchema>;
@@ -168,6 +180,7 @@ export declare const InstallParamsSchema: z.ZodObject<{
168
180
  env?: Record<string, string> | undefined;
169
181
  url?: string | undefined;
170
182
  }>;
183
+ timeout_ms: z.ZodOptional<z.ZodNumber>;
171
184
  }, "strip", z.ZodTypeAny, {
172
185
  server_name: string;
173
186
  description: string;
@@ -182,6 +195,7 @@ export declare const InstallParamsSchema: z.ZodObject<{
182
195
  env?: Record<string, string> | undefined;
183
196
  url?: string | undefined;
184
197
  };
198
+ timeout_ms?: number | undefined;
185
199
  }, {
186
200
  server_name: string;
187
201
  description: string;
@@ -196,6 +210,7 @@ export declare const InstallParamsSchema: z.ZodObject<{
196
210
  env?: Record<string, string> | undefined;
197
211
  url?: string | undefined;
198
212
  };
213
+ timeout_ms?: number | undefined;
199
214
  }>;
200
215
  export type InstallParams = z.infer<typeof InstallParamsSchema>;
201
216
  export declare const ListToolsParamsSchema: z.ZodObject<{
@@ -60,6 +60,7 @@ export const InstallParamsSchema = z.object({
60
60
  server_name: z.string(),
61
61
  description: z.string(),
62
62
  config: ServerConfigSchema,
63
+ timeout_ms: z.number().int().min(10000).max(300000).optional(),
63
64
  });
64
65
  // --------------------
65
66
  // ListToolsParams
@@ -7,6 +7,6 @@ export declare class StdioServerManagerClient {
7
7
  private env;
8
8
  constructor(command: string, args: string[], env?: NodeJS.ProcessEnv);
9
9
  start(): Promise<void>;
10
- sendRequest(method: string, params: any): Promise<any>;
10
+ sendRequest(method: string, params: any, timeoutMs?: number): Promise<any>;
11
11
  stop(): Promise<void>;
12
12
  }
@@ -55,8 +55,9 @@ export class StdioServerManagerClient {
55
55
  this.messageBuffer = "";
56
56
  });
57
57
  }
58
+ async sendRequest(method,
58
59
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
59
- async sendRequest(method, params) {
60
+ params, timeoutMs = 60000) {
60
61
  return new Promise((resolve, reject) => {
61
62
  const id = Date.now();
62
63
  if (!this.serverProcess?.stdin) {
@@ -84,7 +85,7 @@ export class StdioServerManagerClient {
84
85
  this.messageHandlers.delete(id);
85
86
  reject(new Error(`Request timed out: ${method}`));
86
87
  }
87
- }, 60000);
88
+ }, timeoutMs);
88
89
  });
89
90
  }
90
91
  async stop() {
@@ -9,6 +9,12 @@ class CallToolObserver {
9
9
  }
10
10
  this.serverToolCalls.get(serverId).add(toolName);
11
11
  }
12
+ // Seed the observer with historical tool calls (for session resume)
13
+ seedHistory(history) {
14
+ history.forEach(({ server_id, tool_name }) => {
15
+ this.recordCall(server_id, tool_name);
16
+ });
17
+ }
12
18
  // Check if a server was called at all
13
19
  wasServerCalled(serverId) {
14
20
  return (this.serverToolCalls.has(serverId) &&
@@ -10,6 +10,15 @@ class InstallObserver {
10
10
  recordUninstall(serverId) {
11
11
  this.recordAction(serverId, "uninstall");
12
12
  }
13
+ // Seed the observer with historical install/uninstall actions (for session resume)
14
+ seedHistory(installs, uninstalls) {
15
+ installs.forEach(({ server_id }) => {
16
+ this.recordInstall(server_id);
17
+ });
18
+ uninstalls.forEach(({ server_id }) => {
19
+ this.recordUninstall(server_id);
20
+ });
21
+ }
13
22
  // Check if a server has been installed
14
23
  wasServerInstalled(serverId) {
15
24
  return (this.serverInstallActions.has(serverId) &&
@@ -101,6 +101,21 @@ class Registry {
101
101
  static getBundledDependencyPath(depName) {
102
102
  return this._bundledDependencies[depName];
103
103
  }
104
+ /**
105
+ * Set the server configuration (includes session resume history).
106
+ */
107
+ static setServerConfig(config) {
108
+ this._serverConfig = config;
109
+ }
110
+ /**
111
+ * Get the server configuration.
112
+ */
113
+ static getServerConfig() {
114
+ if (!this._serverConfig) {
115
+ throw new Error("ServerConfig not set in Registry");
116
+ }
117
+ return this._serverConfig;
118
+ }
104
119
  static reset() {
105
120
  this._clientContext = null;
106
121
  this._toolplexApiService = null;
@@ -120,6 +135,7 @@ class Registry {
120
135
  }
121
136
  this._policyEnforcer = null;
122
137
  this._bundledDependencies = {};
138
+ this._serverConfig = null;
123
139
  }
124
140
  }
125
141
  Registry._clientContext = null;
@@ -131,4 +147,5 @@ Registry._toolDefinitionsCache = null;
131
147
  Registry._serversCache = null;
132
148
  Registry._policyEnforcer = null;
133
149
  Registry._bundledDependencies = {};
150
+ Registry._serverConfig = null;
134
151
  export default Registry;
@@ -100,6 +100,21 @@ export async function handleInitialize(params) {
100
100
  promptsCache.init(processedPrompts);
101
101
  // Init PolicyEnforce after setting permissions and flags
102
102
  policyEnforcer.init(clientContext);
103
+ // Seed enforcement history if provided (for restored sessions)
104
+ const serverConfig = Registry.getServerConfig();
105
+ if (serverConfig.sessionResumeHistory) {
106
+ const { tool_calls, installs, uninstalls } = serverConfig.sessionResumeHistory;
107
+ await logger.info(`Seeding enforcement history from restored session - ` +
108
+ `${tool_calls.length} tool calls, ${installs.length} installs, ${uninstalls.length} uninstalls`);
109
+ const callToolObserver = policyEnforcer.getCallToolObserver();
110
+ callToolObserver.seedHistory(tool_calls);
111
+ const installObserver = policyEnforcer.getInstallObserver();
112
+ installObserver.seedHistory(installs, uninstalls);
113
+ await logger.info("Enforcement history seeded successfully");
114
+ }
115
+ else {
116
+ await logger.debug("No session resume history provided (new session)");
117
+ }
103
118
  const allSucceeded = serverManagerInitResults.succeeded;
104
119
  const allFailures = serverManagerInitResults.failures;
105
120
  // Initialize the serversCache with the succeeded servers
@@ -60,6 +60,7 @@ export const InstallParamsSchema = z.object({
60
60
  server_name: z.string(),
61
61
  description: z.string(),
62
62
  config: ServerConfigSchema,
63
+ timeout_ms: z.number().int().min(10000).max(300000).optional(),
63
64
  });
64
65
  // --------------------
65
66
  // ListToolsParams
@@ -55,8 +55,9 @@ export class StdioServerManagerClient {
55
55
  this.messageBuffer = "";
56
56
  });
57
57
  }
58
+ async sendRequest(method,
58
59
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
59
- async sendRequest(method, params) {
60
+ params, timeoutMs = 60000) {
60
61
  return new Promise((resolve, reject) => {
61
62
  const id = Date.now();
62
63
  if (!this.serverProcess?.stdin) {
@@ -84,7 +85,7 @@ export class StdioServerManagerClient {
84
85
  this.messageHandlers.delete(id);
85
86
  reject(new Error(`Request timed out: ${method}`));
86
87
  }
87
- }, 60000);
88
+ }, timeoutMs);
88
89
  });
89
90
  }
90
91
  async stop() {
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const version = "0.1.26";
1
+ export declare const version = "0.1.28";
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const version = '0.1.26';
1
+ export const version = '0.1.28';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@toolplex/client",
3
- "version": "0.1.26",
3
+ "version": "0.1.28",
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",