@toolplex/client 0.1.17 → 0.1.19

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.
Files changed (39) hide show
  1. package/dist/mcp-server/index.js +10 -0
  2. package/dist/mcp-server/registry.d.ts +17 -0
  3. package/dist/mcp-server/registry.js +23 -0
  4. package/dist/mcp-server/toolHandlers/initHandler.d.ts +9 -0
  5. package/dist/mcp-server/toolHandlers/initHandler.js +46 -1
  6. package/dist/mcp-server/toolHandlers/installServerHandler.js +27 -40
  7. package/dist/mcp-server/toolplexServer.js +5 -0
  8. package/dist/mcp-server/utils/runtimeCheck.d.ts +18 -0
  9. package/dist/mcp-server/utils/runtimeCheck.js +70 -6
  10. package/dist/server-manager/serverManager.js +3 -9
  11. package/dist/shared/enhancedPath.js +3 -3
  12. package/dist/shared/mcpServerTypes.d.ts +12 -0
  13. package/dist/src/mcp-server/clientContext.js +118 -0
  14. package/dist/src/mcp-server/logging/telemetryLogger.js +54 -0
  15. package/dist/src/mcp-server/policy/callToolObserver.js +27 -0
  16. package/dist/src/mcp-server/policy/feedbackPolicy.js +39 -0
  17. package/dist/src/mcp-server/policy/installObserver.js +35 -0
  18. package/dist/src/mcp-server/policy/playbookPolicy.js +81 -0
  19. package/dist/src/mcp-server/policy/policyEnforcer.js +105 -0
  20. package/dist/src/mcp-server/policy/serverPolicy.js +63 -0
  21. package/dist/src/mcp-server/promptsCache.js +51 -0
  22. package/dist/src/mcp-server/registry.js +134 -0
  23. package/dist/src/mcp-server/serversCache.js +100 -0
  24. package/dist/src/mcp-server/toolDefinitionsCache.js +67 -0
  25. package/dist/src/mcp-server/toolHandlers/initHandler.js +185 -0
  26. package/dist/src/mcp-server/toolplexApi/service.js +221 -0
  27. package/dist/src/mcp-server/toolplexApi/types.js +1 -0
  28. package/dist/src/mcp-server/utils/initServerManagers.js +31 -0
  29. package/dist/src/mcp-server/utils/runtimeCheck.js +94 -0
  30. package/dist/src/shared/enhancedPath.js +52 -0
  31. package/dist/src/shared/fileLogger.js +66 -0
  32. package/dist/src/shared/mcpServerTypes.js +158 -0
  33. package/dist/src/shared/serverManagerTypes.js +73 -0
  34. package/dist/src/shared/stdioServerManagerClient.js +98 -0
  35. package/dist/tests/unit/bundledDependencies.test.js +152 -0
  36. package/dist/tests/unit/registry.test.js +216 -0
  37. package/dist/version.d.ts +1 -1
  38. package/dist/version.js +1 -1
  39. package/package.json +8 -3
@@ -9,6 +9,15 @@ const apiKey = process.env.TOOLPLEX_API_KEY;
9
9
  const clientMode = process.env.TOOLPLEX_CLIENT_MODE || "standard";
10
10
  const clientName = process.env.CLIENT_NAME || "unknown";
11
11
  const logLevel = process.env.LOG_LEVEL || "info";
12
+ // Read bundled dependency paths from environment variables
13
+ // These are provided by the host application (e.g., Electron desktop)
14
+ const bundledDependencies = {
15
+ node: process.env.TOOLPLEX_NODE_PATH,
16
+ python: process.env.TOOLPLEX_PYTHON_PATH,
17
+ git: process.env.TOOLPLEX_GIT_PATH,
18
+ uvx: process.env.TOOLPLEX_UVX_PATH,
19
+ npx: process.env.TOOLPLEX_NPX_PATH,
20
+ };
12
21
  if (!apiKey) {
13
22
  process.exit(1);
14
23
  }
