@toolplex/client 0.1.34 → 0.1.36
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/policy/policyEnforcer.d.ts +9 -0
- package/dist/mcp-server/policy/policyEnforcer.js +14 -0
- package/dist/mcp-server/policy/serverPolicy.d.ts +21 -0
- package/dist/mcp-server/policy/serverPolicy.js +48 -3
- package/dist/mcp-server/toolHandlers/initHandler.js +23 -2
- package/dist/mcp-server/toolHandlers/listServersHandler.js +2 -2
- package/dist/mcp-server/toolHandlers/listToolsHandler.js +2 -2
- package/dist/mcp-server/toolplexApi/types.d.ts +1 -0
- package/dist/src/mcp-server/policy/policyEnforcer.js +14 -0
- package/dist/src/mcp-server/policy/serverPolicy.js +48 -3
- package/dist/src/mcp-server/toolHandlers/initHandler.js +23 -2
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
|
@@ -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
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
-
|
|
119
|
-
const
|
|
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}`);
|
|
@@ -167,6 +172,22 @@ export async function handleInitialize(params) {
|
|
|
167
172
|
.replace("{ALLOWED_MCP_SERVERS}", clientContext.permissions.allowed_mcp_servers.join(", ")),
|
|
168
173
|
});
|
|
169
174
|
}
|
|
175
|
+
// For org users with no tools approved yet, add explicit message
|
|
176
|
+
if (clientContext.isOrgUser && allSucceeded.length === 0) {
|
|
177
|
+
result.content.push({
|
|
178
|
+
type: "text",
|
|
179
|
+
text: "IMPORTANT: No tools have been approved for this organization yet. You cannot use any tools until an admin approves them. Do NOT hallucinate or make up tool capabilities - simply inform the user that no tools are currently available and they should contact their admin.",
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
// Add custom prompt / agent instructions for org users
|
|
183
|
+
if (clientContext.permissions.custom_prompt) {
|
|
184
|
+
result.content.push({
|
|
185
|
+
type: "text",
|
|
186
|
+
text: "ORGANIZATION INSTRUCTIONS:\n" +
|
|
187
|
+
"The following instructions have been set by your organization's administrator. Follow these guidelines:\n\n" +
|
|
188
|
+
clientContext.permissions.custom_prompt,
|
|
189
|
+
});
|
|
190
|
+
}
|
|
170
191
|
result.content.push({
|
|
171
192
|
type: "text",
|
|
172
193
|
text: "Your Most Recently Used Playbooks:\n" +
|
|
@@ -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
|
|
28
|
-
const filteredServers = policyEnforcer.
|
|
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
|
|
71
|
+
// Filter servers by policy (blocked + allowed)
|
|
72
72
|
const serverEntries = Object.entries(parsed.data.tools);
|
|
73
|
-
const filteredEntries = policyEnforcer.
|
|
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({
|
|
@@ -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
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
-
|
|
119
|
-
const
|
|
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}`);
|
|
@@ -167,6 +172,22 @@ export async function handleInitialize(params) {
|
|
|
167
172
|
.replace("{ALLOWED_MCP_SERVERS}", clientContext.permissions.allowed_mcp_servers.join(", ")),
|
|
168
173
|
});
|
|
169
174
|
}
|
|
175
|
+
// For org users with no tools approved yet, add explicit message
|
|
176
|
+
if (clientContext.isOrgUser && allSucceeded.length === 0) {
|
|
177
|
+
result.content.push({
|
|
178
|
+
type: "text",
|
|
179
|
+
text: "IMPORTANT: No tools have been approved for this organization yet. You cannot use any tools until an admin approves them. Do NOT hallucinate or make up tool capabilities - simply inform the user that no tools are currently available and they should contact their admin.",
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
// Add custom prompt / agent instructions for org users
|
|
183
|
+
if (clientContext.permissions.custom_prompt) {
|
|
184
|
+
result.content.push({
|
|
185
|
+
type: "text",
|
|
186
|
+
text: "ORGANIZATION INSTRUCTIONS:\n" +
|
|
187
|
+
"The following instructions have been set by your organization's administrator. Follow these guidelines:\n\n" +
|
|
188
|
+
clientContext.permissions.custom_prompt,
|
|
189
|
+
});
|
|
190
|
+
}
|
|
170
191
|
result.content.push({
|
|
171
192
|
type: "text",
|
|
172
193
|
text: "Your Most Recently Used Playbooks:\n" +
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const version = "0.1.
|
|
1
|
+
export declare const version = "0.1.36";
|
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const version = '0.1.
|
|
1
|
+
export const version = '0.1.36';
|