@toolplex/client 0.1.29 → 0.1.31
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/dist/mcp-server/index.js +1 -0
- package/dist/mcp-server/registry.d.ts +1 -1
- package/dist/mcp-server/toolplexServer.js +34 -1
- package/dist/mcp-server/utils/runtimeCheck.d.ts +20 -2
- package/dist/mcp-server/utils/runtimeCheck.js +53 -20
- package/dist/server-manager/index.js +28 -0
- package/dist/server-manager/serverManager.js +54 -9
- package/dist/shared/mcpServerTypes.d.ts +5 -0
- package/dist/src/mcp-server/utils/runtimeCheck.js +53 -20
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +2 -2
package/dist/mcp-server/index.js
CHANGED
|
@@ -13,6 +13,7 @@ const logLevel = process.env.LOG_LEVEL || "info";
|
|
|
13
13
|
// These are provided by the host application (e.g., Electron desktop)
|
|
14
14
|
const bundledDependencies = {
|
|
15
15
|
node: process.env.TOOLPLEX_NODE_PATH,
|
|
16
|
+
npm: process.env.TOOLPLEX_NPM_PATH,
|
|
16
17
|
npx: process.env.TOOLPLEX_NPX_PATH,
|
|
17
18
|
python: process.env.TOOLPLEX_PYTHON_PATH,
|
|
18
19
|
pip: process.env.TOOLPLEX_PIP_PATH,
|
|
@@ -46,7 +46,7 @@ declare class Registry {
|
|
|
46
46
|
* Get the path for a specific bundled dependency by name.
|
|
47
47
|
* Returns undefined if the dependency is not available.
|
|
48
48
|
*/
|
|
49
|
-
static getBundledDependencyPath(depName: "node" | "python" | "git" | "uvx" | "npx"): string | undefined;
|
|
49
|
+
static getBundledDependencyPath(depName: "node" | "npm" | "python" | "git" | "uvx" | "npx"): string | undefined;
|
|
50
50
|
/**
|
|
51
51
|
* Set the server configuration (includes session resume history).
|
|
52
52
|
*/
|
|
@@ -52,9 +52,42 @@ export async function serve(config) {
|
|
|
52
52
|
},
|
|
53
53
|
});
|
|
54
54
|
// Initialize server manager clients
|
|
55
|
+
// Use bundled node if available, otherwise fall back to system "node"
|
|
56
|
+
// Pass bundled dependency paths so the server-manager can initialize its Registry
|
|
55
57
|
await logger.info("Initializing server manager clients");
|
|
58
|
+
const nodeCommand = config.bundledDependencies?.node || "node";
|
|
56
59
|
const serverManagerClients = {
|
|
57
|
-
node: new StdioServerManagerClient(
|
|
60
|
+
node: new StdioServerManagerClient(nodeCommand, [path.join(__dirname, "..", "server-manager", "index.js")], {
|
|
61
|
+
LOG_LEVEL: config.logLevel,
|
|
62
|
+
// Pass the current PATH which includes bundled bin directories
|
|
63
|
+
PATH: process.env.PATH,
|
|
64
|
+
// Pass all bundled dependency paths to server-manager
|
|
65
|
+
// These are critical for proper command resolution (e.g., npx -> node + npx-cli.js)
|
|
66
|
+
...(config.bundledDependencies?.node && {
|
|
67
|
+
TOOLPLEX_NODE_PATH: config.bundledDependencies.node,
|
|
68
|
+
}),
|
|
69
|
+
...(config.bundledDependencies?.npm && {
|
|
70
|
+
TOOLPLEX_NPM_PATH: config.bundledDependencies.npm,
|
|
71
|
+
}),
|
|
72
|
+
...(config.bundledDependencies?.npx && {
|
|
73
|
+
TOOLPLEX_NPX_PATH: config.bundledDependencies.npx,
|
|
74
|
+
}),
|
|
75
|
+
...(config.bundledDependencies?.python && {
|
|
76
|
+
TOOLPLEX_PYTHON_PATH: config.bundledDependencies.python,
|
|
77
|
+
}),
|
|
78
|
+
...(config.bundledDependencies?.pip && {
|
|
79
|
+
TOOLPLEX_PIP_PATH: config.bundledDependencies.pip,
|
|
80
|
+
}),
|
|
81
|
+
...(config.bundledDependencies?.uv && {
|
|
82
|
+
TOOLPLEX_UV_PATH: config.bundledDependencies.uv,
|
|
83
|
+
}),
|
|
84
|
+
...(config.bundledDependencies?.uvx && {
|
|
85
|
+
TOOLPLEX_UVX_PATH: config.bundledDependencies.uvx,
|
|
86
|
+
}),
|
|
87
|
+
...(config.bundledDependencies?.git && {
|
|
88
|
+
TOOLPLEX_GIT_PATH: config.bundledDependencies.git,
|
|
89
|
+
}),
|
|
90
|
+
}),
|
|
58
91
|
};
|
|
59
92
|
// Start all server manager clients
|
|
60
93
|
await logger.info("Starting server manager clients");
|
|
@@ -1,12 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Result of resolving a command, which may need to be invoked via node
|
|
3
|
+
* if it's a .js script (e.g., bundled npm/npx on Unix).
|
|
4
|
+
*/
|
|
5
|
+
export interface ResolvedCommand {
|
|
6
|
+
command: string;
|
|
7
|
+
prependArgs: string[];
|
|
8
|
+
}
|
|
1
9
|
export declare class RuntimeCheck {
|
|
10
|
+
/**
|
|
11
|
+
* Resolve a dependency and return both the command and any prepended args needed.
|
|
12
|
+
* This handles the case where bundled npm/npx on Unix are .js scripts that
|
|
13
|
+
* need to be invoked via node.
|
|
14
|
+
*
|
|
15
|
+
* @param commandName - The command to resolve (e.g., "npx", "node", "uvx")
|
|
16
|
+
* @returns ResolvedCommand with command and prependArgs
|
|
17
|
+
* @throws Error if the command is not available
|
|
18
|
+
*/
|
|
19
|
+
static resolveCommandWithArgs(commandName: string): ResolvedCommand;
|
|
2
20
|
/**
|
|
3
21
|
* Resolve a dependency path with priority order:
|
|
4
22
|
* 1. Bundled dependencies (if provided by host application like ToolPlex Desktop)
|
|
5
23
|
* 2. System PATH (fallback for standalone @client usage)
|
|
6
24
|
* 3. Error if neither available
|
|
7
25
|
*
|
|
8
|
-
* This
|
|
9
|
-
*
|
|
26
|
+
* NOTE: This returns just the path. For npm/npx which may be .js files on Unix,
|
|
27
|
+
* use resolveCommandWithArgs() instead to get the proper invocation.
|
|
10
28
|
*
|
|
11
29
|
* @param commandName - The command to resolve
|
|
12
30
|
* @returns The full path to the command executable
|
|
@@ -8,12 +8,14 @@ const INSTALL_HINTS = {
|
|
|
8
8
|
python: "Install Python: https://www.python.org/downloads/. Or check if you have `python3` installed.",
|
|
9
9
|
python3: "Install Python: https://www.python.org/downloads/. Or check if you have `python` installed.",
|
|
10
10
|
node: "Install Node.js: https://nodejs.org/en/download/",
|
|
11
|
+
npm: "Install npm (comes with Node.js): https://nodejs.org/en/download/",
|
|
11
12
|
npx: "Install npx (comes with Node.js): https://nodejs.org/en/download/",
|
|
12
13
|
git: "Install Git: https://git-scm.com/downloads",
|
|
13
14
|
};
|
|
14
15
|
// Commands that should use bundled dependencies (required)
|
|
15
16
|
const BUNDLED_DEPENDENCY_COMMANDS = [
|
|
16
17
|
"node",
|
|
18
|
+
"npm",
|
|
17
19
|
"python",
|
|
18
20
|
"python3",
|
|
19
21
|
"git",
|
|
@@ -22,44 +24,54 @@ const BUNDLED_DEPENDENCY_COMMANDS = [
|
|
|
22
24
|
];
|
|
23
25
|
export class RuntimeCheck {
|
|
24
26
|
/**
|
|
25
|
-
* Resolve a dependency
|
|
26
|
-
*
|
|
27
|
-
*
|
|
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.
|
|
27
|
+
* Resolve a dependency and return both the command and any prepended args needed.
|
|
28
|
+
* This handles the case where bundled npm/npx on Unix are .js scripts that
|
|
29
|
+
* need to be invoked via node.
|
|
32
30
|
*
|
|
33
|
-
* @param commandName - The command to resolve
|
|
34
|
-
* @returns
|
|
35
|
-
* @throws Error if the command is not available
|
|
31
|
+
* @param commandName - The command to resolve (e.g., "npx", "node", "uvx")
|
|
32
|
+
* @returns ResolvedCommand with command and prependArgs
|
|
33
|
+
* @throws Error if the command is not available
|
|
36
34
|
*/
|
|
37
|
-
static
|
|
38
|
-
// Check if this is a known bundled dependency type
|
|
35
|
+
static resolveCommandWithArgs(commandName) {
|
|
39
36
|
const isBundledDep = BUNDLED_DEPENDENCY_COMMANDS.includes(commandName);
|
|
40
37
|
if (isBundledDep) {
|
|
41
|
-
// Priority 1: Try bundled dependency first
|
|
38
|
+
// Priority 1: Try bundled dependency first
|
|
42
39
|
const bundledPath = Registry.getBundledDependencyPath(commandName);
|
|
43
40
|
if (bundledPath && fs.existsSync(bundledPath)) {
|
|
44
|
-
|
|
41
|
+
// Check if this is a .js file that needs to be invoked via node
|
|
42
|
+
if (bundledPath.endsWith(".js")) {
|
|
43
|
+
const nodePath = Registry.getBundledDependencyPath("node");
|
|
44
|
+
if (nodePath && fs.existsSync(nodePath)) {
|
|
45
|
+
return {
|
|
46
|
+
command: nodePath,
|
|
47
|
+
prependArgs: [bundledPath],
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
// Fallback to system node if bundled node not available
|
|
51
|
+
return {
|
|
52
|
+
command: "node",
|
|
53
|
+
prependArgs: [bundledPath],
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
return { command: bundledPath, prependArgs: [] };
|
|
45
57
|
}
|
|
46
|
-
// Handle python3 -> python mapping
|
|
58
|
+
// Handle python3 -> python mapping
|
|
47
59
|
if (commandName === "python3") {
|
|
48
60
|
const pythonPath = Registry.getBundledDependencyPath("python");
|
|
49
61
|
if (pythonPath && fs.existsSync(pythonPath)) {
|
|
50
|
-
return pythonPath;
|
|
62
|
+
return { command: pythonPath, prependArgs: [] };
|
|
51
63
|
}
|
|
52
64
|
}
|
|
53
|
-
// Priority 2: Fall back to system PATH
|
|
65
|
+
// Priority 2: Fall back to system PATH
|
|
54
66
|
const enhancedPath = getEnhancedPath();
|
|
55
67
|
const resolved = which.sync(commandName, {
|
|
56
68
|
path: enhancedPath,
|
|
57
69
|
nothrow: true,
|
|
58
70
|
});
|
|
59
71
|
if (resolved) {
|
|
60
|
-
return resolved;
|
|
72
|
+
return { command: resolved, prependArgs: [] };
|
|
61
73
|
}
|
|
62
|
-
// Priority 3:
|
|
74
|
+
// Priority 3: Error
|
|
63
75
|
const hint = INSTALL_HINTS[commandName];
|
|
64
76
|
throw new Error(`Missing required command: '${commandName}'.\n` +
|
|
65
77
|
`This command is not available in bundled dependencies or system PATH.\n` +
|
|
@@ -78,7 +90,28 @@ export class RuntimeCheck {
|
|
|
78
90
|
}
|
|
79
91
|
throw new Error(`Command '${commandName}' not found in enhanced PATH. Please install it manually or check your config.`);
|
|
80
92
|
}
|
|
81
|
-
return resolved;
|
|
93
|
+
return { command: resolved, prependArgs: [] };
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Resolve a dependency path with priority order:
|
|
97
|
+
* 1. Bundled dependencies (if provided by host application like ToolPlex Desktop)
|
|
98
|
+
* 2. System PATH (fallback for standalone @client usage)
|
|
99
|
+
* 3. Error if neither available
|
|
100
|
+
*
|
|
101
|
+
* NOTE: This returns just the path. For npm/npx which may be .js files on Unix,
|
|
102
|
+
* use resolveCommandWithArgs() instead to get the proper invocation.
|
|
103
|
+
*
|
|
104
|
+
* @param commandName - The command to resolve
|
|
105
|
+
* @returns The full path to the command executable
|
|
106
|
+
* @throws Error if the command is not available in bundled deps or system PATH
|
|
107
|
+
*/
|
|
108
|
+
static resolveDependency(commandName) {
|
|
109
|
+
const resolved = this.resolveCommandWithArgs(commandName);
|
|
110
|
+
// If there are prepend args, the first one is the actual script path
|
|
111
|
+
if (resolved.prependArgs.length > 0) {
|
|
112
|
+
return resolved.prependArgs[0];
|
|
113
|
+
}
|
|
114
|
+
return resolved.command;
|
|
82
115
|
}
|
|
83
116
|
/**
|
|
84
117
|
* Validate that a command is available (either bundled or in system PATH).
|
|
@@ -1,6 +1,34 @@
|
|
|
1
1
|
import { ServerManagerProtocol } from "./stdioServer.js";
|
|
2
2
|
import { FileLogger } from "../shared/fileLogger.js";
|
|
3
|
+
import Registry from "../mcp-server/registry.js";
|
|
3
4
|
FileLogger.initialize("server-manager");
|
|
5
|
+
// Initialize bundled dependencies from environment variables.
|
|
6
|
+
// These are passed from the MCP server process which received them from Electron.
|
|
7
|
+
// This is critical for proper command resolution (e.g., npx -> node + npx-cli.js).
|
|
8
|
+
//
|
|
9
|
+
// NOTE: We do NOT call Registry.init() here because that does heavy initialization
|
|
10
|
+
// (API services, caches, etc.) that the server-manager doesn't need. We only need
|
|
11
|
+
// the bundled dependency paths for command resolution.
|
|
12
|
+
function initializeBundledDeps() {
|
|
13
|
+
const bundledDependencies = {
|
|
14
|
+
node: process.env.TOOLPLEX_NODE_PATH,
|
|
15
|
+
npm: process.env.TOOLPLEX_NPM_PATH,
|
|
16
|
+
npx: process.env.TOOLPLEX_NPX_PATH,
|
|
17
|
+
python: process.env.TOOLPLEX_PYTHON_PATH,
|
|
18
|
+
pip: process.env.TOOLPLEX_PIP_PATH,
|
|
19
|
+
uv: process.env.TOOLPLEX_UV_PATH,
|
|
20
|
+
uvx: process.env.TOOLPLEX_UVX_PATH,
|
|
21
|
+
git: process.env.TOOLPLEX_GIT_PATH,
|
|
22
|
+
};
|
|
23
|
+
// Only set if we have at least one bundled dep
|
|
24
|
+
const hasAnyBundledDep = Object.values(bundledDependencies).some(Boolean);
|
|
25
|
+
if (hasAnyBundledDep) {
|
|
26
|
+
Registry.setBundledDependencies(bundledDependencies);
|
|
27
|
+
FileLogger.debug(`Server-manager initialized with bundled deps: ${JSON.stringify(bundledDependencies)}`);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
// Initialize bundled deps and start
|
|
31
|
+
initializeBundledDeps();
|
|
4
32
|
const protocol = new ServerManagerProtocol();
|
|
5
33
|
protocol.start().catch((error) => {
|
|
6
34
|
console.error("Failed to start server:", error);
|
|
@@ -6,6 +6,22 @@ import * as path from "path";
|
|
|
6
6
|
import { FileLogger } from "../shared/fileLogger.js";
|
|
7
7
|
import envPaths from "env-paths";
|
|
8
8
|
import { getEnhancedPath } from "../shared/enhancedPath.js";
|
|
9
|
+
import { version } from "../version.js";
|
|
10
|
+
/**
|
|
11
|
+
* A permissive JSON Schema validator that doesn't fail on unresolved $ref.
|
|
12
|
+
* This is needed because MCP SDK 1.22.0 uses AJV which throws errors when
|
|
13
|
+
* output schemas have $ref references to $defs that aren't in the schema
|
|
14
|
+
* (e.g., "#/$defs/TextContent" from Pydantic/FastMCP-generated schemas).
|
|
15
|
+
*/
|
|
16
|
+
class PermissiveJsonSchemaValidator {
|
|
17
|
+
getValidator(_schema) {
|
|
18
|
+
return (input) => ({
|
|
19
|
+
valid: true,
|
|
20
|
+
data: input,
|
|
21
|
+
errorMessage: undefined,
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
}
|
|
9
25
|
const logger = FileLogger;
|
|
10
26
|
export class ServerManager {
|
|
11
27
|
constructor() {
|
|
@@ -242,18 +258,45 @@ export class ServerManager {
|
|
|
242
258
|
else if (config.transport === "stdio") {
|
|
243
259
|
if (!config.command)
|
|
244
260
|
throw new Error("Command is required for stdio transport");
|
|
245
|
-
// Use
|
|
246
|
-
|
|
247
|
-
//
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
261
|
+
// Use the inherited PATH from the parent process (MCP server -> Electron).
|
|
262
|
+
// This PATH already includes bundled bin directories prepended, so commands
|
|
263
|
+
// like "npx", "uvx", "git" will resolve to bundled versions first.
|
|
264
|
+
// We use process.env.PATH directly instead of rebuilding with getEnhancedPath()
|
|
265
|
+
// to preserve the bundled directories that were set up by Electron.
|
|
266
|
+
let inheritedPath = process.env.PATH || getEnhancedPath();
|
|
267
|
+
// When npx downloads and runs packages, it spawns child processes
|
|
268
|
+
// that need to find 'node' in PATH. The bundled node directory MUST be explicitly
|
|
269
|
+
// prepended to PATH to ensure child processes can find the node executable.
|
|
270
|
+
if (process.env.TOOLPLEX_NODE_PATH) {
|
|
271
|
+
const nodeDir = path.dirname(process.env.TOOLPLEX_NODE_PATH);
|
|
272
|
+
const pathDelimiter = process.platform === "win32" ? ";" : ":";
|
|
273
|
+
if (nodeDir && !inheritedPath.startsWith(nodeDir)) {
|
|
274
|
+
inheritedPath = nodeDir + pathDelimiter + inheritedPath;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
// For the command itself, resolve it properly handling the case where
|
|
278
|
+
// bundled npm/npx on Unix are .js scripts that need to be invoked via node.
|
|
279
|
+
let resolvedCommand = config.command;
|
|
280
|
+
let prependArgs = [];
|
|
281
|
+
if (!config.command.startsWith("/") &&
|
|
282
|
+
!/^[A-Za-z]:[\\/]/.test(config.command)) {
|
|
283
|
+
// It's a relative command name (like "npx"), resolve via RuntimeCheck
|
|
284
|
+
const { RuntimeCheck } = await import("../mcp-server/utils/runtimeCheck.js");
|
|
285
|
+
const commandName = RuntimeCheck.extractCommandName(config.command);
|
|
286
|
+
const resolved = RuntimeCheck.resolveCommandWithArgs(commandName);
|
|
287
|
+
resolvedCommand = resolved.command;
|
|
288
|
+
prependArgs = resolved.prependArgs;
|
|
289
|
+
}
|
|
290
|
+
// Combine prependArgs with config.args
|
|
291
|
+
// e.g., if npx is a .js file: command="node", prependArgs=["/path/to/npx-cli.js"]
|
|
292
|
+
// then args become ["/path/to/npx-cli.js", "-y", "@wonderwhy-er/desktop-commander"]
|
|
293
|
+
const finalArgs = [...prependArgs, ...(config.args || [])];
|
|
251
294
|
const serverParams = {
|
|
252
295
|
command: resolvedCommand,
|
|
253
|
-
args:
|
|
296
|
+
args: finalArgs,
|
|
254
297
|
env: {
|
|
255
298
|
...process.env,
|
|
256
|
-
PATH:
|
|
299
|
+
PATH: inheritedPath,
|
|
257
300
|
...(config.env || {}),
|
|
258
301
|
},
|
|
259
302
|
stderr: "pipe",
|
|
@@ -263,7 +306,9 @@ export class ServerManager {
|
|
|
263
306
|
else {
|
|
264
307
|
throw new Error(`Invalid transport type: ${config.transport}`);
|
|
265
308
|
}
|
|
266
|
-
const client = new Client({ name: serverId, version
|
|
309
|
+
const client = new Client({ name: serverId, version }, {
|
|
310
|
+
jsonSchemaValidator: new PermissiveJsonSchemaValidator(),
|
|
311
|
+
});
|
|
267
312
|
try {
|
|
268
313
|
const toolsResponse = await this.connectWithHandshakeTimeout(client, transport, 60000, stderrBuffer, serverId);
|
|
269
314
|
const tools = toolsResponse.tools || [];
|
|
@@ -4,9 +4,14 @@ export type LogLevel = "error" | "warn" | "info" | "debug";
|
|
|
4
4
|
/**
|
|
5
5
|
* Paths to bundled dependencies provided by the host application (e.g., Electron).
|
|
6
6
|
* These dependencies are required for MCP server installations and execution.
|
|
7
|
+
*
|
|
8
|
+
* NOTE: On Unix systems, npm/npx paths point to the actual CLI .js files
|
|
9
|
+
* (e.g., lib/node_modules/npm/bin/npx-cli.js) because electron-builder dereferences
|
|
10
|
+
* symlinks during packaging. These must be invoked via `node <script>`.
|
|
7
11
|
*/
|
|
8
12
|
export interface BundledDependencies {
|
|
9
13
|
node?: string;
|
|
14
|
+
npm?: string;
|
|
10
15
|
npx?: string;
|
|
11
16
|
python?: string;
|
|
12
17
|
pip?: string;
|
|
@@ -8,12 +8,14 @@ const INSTALL_HINTS = {
|
|
|
8
8
|
python: "Install Python: https://www.python.org/downloads/. Or check if you have `python3` installed.",
|
|
9
9
|
python3: "Install Python: https://www.python.org/downloads/. Or check if you have `python` installed.",
|
|
10
10
|
node: "Install Node.js: https://nodejs.org/en/download/",
|
|
11
|
+
npm: "Install npm (comes with Node.js): https://nodejs.org/en/download/",
|
|
11
12
|
npx: "Install npx (comes with Node.js): https://nodejs.org/en/download/",
|
|
12
13
|
git: "Install Git: https://git-scm.com/downloads",
|
|
13
14
|
};
|
|
14
15
|
// Commands that should use bundled dependencies (required)
|
|
15
16
|
const BUNDLED_DEPENDENCY_COMMANDS = [
|
|
16
17
|
"node",
|
|
18
|
+
"npm",
|
|
17
19
|
"python",
|
|
18
20
|
"python3",
|
|
19
21
|
"git",
|
|
@@ -22,44 +24,54 @@ const BUNDLED_DEPENDENCY_COMMANDS = [
|
|
|
22
24
|
];
|
|
23
25
|
export class RuntimeCheck {
|
|
24
26
|
/**
|
|
25
|
-
* Resolve a dependency
|
|
26
|
-
*
|
|
27
|
-
*
|
|
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.
|
|
27
|
+
* Resolve a dependency and return both the command and any prepended args needed.
|
|
28
|
+
* This handles the case where bundled npm/npx on Unix are .js scripts that
|
|
29
|
+
* need to be invoked via node.
|
|
32
30
|
*
|
|
33
|
-
* @param commandName - The command to resolve
|
|
34
|
-
* @returns
|
|
35
|
-
* @throws Error if the command is not available
|
|
31
|
+
* @param commandName - The command to resolve (e.g., "npx", "node", "uvx")
|
|
32
|
+
* @returns ResolvedCommand with command and prependArgs
|
|
33
|
+
* @throws Error if the command is not available
|
|
36
34
|
*/
|
|
37
|
-
static
|
|
38
|
-
// Check if this is a known bundled dependency type
|
|
35
|
+
static resolveCommandWithArgs(commandName) {
|
|
39
36
|
const isBundledDep = BUNDLED_DEPENDENCY_COMMANDS.includes(commandName);
|
|
40
37
|
if (isBundledDep) {
|
|
41
|
-
// Priority 1: Try bundled dependency first
|
|
38
|
+
// Priority 1: Try bundled dependency first
|
|
42
39
|
const bundledPath = Registry.getBundledDependencyPath(commandName);
|
|
43
40
|
if (bundledPath && fs.existsSync(bundledPath)) {
|
|
44
|
-
|
|
41
|
+
// Check if this is a .js file that needs to be invoked via node
|
|
42
|
+
if (bundledPath.endsWith(".js")) {
|
|
43
|
+
const nodePath = Registry.getBundledDependencyPath("node");
|
|
44
|
+
if (nodePath && fs.existsSync(nodePath)) {
|
|
45
|
+
return {
|
|
46
|
+
command: nodePath,
|
|
47
|
+
prependArgs: [bundledPath],
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
// Fallback to system node if bundled node not available
|
|
51
|
+
return {
|
|
52
|
+
command: "node",
|
|
53
|
+
prependArgs: [bundledPath],
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
return { command: bundledPath, prependArgs: [] };
|
|
45
57
|
}
|
|
46
|
-
// Handle python3 -> python mapping
|
|
58
|
+
// Handle python3 -> python mapping
|
|
47
59
|
if (commandName === "python3") {
|
|
48
60
|
const pythonPath = Registry.getBundledDependencyPath("python");
|
|
49
61
|
if (pythonPath && fs.existsSync(pythonPath)) {
|
|
50
|
-
return pythonPath;
|
|
62
|
+
return { command: pythonPath, prependArgs: [] };
|
|
51
63
|
}
|
|
52
64
|
}
|
|
53
|
-
// Priority 2: Fall back to system PATH
|
|
65
|
+
// Priority 2: Fall back to system PATH
|
|
54
66
|
const enhancedPath = getEnhancedPath();
|
|
55
67
|
const resolved = which.sync(commandName, {
|
|
56
68
|
path: enhancedPath,
|
|
57
69
|
nothrow: true,
|
|
58
70
|
});
|
|
59
71
|
if (resolved) {
|
|
60
|
-
return resolved;
|
|
72
|
+
return { command: resolved, prependArgs: [] };
|
|
61
73
|
}
|
|
62
|
-
// Priority 3:
|
|
74
|
+
// Priority 3: Error
|
|
63
75
|
const hint = INSTALL_HINTS[commandName];
|
|
64
76
|
throw new Error(`Missing required command: '${commandName}'.\n` +
|
|
65
77
|
`This command is not available in bundled dependencies or system PATH.\n` +
|
|
@@ -78,7 +90,28 @@ export class RuntimeCheck {
|
|
|
78
90
|
}
|
|
79
91
|
throw new Error(`Command '${commandName}' not found in enhanced PATH. Please install it manually or check your config.`);
|
|
80
92
|
}
|
|
81
|
-
return resolved;
|
|
93
|
+
return { command: resolved, prependArgs: [] };
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Resolve a dependency path with priority order:
|
|
97
|
+
* 1. Bundled dependencies (if provided by host application like ToolPlex Desktop)
|
|
98
|
+
* 2. System PATH (fallback for standalone @client usage)
|
|
99
|
+
* 3. Error if neither available
|
|
100
|
+
*
|
|
101
|
+
* NOTE: This returns just the path. For npm/npx which may be .js files on Unix,
|
|
102
|
+
* use resolveCommandWithArgs() instead to get the proper invocation.
|
|
103
|
+
*
|
|
104
|
+
* @param commandName - The command to resolve
|
|
105
|
+
* @returns The full path to the command executable
|
|
106
|
+
* @throws Error if the command is not available in bundled deps or system PATH
|
|
107
|
+
*/
|
|
108
|
+
static resolveDependency(commandName) {
|
|
109
|
+
const resolved = this.resolveCommandWithArgs(commandName);
|
|
110
|
+
// If there are prepend args, the first one is the actual script path
|
|
111
|
+
if (resolved.prependArgs.length > 0) {
|
|
112
|
+
return resolved.prependArgs[0];
|
|
113
|
+
}
|
|
114
|
+
return resolved.command;
|
|
82
115
|
}
|
|
83
116
|
/**
|
|
84
117
|
* Validate that a command is available (either bundled or in system PATH).
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const version = "0.1.
|
|
1
|
+
export declare const version = "0.1.31";
|
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const version = '0.1.
|
|
1
|
+
export const version = '0.1.31';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@toolplex/client",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.31",
|
|
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",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"ai-integration"
|
|
45
45
|
],
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
47
|
+
"@modelcontextprotocol/sdk": "^1.22.0",
|
|
48
48
|
"@types/node": "^22.13.11",
|
|
49
49
|
"@types/node-fetch": "^2.6.12",
|
|
50
50
|
"@types/which": "^3.0.4",
|