@@ -18,6 +27,7 @@ const config = {
18
27
  clientMode,
19
28
  clientName,
20
29
  logLevel,
30
+ bundledDependencies,
21
31
  };
22
32
  serve(config).catch(() => {
23
33
  process.exit(1);
@@ -6,6 +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
10
  /**
10
11
  * In-memory global registry for the ToolPlex client.
11
12
  * Maintains singleton instances of core services and clients used throughout the application.
@@ -19,6 +20,7 @@ declare class Registry {
19
20
  private static _toolDefinitionsCache;
20
21
  private static _serversCache;
21
22
  private static _policyEnforcer;
23
+ private static _bundledDependencies;
22
24
  static init(clientContext: ClientContext): Promise<void>;
23
25
  static getClientContext(): ClientContext;
24
26
  static getToolplexApiService(): ToolplexApiService;
@@ -29,6 +31,21 @@ declare class Registry {
29
31
  static getToolDefinitionsCache(): ToolDefinitionsCache;
30
32
  static getServersCache(): ServersCache;
31
33
  static getPolicyEnforcer(): PolicyEnforcer;
34
+ /**
35
+ * Set bundled dependencies (paths to Node, Python, Git, etc.)
36
+ * provided by the host application (e.g., Electron desktop app).
37
+ */
38
+ static setBundledDependencies(deps: BundledDependencies): void;
39
+ /**
40
+ * Get bundled dependencies (paths to required executables).
41
+ * Returns a copy of the bundled dependencies object.
42
+ */
43
+ static getBundledDependencies(): BundledDependencies;
44
+ /**
45
+ * Get the path for a specific bundled dependency by name.
46
+ * Returns undefined if the dependency is not available.
47
+ */
48
+ static getBundledDependencyPath(depName: "node" | "python" | "git" | "uvx" | "npx"): string | undefined;
32
49
  static reset(): void;
33
50
  }
34
51
  export default Registry;
@@ -80,6 +80,27 @@ class Registry {
80
80
  }
81
81
  return this._policyEnforcer;
82
82
  }
83
+ /**
84
+ * Set bundled dependencies (paths to Node, Python, Git, etc.)
85
+ * provided by the host application (e.g., Electron desktop app).
86
+ */
87
+ static setBundledDependencies(deps) {
88
+ this._bundledDependencies = deps;
89
+ }
90
+ /**
91
+ * Get bundled dependencies (paths to required executables).
92
+ * Returns a copy of the bundled dependencies object.
93
+ */
94
+ static getBundledDependencies() {
95
+ return { ...this._bundledDependencies };
96
+ }
97
+ /**
98
+ * Get the path for a specific bundled dependency by name.
99
+ * Returns undefined if the dependency is not available.
100
+ */
101
+ static getBundledDependencyPath(depName) {
102
+ return this._bundledDependencies[depName];
103
+ }
83
104
  static reset() {
84
105
  this._clientContext = null;
85
106
  this._toolplexApiService = null;
@@ -98,6 +119,7 @@ class Registry {
98
119
  this._serversCache = null;
99
120
  }
100
121
  this._policyEnforcer = null;
122
+ this._bundledDependencies = {};
101
123
  }
102
124
  }
103
125
  Registry._clientContext = null;
@@ -108,4 +130,5 @@ Registry._promptsCache = null;
108
130
  Registry._toolDefinitionsCache = null;
109
131
  Registry._serversCache = null;
110
132
  Registry._policyEnforcer = null;
133
+ Registry._bundledDependencies = {};
111
134
  export default Registry;
@@ -1,3 +1,12 @@
1
1
  import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
2
2
  import { InitializeToolplexParams } from "../../shared/mcpServerTypes.js";
