@toolplex/client 0.1.33 → 0.1.35

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/README.md CHANGED
@@ -1,15 +1,18 @@
1
1
  # ToolPlex Client
2
2
 
3
- [![Visit ToolPlex](https://img.shields.io/badge/ToolPlex.ai-%F0%9F%9A%80-blue?style=flat)](https://toolplex.ai)
3
+ [![npm version](https://img.shields.io/npm/v/@toolplex/client)](https://www.npmjs.com/package/@toolplex/client)
4
+ [![npm downloads](https://img.shields.io/npm/dw/@toolplex/client)](https://www.npmjs.com/package/@toolplex/client)
5
+ [![Visit ToolPlex](https://img.shields.io/badge/ToolPlex.ai-%F0%9F%9A%80-blue?style=flat)](https://toolplex.ai)
4
6
  [![Discord](https://img.shields.io/badge/Join%20Discord-5865F2?style=for-the-badge&logo=discord&logoColor=white)](https://discord.gg/KpCjj8ay)
5
7
 
6
- This repository contains the official **ToolPlex MCP server** — the npm package that enables AI agents to connect to the ToolPlex platform.
8
+ This repository contains the official **ToolPlex MCP server** — the npm package that powers agent interaction with the ToolPlex network. It's the core of [ToolPlex Desktop](https://toolplex.ai) and works with any MCP-compatible client.
7
9
 
8
10
  ToolPlex is a curated tool ecosystem built for AI agents. With ToolPlex, your agent can:
9
- - 🔍 Discover 2,000+ high quality, open-source MCP tools
10
- - 🛠️ Install and run tools with your permission
11
- - 📚 Save workflows as reusable playbooks
12
- - 🔁 Learn from success your agent evolves from collective agent feedback
11
+ - Discover 4,000+ high quality, open-source MCP tools (and growing)
12
+ - Automatically install and debug complex MCP servers — including fetching READMEs and resolving dependencies
13
+ - Call any tool across any installed MCP server without loading every schema into context
14
+ - Create and run playbooks (multi-step AI workflows) just by chatting
15
+ - Learn from collective agent feedback to improve over time
13
16
 
14
17
  No complex setup. Just add ToolPlex to your AI client and start automating.
15
18
 
@@ -17,20 +20,22 @@ No complex setup. Just add ToolPlex to your AI client and start automating.
17
20
 
18
21
  - **Agent Tool Discovery** — Your agent can search a curated index of MCP servers, filtered by code analysis and popularity signals to find tools that actually work
19
22
  - **Smooth Install Experience** — Automatically installs even complex tools, with the agent handling tricky build steps, dependencies, and setup flows behind the scenes
20
- - **Secure Config Handling** — Injects API keys, secrets, and file paths only when needed — always under your control
21
23
  - **Seamless Server Activation** — Install and uninstall MCP servers at any time without restarting your LLM or resetting the session
22
24
  - **Workflow Memory** — Successful tasks are saved as playbooks your agent can search, reuse, and adapt later
23
25
  - **Quality Signals Built-In** — Agents report tool usage and failure rates to help down-rank unreliable or broken servers automatically
24
- - **Team-Ready** — Share tools, playbooks, and permission sets across your org for faster onboarding and coordinated automation
25
26
  - **Full Agent Control** — Use the ToolPlex dashboard to manage server access, shell permissions, file visibility, feedback settings, and more
26
27
  - **Local-First Execution** — By default, ToolPlex installs and runs tools on your machine for full speed, privacy, and control
27
28
 
28
29
  ## Quick Setup
29
30
 
30
- **Claude Desktop (recommended)**
31
+ **ToolPlex Desktop (recommended)**
32
+
33
+ Download [ToolPlex Desktop](https://toolplex.ai) — a native app with the ToolPlex client built-in. No configuration needed.
34
+
35
+ **Claude Desktop or other MCP clients**
36
+
31
37
  1. Sign up for a [ToolPlex AI](https://toolplex.ai) account and create your first API key.
32
- 2. Install [Claude Desktop](https://claude.ai/download).
33
- 3. Add this to your `claude_desktop_config.json`:
38
+ 2. Add this to your MCP client config (e.g. `claude_desktop_config.json`):
34
39
 
35
40
  ```json
36
41
  {
@@ -46,7 +51,7 @@ No complex setup. Just add ToolPlex to your AI client and start automating.
46
51
  }
47
52
  ```
48
53
 
49
- Or use any AI chat client that [supports MCP](https://github.com/punkpeye/awesome-mcp-clients).
54
+ Works with any AI client that [supports MCP](https://github.com/punkpeye/awesome-mcp-clients).
50
55
 
51
56
  ToolPlex works best as the **only server** in your MCP config, since it handles discovery, installation, and management of all other MCP servers on your behalf.
52
57
 
@@ -102,11 +107,12 @@ After initializing ToolPlex, just talk to your agent naturally:
102
107
  > save this as a playbook
103
108
  ```
104
109
 
105
- ## LLM Compatability
106
- ToolPlex works better with generalist, high-context LLMs that support tool-calling, like:
107
- * `claude-sonnet-3.7`
108
- * `claude-sonnet-4`
109
- * `gpt-4o`
110
- * `gpt-4.1`
110
+ ## LLM Compatibility
111
+ ToolPlex works best with high-context LLMs that support tool-calling:
112
+ * Claude Sonnet 4.5, Opus 4.5, Haiku 4.5
113
+ * GPT-5, GPT-5 Mini
114
+ * Gemini 2.5 Pro, Gemini 3 Pro
115
+ * Kimi K2, Kimi K2 Thinking
116
+ * Grok 4
111
117
 
112
- Weaker reasoning models like `deepseek-chat` or `claude-haiku-3.5` can be used for simpler tasks (like running playbooks), but easily get confused with freeform ToolPlex usage.
118
+ Lighter models like DeepSeek V3 or Qwen3 can handle simpler tasks (like running playbooks), but may struggle with complex freeform ToolPlex usage.
@@ -46,6 +46,15 @@ export declare class PolicyEnforcer {
46
46
  * @returns Filtered list with blocked servers removed
47
47
  */
48
48
  filterBlockedMcpServers<T>(servers: T[], getServerId: (item: T) => string): T[];
49
+ /**
50
+ * Applies both blocked and allowed server filtering.
51
+ * First removes blocked servers, then filters to allowed servers (if set).
52
+ *
53
+ * @param servers List of objects containing server IDs
54
+ * @param getServerId Function that extracts the server ID from an object
55
+ * @returns Filtered list with policy applied
56
+ */
57
+ filterServersByPolicy<T>(servers: T[], getServerId: (item: T) => string): T[];
49
58
  /**
50
59
  * Get a reference to the CallToolObserver instance.
51
60
  */
@@ -84,6 +84,20 @@ export class PolicyEnforcer {
84
84
  }
85
85
  return this.serverPolicy.filterBlockedMcpServers(servers, getServerId);
86
86
  }
87
+ /**
88
+ * Applies both blocked and allowed server filtering.
89
+ * First removes blocked servers, then filters to allowed servers (if set).
90
+ *
91
+ * @param servers List of objects containing server IDs
92
+ * @param getServerId Function that extracts the server ID from an object
93
+ * @returns Filtered list with policy applied
94
+ */
95
+ filterServersByPolicy(servers, getServerId) {
96
+ if (!this.serverPolicy) {
97
+ throw new Error("PolicyEnforcer not initialized");
98
+ }
99
+ return this.serverPolicy.filterServersByPolicy(servers, getServerId);
100
+ }
87
101
  /**
88
102
  * Get a reference to the CallToolObserver instance.
89
103
  */
@@ -11,6 +11,8 @@ export declare class ServerPolicy {
11
11
  enforceBlockedServerPolicy(serverId: string): void;
12
12
  /**
13
13
  * Validates that a server is allowed.
14
+ * - For org users: if allowlist is empty/null, NO servers are allowed
15
+ * - For non-org users: if allowlist is empty/null, all servers are allowed
14
16
  *
15
17
  * @throws Error if attempting to use a server not in the allowed list
16
18
  */
@@ -36,4 +38,23 @@ export declare class ServerPolicy {
36
38
  * @returns Filtered list with blocked servers removed
37
39
  */
38
40
  filterBlockedMcpServers<T>(servers: T[], getServerId: (item: T) => string): T[];
41
+ /**
42
+ * Filters servers to only include allowed servers (if allowlist is set).
43
+ * - For org users: if allowlist is empty/null, return NO servers (admin hasn't approved any)
44
+ * - For non-org users: if allowlist is empty/null, return all servers (no restrictions)
45
+ *
46
+ * @param servers List of objects containing server IDs
47
+ * @param getServerId Function that extracts the server ID from an object
48
+ * @returns Filtered list with only allowed servers
49
+ */
50
+ filterToAllowedServers<T>(servers: T[], getServerId: (item: T) => string): T[];
51
+ /**
52
+ * Applies both blocked and allowed server filtering.
53
+ * First removes blocked servers, then filters to allowed servers (if set).
54
+ *
55
+ * @param servers List of objects containing server IDs
56
+ * @param getServerId Function that extracts the server ID from an object
57
+ * @returns Filtered list with policy applied
58
+ */
59
+ filterServersByPolicy<T>(servers: T[], getServerId: (item: T) => string): T[];
39
60
  }
@@ -15,14 +15,24 @@ export class ServerPolicy {
15
15
  }
16
16
  /**
17
17
  * Validates that a server is allowed.
18
+ * - For org users: if allowlist is empty/null, NO servers are allowed
19
+ * - For non-org users: if allowlist is empty/null, all servers are allowed
18
20
  *
19
21
  * @throws Error if attempting to use a server not in the allowed list
20
22
  */
21
23
  enforceAllowedServerPolicy(serverId) {
22
24
  const allowedServers = this.clientContext.permissions.allowed_mcp_servers;
23
- if (allowedServers &&
24
- allowedServers.length > 0 &&
25
- !allowedServers.includes(serverId)) {
25
+ // If allowlist is empty/null
26
+ if (!allowedServers || allowedServers.length === 0) {
27
+ // For org users: empty allowlist means NO servers approved
28
+ if (this.clientContext.isOrgUser) {
29
+ throw new Error(`No tools have been approved for your organization yet. Please contact your admin to approve tools on the ToolPlex Dashboard.`);
30
+ }
31
+ // For non-org users: no restrictions
32
+ return;
33
+ }
34
+ // Check if server is in the allowlist
35
+ if (!allowedServers.includes(serverId)) {
26
36
  throw new Error(`Server "${serverId}" is not allowed for your account. Please adjust the Allowed MCP Servers permissions on the ToolPlex Dashboard if this is a mistake.`);
27
37
  }
28
38
  }
@@ -60,4 +70,39 @@ export class ServerPolicy {
60
70
  filterBlockedMcpServers(servers, getServerId) {
61
71
  return servers.filter((server) => !this.blockedMcpServersSet.has(getServerId(server)));
62
72
  }
73
+ /**
74
+ * Filters servers to only include allowed servers (if allowlist is set).
75
+ * - For org users: if allowlist is empty/null, return NO servers (admin hasn't approved any)
76
+ * - For non-org users: if allowlist is empty/null, return all servers (no restrictions)
77
+ *
78
+ * @param servers List of objects containing server IDs
79
+ * @param getServerId Function that extracts the server ID from an object
80
+ * @returns Filtered list with only allowed servers
81
+ */
82
+ filterToAllowedServers(servers, getServerId) {
83
+ const allowedServers = this.clientContext.permissions.allowed_mcp_servers;
84
+ // If no allowlist configured
85
+ if (!allowedServers || allowedServers.length === 0) {
86
+ // For org users: empty allowlist means NO servers approved yet
87
+ if (this.clientContext.isOrgUser) {
88
+ return [];
89
+ }
90
+ // For non-org users: no restrictions, return all
91
+ return servers;
92
+ }
93
+ const allowedSet = new Set(allowedServers);
94
+ return servers.filter((server) => allowedSet.has(getServerId(server)));
95
+ }
96
+ /**
97
+ * Applies both blocked and allowed server filtering.
98
+ * First removes blocked servers, then filters to allowed servers (if set).
99
+ *
100
+ * @param servers List of objects containing server IDs
101
+ * @param getServerId Function that extracts the server ID from an object
102
+ * @returns Filtered list with policy applied
103
+ */
104
+ filterServersByPolicy(servers, getServerId) {
105
+ const withoutBlocked = this.filterBlockedMcpServers(servers, getServerId);
106
+ return this.filterToAllowedServers(withoutBlocked, getServerId);
107
+ }
63
108
  }
@@ -115,8 +115,13 @@ export async function handleInitialize(params) {
115
115
  else {
116
116
  await logger.debug("No session resume history provided (new session)");
117
117
  }
118
- const allSucceeded = serverManagerInitResults.succeeded;
119
- const allFailures = serverManagerInitResults.failures;
118
+ // Filter servers by policy (blocked + allowed)
119
+ const allSucceeded = policyEnforcer.filterServersByPolicy(serverManagerInitResults.succeeded, (s) => s.server_id);
120
+ // Also filter failures by policy
121
+ const failureEntries = Object.entries(serverManagerInitResults.failures);
122
+ const filteredFailureEntries = policyEnforcer.filterServersByPolicy(failureEntries, ([serverId]) => serverId);
123
+ const allFailures = Object.fromEntries(filteredFailureEntries);
124
+ await logger.info(`Filtered to ${allSucceeded.length} servers (policy enforced)`);
120
125
  // Initialize the serversCache with the succeeded servers
121
126
  serversCache.init(allSucceeded);
122
127
  await logger.debug(`Total successes: ${allSucceeded.length}, Total failures: ${Object.keys(allFailures).length}`);
@@ -24,8 +24,8 @@ export async function handleListServers() {
24
24
  continue;
25
25
  }
26
26
  if (parsed.data.servers && parsed.data.servers.length > 0) {
27
- // Filter out blocked servers
28
- const filteredServers = policyEnforcer.filterBlockedMcpServers(parsed.data.servers, (server) => server.server_id);
27
+ // Filter servers by policy (blocked + allowed)
28
+ const filteredServers = policyEnforcer.filterServersByPolicy(parsed.data.servers, (server) => server.server_id);
29
29
  filteredServers.forEach((server) => {
30
30
  // Add to allServers for cache update and structured response
31
31
  allServers.push({
@@ -68,9 +68,9 @@ export async function handleListTools(params) {
68
68
  await logger.error(`Invalid response from server manager: ${parsed.error}, runtime: ${runtime}`);
69
69
  continue;
70
70
  }
71
- // Filter out blocked servers
71
+ // Filter servers by policy (blocked + allowed)
72
72
  const serverEntries = Object.entries(parsed.data.tools);
73
- const filteredEntries = policyEnforcer.filterBlockedMcpServers(serverEntries, ([serverId]) => serverId);
73
+ const filteredEntries = policyEnforcer.filterServersByPolicy(serverEntries, ([serverId]) => serverId);
74
74
  for (const [serverId, serverTools] of filteredEntries) {
75
75
  if (serverTools && serverTools.length > 0) {
76
76
  allServerTools.push({
@@ -23,6 +23,58 @@ class PermissiveJsonSchemaValidator {
23
23
  }
24
24
  }
25
25
  const logger = FileLogger;
26
+ // Private registry constants
27
+ const PRIVATE_REGISTRY_URL = "https://registry.toolplex.ai";
28
+ const PRIVATE_SCOPE_PATTERN = /^@(tp-(user|org)-[a-f0-9]{11})\//;
29
+ /**
30
+ * Extract the private registry scope from args if present.
31
+ * Returns the scope without @ (e.g., "tp-user-abc123def45") or null if not found.
32
+ */
33
+ function extractPrivateRegistryScope(args) {
34
+ for (const arg of args) {
35
+ const match = arg.match(PRIVATE_SCOPE_PATTERN);
36
+ if (match) {
37
+ return match[1]; // e.g., "tp-user-abc123def45"
38
+ }
39
+ }
40
+ return null;
41
+ }
42
+ /**
43
+ * Build npm config env vars for private registry authentication.
44
+ *
45
+ * Uses Basic auth (_auth) instead of _authToken because:
46
+ * - _authToken expects a token issued by Verdaccio (JWT)
47
+ * - _auth sends username:password on every request, triggering authenticate()
48
+ *
49
+ * The Verdaccio auth plugin expects:
50
+ * - username: the scope without @ (e.g., "tp-user-abc123def45")
51
+ * - password: the ToolPlex API key (tp_live_xxx or tp_test_xxx)
52
+ *
53
+ * @param scope - The scope without @ (e.g., "tp-user-abc123def45")
54
+ * @param apiKey - The ToolPlex API key
55
+ */
56
+ function getPrivateRegistryEnv(scope, apiKey) {
57
+ // Create base64-encoded "username:password" for Basic auth
58
+ // Username is the scope (e.g., "tp-user-abc123def45")
59
+ // Password is the API key
60
+ const auth = Buffer.from(`${scope}:${apiKey}`).toString("base64");
61
+ return {
62
+ // Set the registry for @tp-user-* and @tp-org-* scopes
63
+ // Note: Using npm_config_ prefix with special chars may not work on all systems
64
+ "npm_config_@tp-user:registry": PRIVATE_REGISTRY_URL,
65
+ "npm_config_@tp-org:registry": PRIVATE_REGISTRY_URL,
66
+ // Set Basic auth for the registry
67
+ // npm_config_//host/:_auth maps to //host/:_auth in .npmrc
68
+ "npm_config_//registry.toolplex.ai/:_auth": auth,
69
+ };
70
+ }
71
+ /**
72
+ * Get additional args to prepend for private registry packages.
73
+ * Uses --registry flag which is more reliable than env vars with special chars.
74
+ */
75
+ function getPrivateRegistryArgs() {
76
+ return [`--registry=${PRIVATE_REGISTRY_URL}`];
77
+ }
26
78
  export class ServerManager {
27
79
  constructor() {
28
80
  this.config = {};
@@ -287,16 +339,38 @@ export class ServerManager {
287
339
  resolvedCommand = resolved.command;
288
340
  prependArgs = resolved.prependArgs;
289
341
  }
342
+ // Check if this is a private registry package and inject auth if needed
343
+ // Private packages have scopes like @tp-user-xxx/ or @tp-org-xxx/
344
+ let privateRegistryEnv = {};
345
+ let privateRegistryArgs = [];
346
+ const privateScope = extractPrivateRegistryScope(config.args || []);
347
+ if (privateScope) {
348
+ const apiKey = process.env.TOOLPLEX_API_KEY;
349
+ if (apiKey) {
350
+ privateRegistryEnv = getPrivateRegistryEnv(privateScope, apiKey);
351
+ privateRegistryArgs = getPrivateRegistryArgs();
352
+ await logger.debug(`Injecting private registry auth for ${serverId} (scope: ${privateScope})`);
353
+ }
354
+ else {
355
+ await logger.warn(`Private registry package detected but no TOOLPLEX_API_KEY available`);
356
+ }
357
+ }
290
358
  // Combine prependArgs with config.args
291
359
  // e.g., if npx is a .js file: command="node", prependArgs=["/path/to/npx-cli.js"]
292
360
  // then args become ["/path/to/npx-cli.js", "-y", "@wonderwhy-er/desktop-commander"]
293
- const finalArgs = [...prependArgs, ...(config.args || [])];
361
+ // For private registry packages, insert --registry flag after prependArgs but before package name
362
+ const finalArgs = [
363
+ ...prependArgs,
364
+ ...privateRegistryArgs,
365
+ ...(config.args || []),
366
+ ];
294
367
  const serverParams = {
295
368
  command: resolvedCommand,
296
369
  args: finalArgs,
297
370
  env: {
298
371
  ...process.env,
299
372
  PATH: inheritedPath,
373
+ ...privateRegistryEnv,
300
374
  ...(config.env || {}),
301
375
  },
302
376
  stderr: "pipe",
@@ -84,6 +84,20 @@ export class PolicyEnforcer {
84
84
  }
85
85
  return this.serverPolicy.filterBlockedMcpServers(servers, getServerId);
86
86
  }
87
+ /**
88
+ * Applies both blocked and allowed server filtering.
89
+ * First removes blocked servers, then filters to allowed servers (if set).
90
+ *
91
+ * @param servers List of objects containing server IDs
92
+ * @param getServerId Function that extracts the server ID from an object
93
+ * @returns Filtered list with policy applied
94
+ */
95
+ filterServersByPolicy(servers, getServerId) {
96
+ if (!this.serverPolicy) {
97
+ throw new Error("PolicyEnforcer not initialized");
98
+ }
99
+ return this.serverPolicy.filterServersByPolicy(servers, getServerId);
100
+ }
87
101
  /**
88
102
  * Get a reference to the CallToolObserver instance.
89
103
  */
@@ -15,14 +15,24 @@ export class ServerPolicy {
15
15
  }
16
16
  /**
17
17
  * Validates that a server is allowed.
18
+ * - For org users: if allowlist is empty/null, NO servers are allowed
19
+ * - For non-org users: if allowlist is empty/null, all servers are allowed
18
20
  *
19
21
  * @throws Error if attempting to use a server not in the allowed list
20
22
  */
21
23
  enforceAllowedServerPolicy(serverId) {
22
24
  const allowedServers = this.clientContext.permissions.allowed_mcp_servers;
23
- if (allowedServers &&
24
- allowedServers.length > 0 &&
25
- !allowedServers.includes(serverId)) {
25
+ // If allowlist is empty/null
26
+ if (!allowedServers || allowedServers.length === 0) {
27
+ // For org users: empty allowlist means NO servers approved
28
+ if (this.clientContext.isOrgUser) {
29
+ throw new Error(`No tools have been approved for your organization yet. Please contact your admin to approve tools on the ToolPlex Dashboard.`);
30
+ }
31
+ // For non-org users: no restrictions
32
+ return;
33
+ }
34
+ // Check if server is in the allowlist
35
+ if (!allowedServers.includes(serverId)) {
26
36
  throw new Error(`Server "${serverId}" is not allowed for your account. Please adjust the Allowed MCP Servers permissions on the ToolPlex Dashboard if this is a mistake.`);
27
37
  }
28
38
  }
@@ -60,4 +70,39 @@ export class ServerPolicy {
60
70
  filterBlockedMcpServers(servers, getServerId) {
61
71
  return servers.filter((server) => !this.blockedMcpServersSet.has(getServerId(server)));
62
72
  }
73
+ /**
74
+ * Filters servers to only include allowed servers (if allowlist is set).
75
+ * - For org users: if allowlist is empty/null, return NO servers (admin hasn't approved any)
76
+ * - For non-org users: if allowlist is empty/null, return all servers (no restrictions)
77
+ *
78
+ * @param servers List of objects containing server IDs
79
+ * @param getServerId Function that extracts the server ID from an object
80
+ * @returns Filtered list with only allowed servers
81
+ */
82
+ filterToAllowedServers(servers, getServerId) {
83
+ const allowedServers = this.clientContext.permissions.allowed_mcp_servers;
84
+ // If no allowlist configured
85
+ if (!allowedServers || allowedServers.length === 0) {
86
+ // For org users: empty allowlist means NO servers approved yet
87
+ if (this.clientContext.isOrgUser) {
88
+ return [];
89
+ }
90
+ // For non-org users: no restrictions, return all
91
+ return servers;
92
+ }
93
+ const allowedSet = new Set(allowedServers);
94
+ return servers.filter((server) => allowedSet.has(getServerId(server)));
95
+ }
96
+ /**
97
+ * Applies both blocked and allowed server filtering.
98
+ * First removes blocked servers, then filters to allowed servers (if set).
99
+ *
100
+ * @param servers List of objects containing server IDs
101
+ * @param getServerId Function that extracts the server ID from an object
102
+ * @returns Filtered list with policy applied
103
+ */
104
+ filterServersByPolicy(servers, getServerId) {
105
+ const withoutBlocked = this.filterBlockedMcpServers(servers, getServerId);
106
+ return this.filterToAllowedServers(withoutBlocked, getServerId);
107
+ }
63
108
  }
@@ -115,8 +115,13 @@ export async function handleInitialize(params) {
115
115
  else {
116
116
  await logger.debug("No session resume history provided (new session)");
117
117
  }
118
- const allSucceeded = serverManagerInitResults.succeeded;
119
- const allFailures = serverManagerInitResults.failures;
118
+ // Filter servers by policy (blocked + allowed)
119
+ const allSucceeded = policyEnforcer.filterServersByPolicy(serverManagerInitResults.succeeded, (s) => s.server_id);
120
+ // Also filter failures by policy
121
+ const failureEntries = Object.entries(serverManagerInitResults.failures);
122
+ const filteredFailureEntries = policyEnforcer.filterServersByPolicy(failureEntries, ([serverId]) => serverId);
123
+ const allFailures = Object.fromEntries(filteredFailureEntries);
124
+ await logger.info(`Filtered to ${allSucceeded.length} servers (policy enforced)`);
120
125
  // Initialize the serversCache with the succeeded servers
121
126
  serversCache.init(allSucceeded);
122
127
  await logger.debug(`Total successes: ${allSucceeded.length}, Total failures: ${Object.keys(allFailures).length}`);
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const version = "0.1.33";
1
+ export declare const version = "0.1.35";
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const version = '0.1.33';
1
+ export const version = '0.1.35';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@toolplex/client",
3
- "version": "0.1.33",
3
+ "version": "0.1.35",
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",