3
+ /**
4
+ * Helper to resolve dependency path with fallback:
5
+ * 1. Bundled dependency (if available)
6
+ * 2. System PATH (fallback)
7
+ * 3. "not available" if neither exists
8
+ *
9
+ * Exported for testing purposes.
10
+ */
11
+ export declare function resolveDependencyForInit(bundledPath: string | undefined, commandName: string): string;
3
12
  export declare function handleInitialize(params: InitializeToolplexParams): Promise<CallToolResult>;
@@ -3,7 +3,39 @@ import os from "os";
3
3
  import { initServerManagersOnly } from "../utils/initServerManagers.js";
4
4
  import Registry from "../registry.js";
5
5
  import envPaths from "env-paths";
6
+ import which from "which";
7
+ import { getEnhancedPath } from "../../shared/enhancedPath.js";
8
+ import * as fs from "fs";
6
9
  const logger = FileLogger;
10
+ /**
11
+ * Helper to resolve dependency path with fallback:
12
+ * 1. Bundled dependency (if available)
13
+ * 2. System PATH (fallback)
14
+ * 3. "not available" if neither exists
15
+ *
16
+ * Exported for testing purposes.
17
+ */
18
+ export function resolveDependencyForInit(bundledPath, commandName) {
19
+ // Check bundled first
20
+ if (bundledPath && fs.existsSync(bundledPath)) {
21
+ return bundledPath;
22
+ }
23
+ // Fall back to system PATH
24
+ try {
25
+ const enhancedPath = getEnhancedPath();
26
+ const systemPath = which.sync(commandName, {
27
+ path: enhancedPath,
28
+ nothrow: true,
29
+ });
30
+ if (systemPath) {
31
+ return `${systemPath} (system)`;
32
+ }
33
+ }
34
+ catch {
35
+ // Ignore errors, will return "not available"
36
+ }
37
+ return "not available";
38
+ }
7
39
  export async function handleInitialize(params) {
8
40
  const startTime = Date.now();
9
41
  await logger.info("Initializing ToolPlex");
@@ -23,6 +55,8 @@ export async function handleInitialize(params) {
23
55
  ? "Windows"
24
56
  : platform.charAt(0).toUpperCase() + platform.slice(1);
25
57
  const paths = envPaths("ToolPlex", { suffix: "" });
58
+ // Get bundled dependency information
59
+ const bundledDeps = Registry.getBundledDependencies();
26
60
  const systemInfo = {
27
61
  os: `${osName} ${os.release()}`,
28
62
  arch: os.arch(),
@@ -35,6 +69,12 @@ export async function handleInitialize(params) {
35
69
  month: "long",
36
70
  day: "numeric",
37
71
  }),
72
+ // Resolve dependency paths with bundled > system > not available priority
73
+ nodePath: resolveDependencyForInit(bundledDeps.node, "node"),
74
+ pythonPath: resolveDependencyForInit(bundledDeps.python, platform === "win32" ? "python" : "python3"),
75
+ gitPath: resolveDependencyForInit(bundledDeps.git, "git"),
76
+ uvxPath: resolveDependencyForInit(bundledDeps.uvx, "uvx"),
77
+ npxPath: resolveDependencyForInit(bundledDeps.npx, "npx"),
38
78
  };
39
79
  await logger.debug("Initializing server managers and API service");
40
80
  const [serverManagerInitResults, toolplexApiInitResponse] = await Promise.all([
@@ -76,7 +116,12 @@ export async function handleInitialize(params) {
76
116
  .replace("{ARGS.memory}", systemInfo.memory)
77
117
  .replace("{ARGS.cpuCores}", systemInfo.cpuCores.toString())
78
118
  .replace("{ARGS.workDir}", systemInfo.workDir)
79
- .replace("{ARGS.date}", systemInfo.date),
119
+ .replace("{ARGS.date}", systemInfo.date)
120
+ .replace("{ARGS.nodePath}", systemInfo.nodePath)
121
+ .replace("{ARGS.pythonPath}", systemInfo.pythonPath)
122
+ .replace("{ARGS.gitPath}", systemInfo.gitPath)
123
+ .replace("{ARGS.uvxPath}", systemInfo.uvxPath)
124
+ .replace("{ARGS.npxPath}", systemInfo.npxPath),
80
125
  },
81
126
  ],
82
127
  };
@@ -145,15 +145,7 @@ export async function handleInstallServer(params) {
145
145
  if (!parsed.success) {
146
146
  throw new Error(`Invalid list_tools response: ${JSON.stringify(parsed.error.errors)}, response was: ${JSON.stringify(toolsResponse)}`);
147
147
  }
148
- let toolsText = "";
149
148
  const tools = parsed.data.tools || [];
150
- if (tools.length > 0) {
151
- toolsText = "Available tools from this server:\n\n";
152
- for (const tool of tools) {
153
- toolsText += `- ${tool.name}: ${tool.description}\n`;
154
- toolsText += ` Input Schema: ${JSON.stringify(tool.inputSchema, null, 2)}\n\n`;
155
- }
156
- }
157
149
  await telemetryLogger.log("client_install", {
158
150
  success: true,
159
151
  log_context: {
@@ -162,24 +154,22 @@ export async function handleInstallServer(params) {
162
154
  },
163
155
  latency_ms: Date.now() - startTime,
164
156
  });
165
- const content = [
166
- {
167
- type: "text",
168
- text: promptsCache
169
- .getPrompt("install_success")
170
- .replace("{SERVER_ID}", installResult.server_id)
171
- .replace("{SERVER_NAME}", installResult.server_name)
172
- .replace("{TOOLS_LIST}", toolsText),
173
- },
174
- ];
175
- if (!clientContext.permissions.enable_read_only_mode) {
176
- content.push({
177
- type: "text",
178
- text: promptsCache.getPrompt("install_next_steps"),
179
- });
180
- }
157
+ // Return structured JSON for clean UI rendering
158
+ const response = {
159
+ success: true,
160
+ server_id: installResult.server_id,
161
+ server_name: installResult.server_name,
162
+ message: `Successfully installed server ${installResult.server_id} (${installResult.server_name})`,
163
+ tools: tools,
164
+ tool_count: tools.length,
165
+ };
181
166
  return {
182
- content,
167
+ content: [
168
+ {
169
+ type: "text",
170
+ text: JSON.stringify(response, null, 2),
171
+ },
172
+ ],
183
173
  };
184
174
  }
185
175
  catch (error) {
@@ -196,23 +186,20 @@ export async function handleInstallServer(params) {
196
186
  pii_sanitized_error_message: errorMessage,
197
187
  latency_ms: Date.now() - startTime,
198
188
  });
199
- const content = [
200
- {
201
- type: "text",
202
- text: promptsCache
203
- .getPrompt("install_failure")
204
- .replace("{ERROR}", errorMessage),
205
- },
206
- ];
207
- if (!clientContext.permissions.enable_read_only_mode) {
208
- content.push({
209
- type: "text",
210
- text: promptsCache.getPrompt("install_next_steps"),
211
- });
212
- }
189
+ // Return structured error JSON
190
+ const errorResponse = {
191
+ success: false,
192
+ error: errorMessage,
193
+ server_id: params.server_id,
194
+ };
213
195
  return {
214
196
  isError: true,
215
- content,
197
+ content: [
198
+ {
199
+ type: "text",
200
+ text: JSON.stringify(errorResponse, null, 2),
201
+ },
202
+ ],
216
203
  };
217
204
  }
218
205
  finally {
@@ -34,6 +34,11 @@ export async function serve(config) {
34
34
  clientContext.clientName = config.clientName;
35
35
  clientContext.clientVersion = clientVersion;
36
36
  await Registry.init(clientContext);
37
+ // Store bundled dependencies in Registry for use throughout the application
38
+ if (config.bundledDependencies) {
39
+ Registry.setBundledDependencies(config.bundledDependencies);
40
+ await logger.debug(`Bundled dependencies registered: ${JSON.stringify(config.bundledDependencies)}`);
41
+ }
37
42
  await logger.info(`Starting Toolplex server in ${config.dev ? "development" : "production"} mode`);
38
43
  const server = new Server({
39
44
  name: "toolplex-server",
@@ -1,4 +1,22 @@
1
1
  export declare class RuntimeCheck {
2
+ /**
3
+ * Resolve a dependency path with priority order:
4
+ * 1. Bundled dependencies (if provided by host application like ToolPlex Desktop)
5
+ * 2. System PATH (fallback for standalone @client usage)
6
+ * 3. Error if neither available
7
+ *
8
+ * This allows ToolPlex Desktop to provide reliable bundled dependencies while
9
+ * still supporting standalone users who have system dependencies installed.
10
+ *
11
+ * @param commandName - The command to resolve
12
+ * @returns The full path to the command executable
13
+ * @throws Error if the command is not available in bundled deps or system PATH
14
+ */
15
+ static resolveDependency(commandName: string): string;
16
+ /**
17
+ * Validate that a command is available (either bundled or in system PATH).
18
+ * Throws an error if the command is not found.
19
+ */
2
20
  static validateCommandOrThrow(rawCommand: string): void;
3
21
  static extractCommandName(command: string): string;
4
22
  }
@@ -1,5 +1,7 @@
1
1
  import { getEnhancedPath } from "../../shared/enhancedPath.js";
2
2
  import which from "which";
3
+ import Registry from "../registry.js";
4
+ import * as fs from "fs";
3
5
  const INSTALL_HINTS = {
4
6
  uvx: "Install uvx: https://docs.astral.sh/uv/getting-started/installation/",
5
7
  uv: "Install uv: https://docs.astral.sh/uv/getting-started/installation/",
@@ -7,22 +9,84 @@ const INSTALL_HINTS = {
7
9
  python3: "Install Python: https://www.python.org/downloads/. Or check if you have `python` installed.",
8
10
  node: "Install Node.js: https://nodejs.org/en/download/",
9
11
  npx: "Install npx (comes with Node.js): https://nodejs.org/en/download/",
12
+ git: "Install Git: https://git-scm.com/downloads",
10
13
  };
14
+ // Commands that should use bundled dependencies (required)
15
+ const BUNDLED_DEPENDENCY_COMMANDS = [
16
+ "node",
17
+ "python",
18
+ "python3",
19
+ "git",
20
+ "npx",
21
+ "uvx",
22
+ ];
11
23
  export class RuntimeCheck {
12
- static validateCommandOrThrow(rawCommand) {
13
- const command = this.extractCommandName(rawCommand);
24
+ /**
25
+ * Resolve a dependency path with priority order:
26
+ * 1. Bundled dependencies (if provided by host application like ToolPlex Desktop)
27
+ * 2. System PATH (fallback for standalone @client usage)
28
+ * 3. Error if neither available
29
+ *
30
+ * This allows ToolPlex Desktop to provide reliable bundled dependencies while
31
+ * still supporting standalone users who have system dependencies installed.
32
+ *
33
+ * @param commandName - The command to resolve
34
+ * @returns The full path to the command executable
35
+ * @throws Error if the command is not available in bundled deps or system PATH
36
+ */
37
+ static resolveDependency(commandName) {
38
+ // Check if this is a known bundled dependency type
39
+ const isBundledDep = BUNDLED_DEPENDENCY_COMMANDS.includes(commandName);
40
+ if (isBundledDep) {
41
+ // Priority 1: Try bundled dependency first (preferred for ToolPlex Desktop)
42
+ const bundledPath = Registry.getBundledDependencyPath(commandName);
43
+ if (bundledPath && fs.existsSync(bundledPath)) {
44
+ return bundledPath;
45
+ }
46
+ // Handle python3 -> python mapping for bundled deps
47
+ if (commandName === "python3") {
48
+ const pythonPath = Registry.getBundledDependencyPath("python");
49
+ if (pythonPath && fs.existsSync(pythonPath)) {
50
+ return pythonPath;
51
+ }
52
+ }
53
+ // Priority 2: Fall back to system PATH (for standalone @client usage)
54
+ const enhancedPath = getEnhancedPath();
55
+ const resolved = which.sync(commandName, {
56
+ path: enhancedPath,
57
+ nothrow: true,
58
+ });
59
+ if (resolved) {
60
+ return resolved;
61
+ }
62
+ // Priority 3: Neither bundled nor system available - error
63
+ const hint = INSTALL_HINTS[commandName];
64
+ throw new Error(`Missing required command: '${commandName}'.\n` +
65
+ `This command is not available in bundled dependencies or system PATH.\n` +
66
+ (hint ? `👉 ${hint}` : ""));
67
+ }
68
+ // For non-bundled dependencies, only check system PATH
14
69
  const enhancedPath = getEnhancedPath();
15
- const resolved = which.sync(command, {
70
+ const resolved = which.sync(commandName, {
16
71
  path: enhancedPath,
17
72
  nothrow: true,
18
73
  });
19
74
  if (!resolved) {
20
- const hint = INSTALL_HINTS[command];
75
+ const hint = INSTALL_HINTS[commandName];
21
76
  if (hint) {
22
- throw new Error(`Missing required command: '${command}'.\n👉 ${hint}`);
77
+ throw new Error(`Missing required command: '${commandName}'.\n👉 ${hint}`);
23
78
  }
24
- throw new Error(`Command '${command}' not found in enhanced PATH. Please install it manually or check your config.`);
79
+ throw new Error(`Command '${commandName}' not found in enhanced PATH. Please install it manually or check your config.`);
25
80
  }
81
+ return resolved;
82
+ }
83
+ /**
84
+ * Validate that a command is available (either bundled or in system PATH).
85
+ * Throws an error if the command is not found.
86
+ */
87
+ static validateCommandOrThrow(rawCommand) {
88
+ const command = this.extractCommandName(rawCommand);
89
+ this.resolveDependency(command);
26
90
  }
27
91
  static extractCommandName(command) {
28
92
  return command.trim().split(/\s+/)[0];
@@ -3,7 +3,6 @@ import { StdioClientTransport, } from "@modelcontextprotocol/sdk/client/stdio.js
3
3
  import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
4
4
  import * as fs from "fs/promises";
5
5
  import * as path from "path";
6
- import which from "which";
7
6
  import { FileLogger } from "../shared/fileLogger.js";
8
7
  import envPaths from "env-paths";
9
8
  import { getEnhancedPath } from "../shared/enhancedPath.js";
@@ -204,15 +203,10 @@ export class ServerManager {
204
203
  else if (config.transport === "stdio") {
205
204
  if (!config.command)
206
205
  throw new Error("Command is required for stdio transport");
206
+ // Use RuntimeCheck to resolve the command, which prioritizes bundled dependencies
207
+ const { RuntimeCheck } = await import("../mcp-server/utils/runtimeCheck.js");
208
+ const resolvedCommand = RuntimeCheck.resolveDependency(config.command);
207
209
  const enhancedPath = getEnhancedPath();
208
- let resolvedCommand = which.sync(config.command, {
209
- path: enhancedPath,
210
- nothrow: true,
211
- });
212
- if (!resolvedCommand) {
213
- // Fallback to supplied command
214
- resolvedCommand = config.command;
215
- }
216
210
  const serverParams = {
217
211
  command: resolvedCommand,
218
212
  args: config.args || [],
@@ -1,5 +1,5 @@
1
1
  import * as path from "path";
2
- import { existsSync } from "fs";
2
+ import * as fs from "fs";
3
3
  import { homedir } from "os";
4
4
  import { glob } from "glob";
5
5
  /**
@@ -18,14 +18,14 @@ export function getEnhancedPath() {
18
18
  if (extraPath.includes("*")) {
19
19
  const matches = glob.sync(extraPath);
20
20
  for (const match of matches) {
21
- if (existsSync(match) && !seen.has(match)) {
21
+ if (fs.existsSync(match) && !seen.has(match)) {
22
22
  seen.add(match);
23
23
  allPaths.unshift(match);
24
24
  }
25
25
  }
26
26
  }
27
27
  else {
28
- if (existsSync(extraPath) && !seen.has(extraPath)) {
28
+ if (fs.existsSync(extraPath) && !seen.has(extraPath)) {
29
29
  seen.add(extraPath);
30
30
  allPaths.unshift(extraPath);
31
31
  }
@@ -1,12 +1,24 @@
1
1
  import { z } from "zod";
2
2
  export type ClientMode = "standard" | "restricted";
3
3
  export type LogLevel = "error" | "warn" | "info" | "debug";
4
+ /**
5
+ * Paths to bundled dependencies provided by the host application (e.g., Electron).
6
+ * These dependencies are required for MCP server installations and execution.
7
+ */
8
+ export interface BundledDependencies {
9
+ node?: string;
10
+ python?: string;
11
+ git?: string;
12
+ uvx?: string;
13
+ npx?: string;
14
+ }
4
15
  export interface ToolplexServerConfig {
5
16
  dev: boolean;
6
17
  apiKey: string;
7
18
  clientMode: ClientMode;
8
19
  clientName: string;
9
20
  logLevel: LogLevel;
21
+ bundledDependencies?: BundledDependencies;
10
22
  }
11
23
  export declare const TransportTypeSchema: z.ZodEnum<["stdio", "sse"]>;
12
24
  export type TransportType = z.infer<typeof TransportTypeSchema>;
@@ -0,0 +1,118 @@
1
+ /**
2
+ * Maintains client context for the ToolPlex server
3
+ */
4
+ export class ClientContext {
5
+ constructor() {
6
+ this._sessionId = null;
7
+ this._dev = null;
8
+ this._apiKey = null;
9
+ this._clientMode = null;
10
+ this._llmContext = null;
11
+ this._clientVersion = null;
12
+ this._permissions = null;
13
+ this._flags = null;
14
+ this._isOrgUser = null;
15
+ this._clientName = null;
16
+ }
17
+ get sessionId() {
18
+ if (!this._sessionId) {
19
+ throw new Error("Session ID not set - ToolPlex not initialized");
20
+ }
21
+ return this._sessionId;
22
+ }
23
+ set sessionId(id) {
24
+ this._sessionId = id;
25
+ }
26
+ get dev() {
27
+ if (this._dev === null) {
28
+ throw new Error("Dev mode not set - ToolPlex not initialized");
29
+ }
30
+ return this._dev;
31
+ }
32
+ set dev(isDev) {
33
+ this._dev = isDev;
34
+ }
35
+ get apiKey() {
36
+ if (!this._apiKey) {
37
+ throw new Error("API key not set - ToolPlex not initialized");
38
+ }
39
+ return this._apiKey;
40
+ }
41
+ set apiKey(key) {
42
+ this._apiKey = key;
43
+ }
44
+ get clientMode() {
45
+ if (!this._clientMode) {
46
+ throw new Error("Client mode not set - ToolPlex not initialized");
47
+ }
48
+ return this._clientMode;
49
+ }
50
+ set clientMode(mode) {
51
+ this._clientMode = mode;
52
+ }
53
+ get clientName() {
54
+ if (!this._clientName) {
55
+ throw new Error("Client name not set - ToolPlex not initialized");
56
+ }
57
+ return this._clientName;
58
+ }
59
+ set clientName(name) {
60
+ this._clientName = name;
61
+ }
62
+ get llmContext() {
63
+ if (!this._llmContext) {
64
+ throw new Error("LLM context not set - ToolPlex not initialized");
65
+ }
66
+ return this._llmContext;
67
+ }
68
+ set llmContext(context) {
69
+ this._llmContext = context;
70
+ }
71
+ get clientVersion() {
72
+ if (!this._clientVersion) {
73
+ throw new Error("Client version not set - ToolPlex not initialized");
74
+ }
75
+ return this._clientVersion;
76
+ }
77
+ set clientVersion(version) {
78
+ this._clientVersion = version;
79
+ }
80
+ get permissions() {
81
+ if (!this._permissions) {
82
+ throw new Error("Permissions not set - ToolPlex not initialized");
83
+ }
84
+ return this._permissions;
85
+ }
86
+ set permissions(perms) {
87
+ this._permissions = perms;
88
+ }
89
+ get flags() {
90
+ if (!this._flags) {
91
+ throw new Error("Consts not set - ToolPlex not initialized");
92
+ }
93
+ return this._flags;
94
+ }
95
+ set flags(consts) {
96
+ this._flags = consts;
97
+ }
98
+ get isOrgUser() {
99
+ if (this._isOrgUser === null) {
100
+ throw new Error("Organization user status not set - ToolPlex not initialized");
101
+ }
102
+ return this._isOrgUser;
103
+ }
104
+ set isOrgUser(isOrgUser) {
105
+ this._isOrgUser = isOrgUser;
106
+ }
107
+ isInitialized() {
108
+ return !!(this._sessionId &&
109
+ this._apiKey &&
110
+ this._clientMode &&
111
+ this._llmContext &&
112
+ this._clientVersion &&
113
+ this._permissions &&
114
+ this._flags &&
115
+ this._isOrgUser !== null &&
116
+ this._clientName);
117
+ }
118
+ }
@@ -0,0 +1,54 @@
1
+ import Registry from "../registry.js";
2
+ import { FileLogger } from "../../shared/fileLogger.js";
3
+ const logger = FileLogger;
4
+ export class TelemetryLogger {
5
+ constructor() {
6
+ this.eventQueue = [];
7
+ this.flushTimeout = null;
8
+ this.BATCH_SIZE = 10;
9
+ this.FLUSH_INTERVAL = 30000;
10
+ this.scheduleFlush();
11
+ }
12
+ /**
13
+ * Log a telemetry event
14
+ */
15
+ async log(eventType, data) {
16
+ this.eventQueue.push({ eventType, data });
17
+ if (this.eventQueue.length >= this.BATCH_SIZE) {
18
+ await this.flush();
19
+ }
20
+ }
21
+ async flush() {
22
+ if (this.eventQueue.length === 0)
23
+ return;
24
+ try {
25
+ const apiService = Registry.getToolplexApiService();
26
+ const events = [...this.eventQueue];
27
+ this.eventQueue = [];
28
+ await logger.debug(`Flushing ${events.length} telemetry events`);
29
+ await apiService.logTelemetryEvents(events);
30
+ }
31
+ catch (err) {
32
+ await logger.error(`Error flushing telemetry events: ${err}`);
33
+ this.eventQueue.unshift(...this.eventQueue);
34
+ }
35
+ }
36
+ scheduleFlush() {
37
+ if (this.flushTimeout) {
38
+ clearTimeout(this.flushTimeout);
39
+ }
40
+ this.flushTimeout = setTimeout(async () => {
41
+ await this.flush();
42
+ this.scheduleFlush();
43
+ }, this.FLUSH_INTERVAL);
44
+ }
45
+ /**
46
+ * Clean up resources - should be called when shutting down
47
+ */
48
+ async dispose() {
49
+ if (this.flushTimeout) {
50
+ clearTimeout(this.flushTimeout);
51
+ }
52
+ await this.flush();
53
+ }
54
+ }