@toolplex/client 0.1.1
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/LICENSE +98 -0
- package/README.md +112 -0
- package/dist/mcp-server/clientContext.d.ts +35 -0
- package/dist/mcp-server/clientContext.js +107 -0
- package/dist/mcp-server/index.d.ts +1 -0
- package/dist/mcp-server/index.js +22 -0
- package/dist/mcp-server/logging/telemetryLogger.d.ts +18 -0
- package/dist/mcp-server/logging/telemetryLogger.js +54 -0
- package/dist/mcp-server/policy/callToolObserver.d.ts +9 -0
- package/dist/mcp-server/policy/callToolObserver.js +25 -0
- package/dist/mcp-server/policy/feedbackPolicy.d.ts +27 -0
- package/dist/mcp-server/policy/feedbackPolicy.js +39 -0
- package/dist/mcp-server/policy/installObserver.d.ts +11 -0
- package/dist/mcp-server/policy/installObserver.js +35 -0
- package/dist/mcp-server/policy/playbookPolicy.d.ts +29 -0
- package/dist/mcp-server/policy/playbookPolicy.js +81 -0
- package/dist/mcp-server/policy/policyEnforcer.d.ts +57 -0
- package/dist/mcp-server/policy/policyEnforcer.js +105 -0
- package/dist/mcp-server/policy/serverPolicy.d.ts +39 -0
- package/dist/mcp-server/policy/serverPolicy.js +61 -0
- package/dist/mcp-server/promptsCache.d.ts +25 -0
- package/dist/mcp-server/promptsCache.js +51 -0
- package/dist/mcp-server/registry.d.ts +34 -0
- package/dist/mcp-server/registry.js +109 -0
- package/dist/mcp-server/serversCache.d.ts +53 -0
- package/dist/mcp-server/serversCache.js +100 -0
- package/dist/mcp-server/staticPrompts.d.ts +6 -0
- package/dist/mcp-server/staticPrompts.js +6 -0
- package/dist/mcp-server/toolDefinitionsCache.d.ts +33 -0
- package/dist/mcp-server/toolDefinitionsCache.js +67 -0
- package/dist/mcp-server/toolHandlers/callToolHandler.d.ts +3 -0
- package/dist/mcp-server/toolHandlers/callToolHandler.js +79 -0
- package/dist/mcp-server/toolHandlers/getServerConfigHandler.d.ts +3 -0
- package/dist/mcp-server/toolHandlers/getServerConfigHandler.js +69 -0
- package/dist/mcp-server/toolHandlers/initHandler.d.ts +3 -0
- package/dist/mcp-server/toolHandlers/initHandler.js +117 -0
- package/dist/mcp-server/toolHandlers/installServerHandler.d.ts +3 -0
- package/dist/mcp-server/toolHandlers/installServerHandler.js +151 -0
- package/dist/mcp-server/toolHandlers/listServersHandler.d.ts +2 -0
- package/dist/mcp-server/toolHandlers/listServersHandler.js +81 -0
- package/dist/mcp-server/toolHandlers/listToolsHandler.d.ts +3 -0
- package/dist/mcp-server/toolHandlers/listToolsHandler.js +112 -0
- package/dist/mcp-server/toolHandlers/logPlaybookUsageHandler.d.ts +3 -0
- package/dist/mcp-server/toolHandlers/logPlaybookUsageHandler.js +65 -0
- package/dist/mcp-server/toolHandlers/lookupEntityHandler.d.ts +3 -0
- package/dist/mcp-server/toolHandlers/lookupEntityHandler.js +112 -0
- package/dist/mcp-server/toolHandlers/savePlaybookHandler.d.ts +3 -0
- package/dist/mcp-server/toolHandlers/savePlaybookHandler.js +65 -0
- package/dist/mcp-server/toolHandlers/searchHandler.d.ts +3 -0
- package/dist/mcp-server/toolHandlers/searchHandler.js +114 -0
- package/dist/mcp-server/toolHandlers/serverManagerUtils.d.ts +2 -0
- package/dist/mcp-server/toolHandlers/serverManagerUtils.js +20 -0
- package/dist/mcp-server/toolHandlers/submitFeedbackHandler.d.ts +3 -0
- package/dist/mcp-server/toolHandlers/submitFeedbackHandler.js +70 -0
- package/dist/mcp-server/toolHandlers/uninstallServerHandler.d.ts +3 -0
- package/dist/mcp-server/toolHandlers/uninstallServerHandler.js +83 -0
- package/dist/mcp-server/toolplexApi/service.d.ts +32 -0
- package/dist/mcp-server/toolplexApi/service.js +222 -0
- package/dist/mcp-server/toolplexApi/types.d.ts +124 -0
- package/dist/mcp-server/toolplexApi/types.js +1 -0
- package/dist/mcp-server/toolplexServer.d.ts +3 -0
- package/dist/mcp-server/toolplexServer.js +249 -0
- package/dist/mcp-server/tools.d.ts +2 -0
- package/dist/mcp-server/tools.js +13 -0
- package/dist/mcp-server/utils/initServerManagers.d.ts +6 -0
- package/dist/mcp-server/utils/initServerManagers.js +31 -0
- package/dist/mcp-server/utils/resultAnnotators.d.ts +23 -0
- package/dist/mcp-server/utils/resultAnnotators.js +50 -0
- package/dist/mcp-server/utils/runtimeCheck.d.ts +4 -0
- package/dist/mcp-server/utils/runtimeCheck.js +30 -0
- package/dist/server-manager/index.d.ts +1 -0
- package/dist/server-manager/index.js +8 -0
- package/dist/server-manager/serverManager.d.ts +37 -0
- package/dist/server-manager/serverManager.js +419 -0
- package/dist/server-manager/stdioServer.d.ts +9 -0
- package/dist/server-manager/stdioServer.js +136 -0
- package/dist/server-manager/stdioTransportProtocol.d.ts +31 -0
- package/dist/server-manager/stdioTransportProtocol.js +67 -0
- package/dist/shared/enhancedPath.d.ts +7 -0
- package/dist/shared/enhancedPath.js +52 -0
- package/dist/shared/fileLogger.d.ts +13 -0
- package/dist/shared/fileLogger.js +66 -0
- package/dist/shared/mcpServerTypes.d.ts +398 -0
- package/dist/shared/mcpServerTypes.js +148 -0
- package/dist/shared/serverManagerTypes.d.ts +179 -0
- package/dist/shared/serverManagerTypes.js +73 -0
- package/dist/shared/stdioServerManagerClient.d.ts +12 -0
- package/dist/shared/stdioServerManagerClient.js +96 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +1 -0
- package/package.json +70 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { FileLogger } from '../../shared/fileLogger.js';
|
|
2
|
+
import { ListServersResultSchema } from '../../shared/serverManagerTypes.js';
|
|
3
|
+
import Registry from '../registry.js';
|
|
4
|
+
const logger = FileLogger;
|
|
5
|
+
export async function handleListServers() {
|
|
6
|
+
const startTime = Date.now();
|
|
7
|
+
const serverManagerClients = Registry.getServerManagerClients();
|
|
8
|
+
const telemetryLogger = Registry.getTelemetryLogger();
|
|
9
|
+
const promptsCache = Registry.getPromptsCache();
|
|
10
|
+
const serversCache = Registry.getServersCache();
|
|
11
|
+
const policyEnforcer = Registry.getPolicyEnforcer();
|
|
12
|
+
try {
|
|
13
|
+
await logger.debug('Listing all installed servers');
|
|
14
|
+
let response = 'Currently installed MCP servers:\n\n';
|
|
15
|
+
// Collect all servers for updating the cache
|
|
16
|
+
const allServers = [];
|
|
17
|
+
for (const [runtime, client] of Object.entries(serverManagerClients)) {
|
|
18
|
+
const response_data = await client.sendRequest('list_servers', {});
|
|
19
|
+
if (response_data.error) {
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
const parsed = ListServersResultSchema.safeParse(response_data);
|
|
23
|
+
if (!parsed.success) {
|
|
24
|
+
await logger.error(`Invalid response from server manager: ${parsed.error}, runtime: ${runtime}`);
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
if (parsed.data.servers && parsed.data.servers.length > 0) {
|
|
28
|
+
// Filter out blocked servers
|
|
29
|
+
const filteredServers = policyEnforcer.filterBlockedMcpServers(parsed.data.servers, (server) => server.server_id);
|
|
30
|
+
filteredServers.forEach((server) => {
|
|
31
|
+
response += `- Server ID: ${server.server_id}\n`;
|
|
32
|
+
response += ` Name: ${server.server_name}\n`;
|
|
33
|
+
response += ` Description: ${server.description}\n\n`;
|
|
34
|
+
// Add to allServers for cache update
|
|
35
|
+
allServers.push({
|
|
36
|
+
server_id: server.server_id,
|
|
37
|
+
server_name: server.server_name,
|
|
38
|
+
description: server.description,
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
// Update the servers cache with the fresh list
|
|
44
|
+
serversCache.updateServers(allServers);
|
|
45
|
+
if (response === 'Currently installed MCP servers:\n\n') {
|
|
46
|
+
response = promptsCache.getPrompt('list_servers_empty');
|
|
47
|
+
}
|
|
48
|
+
await logger.debug('Successfully retrieved servers list');
|
|
49
|
+
await telemetryLogger.log('client_list_servers', {
|
|
50
|
+
success: true,
|
|
51
|
+
latency_ms: Date.now() - startTime,
|
|
52
|
+
});
|
|
53
|
+
return {
|
|
54
|
+
role: 'system',
|
|
55
|
+
content: [
|
|
56
|
+
{
|
|
57
|
+
type: 'text',
|
|
58
|
+
text: response,
|
|
59
|
+
},
|
|
60
|
+
],
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
const errorMessage = error instanceof Error ? error.message : promptsCache.getPrompt('unexpected_error');
|
|
65
|
+
await logger.error(`Failed to list servers: ${errorMessage}`);
|
|
66
|
+
await telemetryLogger.log('client_list_servers', {
|
|
67
|
+
success: false,
|
|
68
|
+
pii_sanitized_error_message: errorMessage,
|
|
69
|
+
latency_ms: Date.now() - startTime,
|
|
70
|
+
});
|
|
71
|
+
return {
|
|
72
|
+
role: 'system',
|
|
73
|
+
content: [
|
|
74
|
+
{
|
|
75
|
+
type: 'text',
|
|
76
|
+
text: promptsCache.getPrompt('unexpected_error').replace('{ERROR}', errorMessage),
|
|
77
|
+
},
|
|
78
|
+
],
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { FileLogger } from '../../shared/fileLogger.js';
|
|
2
|
+
import { findServerManagerClient } from './serverManagerUtils.js';
|
|
3
|
+
import { ListToolsResultSchema, ListAllToolsResultSchema, } from '../../shared/serverManagerTypes.js';
|
|
4
|
+
import Registry from '../registry.js';
|
|
5
|
+
const logger = FileLogger;
|
|
6
|
+
export async function handleListTools(params) {
|
|
7
|
+
const startTime = Date.now();
|
|
8
|
+
const serverManagerClients = Registry.getServerManagerClients();
|
|
9
|
+
const telemetryLogger = Registry.getTelemetryLogger();
|
|
10
|
+
const promptsCache = Registry.getPromptsCache();
|
|
11
|
+
const policyEnforcer = Registry.getPolicyEnforcer();
|
|
12
|
+
try {
|
|
13
|
+
const server_id = params.server_id;
|
|
14
|
+
let response = '';
|
|
15
|
+
if (server_id) {
|
|
16
|
+
// Check if server is blocked using policy enforcer
|
|
17
|
+
policyEnforcer.enforceUseServerPolicy(server_id);
|
|
18
|
+
await logger.debug(`Listing tools for specific server: ${server_id}`);
|
|
19
|
+
const client = await findServerManagerClient(server_id, serverManagerClients);
|
|
20
|
+
const response_data = await client.sendRequest('list_tools', { server_id: server_id });
|
|
21
|
+
if ('error' in response_data) {
|
|
22
|
+
throw new Error(`Failed to list tools for server_id ${server_id}, error message: ${response_data.error.message}`);
|
|
23
|
+
}
|
|
24
|
+
const parsed = ListToolsResultSchema.safeParse(response_data);
|
|
25
|
+
if (!parsed.success) {
|
|
26
|
+
throw new Error(`Invalid response from server manager: ${parsed.error}`);
|
|
27
|
+
}
|
|
28
|
+
response = promptsCache
|
|
29
|
+
.getPrompt('list_tools_server_header')
|
|
30
|
+
.replace('{SERVER_ID}', parsed.data.server_id)
|
|
31
|
+
.replace('{SERVER_NAME}', parsed.data.server_name);
|
|
32
|
+
if (!parsed.data.tools || parsed.data.tools.length === 0) {
|
|
33
|
+
response += promptsCache.getPrompt('list_tools_empty');
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
parsed.data.tools.forEach((tool) => {
|
|
37
|
+
response += `- ${tool.name}: ${tool.description}\n`;
|
|
38
|
+
response += ` Input Schema: ${JSON.stringify(tool.inputSchema, null, 2)}\n\n`;
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
await logger.debug('Listing tools from all installed servers');
|
|
44
|
+
response = promptsCache.getPrompt('list_tools_all_header');
|
|
45
|
+
for (const [runtime, client] of Object.entries(serverManagerClients)) {
|
|
46
|
+
const response_data = await client.sendRequest('list_all_tools', {});
|
|
47
|
+
if (response_data.error) {
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
const parsed = ListAllToolsResultSchema.safeParse(response_data);
|
|
51
|
+
if (!parsed.success) {
|
|
52
|
+
await logger.error(`Invalid response from server manager: ${parsed.error}, runtime: ${runtime}`);
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
// Filter out blocked servers
|
|
56
|
+
const serverEntries = Object.entries(parsed.data.tools);
|
|
57
|
+
const filteredEntries = policyEnforcer.filterBlockedMcpServers(serverEntries, ([serverId]) => serverId);
|
|
58
|
+
for (const [serverId, serverTools] of filteredEntries) {
|
|
59
|
+
if (serverTools && serverTools.length > 0) {
|
|
60
|
+
response += `Server: ${serverId}\n`;
|
|
61
|
+
serverTools.forEach((tool) => {
|
|
62
|
+
response += `- ${tool.name}: ${tool.description}\n`;
|
|
63
|
+
response += ` Input Schema: ${JSON.stringify(tool.inputSchema, null, 2)}\n\n`;
|
|
64
|
+
});
|
|
65
|
+
response += '\n';
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
if (response === promptsCache.getPrompt('list_tools_all_header')) {
|
|
70
|
+
response += promptsCache.getPrompt('list_tools_empty');
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
await logger.debug('Successfully retrieved tools list');
|
|
74
|
+
await telemetryLogger.log('client_list_tools', {
|
|
75
|
+
success: true,
|
|
76
|
+
log_context: {
|
|
77
|
+
server_id: params.server_id,
|
|
78
|
+
},
|
|
79
|
+
latency_ms: Date.now() - startTime,
|
|
80
|
+
});
|
|
81
|
+
return {
|
|
82
|
+
role: 'system',
|
|
83
|
+
content: [
|
|
84
|
+
{
|
|
85
|
+
type: 'text',
|
|
86
|
+
text: response,
|
|
87
|
+
},
|
|
88
|
+
],
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
const errorMessage = error instanceof Error ? error.message : promptsCache.getPrompt('unexpected_error');
|
|
93
|
+
await logger.error(`Failed to list tools: ${errorMessage}`);
|
|
94
|
+
await telemetryLogger.log('client_list_tools', {
|
|
95
|
+
success: false,
|
|
96
|
+
log_context: {
|
|
97
|
+
server_id: params.server_id,
|
|
98
|
+
},
|
|
99
|
+
pii_sanitized_error_message: errorMessage,
|
|
100
|
+
latency_ms: Date.now() - startTime,
|
|
101
|
+
});
|
|
102
|
+
return {
|
|
103
|
+
role: 'system',
|
|
104
|
+
content: [
|
|
105
|
+
{
|
|
106
|
+
type: 'text',
|
|
107
|
+
text: promptsCache.getPrompt('list_tools_failure').replace('{ERROR}', errorMessage),
|
|
108
|
+
},
|
|
109
|
+
],
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { FileLogger } from '../../shared/fileLogger.js';
|
|
2
|
+
import Registry from '../registry.js';
|
|
3
|
+
const logger = FileLogger;
|
|
4
|
+
export async function handleLogPlaybookUsage(params) {
|
|
5
|
+
const startTime = Date.now();
|
|
6
|
+
await logger.debug(`Handling log playbook usage request with params: ${JSON.stringify(params)}`);
|
|
7
|
+
const { playbook_id, success, error_message } = params;
|
|
8
|
+
await logger.info(`Logging usage for playbook ${playbook_id}`);
|
|
9
|
+
const apiService = Registry.getToolplexApiService();
|
|
10
|
+
const telemetryLogger = Registry.getTelemetryLogger();
|
|
11
|
+
const promptsCache = Registry.getPromptsCache();
|
|
12
|
+
const policyEnforcer = Registry.getPolicyEnforcer();
|
|
13
|
+
const clientContext = Registry.getClientContext();
|
|
14
|
+
try {
|
|
15
|
+
// Check if the client is in restricted mode
|
|
16
|
+
if (clientContext.clientMode === 'restricted') {
|
|
17
|
+
throw new Error('Playbook functionality is disabled in restricted mode.');
|
|
18
|
+
}
|
|
19
|
+
// Enforce playbook usage logging policy
|
|
20
|
+
policyEnforcer.enforceLogPlaybookUsagePolicy();
|
|
21
|
+
await apiService.logPlaybookUsage(playbook_id, success, error_message);
|
|
22
|
+
await logger.info('Playbook usage logged successfully');
|
|
23
|
+
await telemetryLogger.log('client_log_playbook_usage', {
|
|
24
|
+
success: true,
|
|
25
|
+
log_context: {
|
|
26
|
+
playbook_id,
|
|
27
|
+
success: success,
|
|
28
|
+
},
|
|
29
|
+
latency_ms: Date.now() - startTime,
|
|
30
|
+
});
|
|
31
|
+
return {
|
|
32
|
+
role: 'system',
|
|
33
|
+
content: [
|
|
34
|
+
{
|
|
35
|
+
type: 'text',
|
|
36
|
+
text: promptsCache.getPrompt('log_playbook_usage_success'),
|
|
37
|
+
},
|
|
38
|
+
],
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
43
|
+
await logger.error(`Failed to log playbook usage: ${errorMessage}`);
|
|
44
|
+
await telemetryLogger.log('client_log_playbook_usage', {
|
|
45
|
+
success: false,
|
|
46
|
+
log_context: {
|
|
47
|
+
playbook_id,
|
|
48
|
+
success: success,
|
|
49
|
+
},
|
|
50
|
+
pii_sanitized_error_message: errorMessage,
|
|
51
|
+
latency_ms: Date.now() - startTime,
|
|
52
|
+
});
|
|
53
|
+
return {
|
|
54
|
+
role: 'system',
|
|
55
|
+
content: [
|
|
56
|
+
{
|
|
57
|
+
type: 'text',
|
|
58
|
+
text: promptsCache
|
|
59
|
+
.getPrompt('log_playbook_usage_failure')
|
|
60
|
+
.replace('{ERROR}', errorMessage),
|
|
61
|
+
},
|
|
62
|
+
],
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { FileLogger } from '../../shared/fileLogger.js';
|
|
2
|
+
import Registry from '../registry.js';
|
|
3
|
+
import { annotateInstalledServer } from '../utils/resultAnnotators.js';
|
|
4
|
+
const logger = FileLogger;
|
|
5
|
+
export async function handleLookupEntityTool(params) {
|
|
6
|
+
const startTime = Date.now();
|
|
7
|
+
await logger.debug(`Handling lookup entity request for ${params.entity_type} with ID: ${params.entity_id}`);
|
|
8
|
+
const apiService = Registry.getToolplexApiService();
|
|
9
|
+
const telemetryLogger = Registry.getTelemetryLogger();
|
|
10
|
+
const promptsCache = Registry.getPromptsCache();
|
|
11
|
+
const serversCache = Registry.getServersCache();
|
|
12
|
+
const policyEnforcer = Registry.getPolicyEnforcer();
|
|
13
|
+
const clientContext = Registry.getClientContext();
|
|
14
|
+
try {
|
|
15
|
+
// Check if the client is in restricted mode
|
|
16
|
+
if (clientContext.clientMode === 'restricted') {
|
|
17
|
+
throw new Error('Lookup functionality is disabled in restricted mode.');
|
|
18
|
+
}
|
|
19
|
+
// Enforce blocked server policy if looking up a server
|
|
20
|
+
if (params.entity_type === 'server') {
|
|
21
|
+
policyEnforcer.enforceUseServerPolicy(params.entity_id);
|
|
22
|
+
}
|
|
23
|
+
let lookupResponse = await apiService.lookupEntity(params.entity_type, params.entity_id);
|
|
24
|
+
if (!lookupResponse) {
|
|
25
|
+
await logger.debug('No entity found');
|
|
26
|
+
await telemetryLogger.log('client_lookup_entity', {
|
|
27
|
+
success: true,
|
|
28
|
+
log_context: {
|
|
29
|
+
entity_type: params.entity_type,
|
|
30
|
+
entity_id: params.entity_id,
|
|
31
|
+
},
|
|
32
|
+
latency_ms: Date.now() - startTime,
|
|
33
|
+
});
|
|
34
|
+
return {
|
|
35
|
+
role: 'system',
|
|
36
|
+
content: [
|
|
37
|
+
{
|
|
38
|
+
type: 'text',
|
|
39
|
+
text: promptsCache
|
|
40
|
+
.getPrompt('lookup_entity_not_found')
|
|
41
|
+
.replace('{entity_type}', params.entity_type)
|
|
42
|
+
.replace('{entity_id}', params.entity_id),
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
// Use resultAnnotators to annotate if the server is installed
|
|
48
|
+
if (params.entity_type === 'server' && lookupResponse && typeof lookupResponse === 'object') {
|
|
49
|
+
try {
|
|
50
|
+
lookupResponse = annotateInstalledServer(lookupResponse, serversCache);
|
|
51
|
+
}
|
|
52
|
+
catch (err) {
|
|
53
|
+
await logger.warn(`Error annotating installed server: ${err}`);
|
|
54
|
+
// fallback: do not annotate
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
await logger.debug(`Found entity: ${JSON.stringify(lookupResponse)}`);
|
|
58
|
+
let response = `Found ${params.entity_type}:\n`;
|
|
59
|
+
response += JSON.stringify(lookupResponse, null, 2);
|
|
60
|
+
response += '\n';
|
|
61
|
+
await logger.debug('Lookup completed successfully');
|
|
62
|
+
await telemetryLogger.log('client_lookup_entity', {
|
|
63
|
+
success: true,
|
|
64
|
+
log_context: {
|
|
65
|
+
entity_type: params.entity_type,
|
|
66
|
+
entity_id: params.entity_id,
|
|
67
|
+
},
|
|
68
|
+
latency_ms: Date.now() - startTime,
|
|
69
|
+
});
|
|
70
|
+
const content = [
|
|
71
|
+
{
|
|
72
|
+
type: 'text',
|
|
73
|
+
text: response,
|
|
74
|
+
},
|
|
75
|
+
];
|
|
76
|
+
if (params.entity_type === 'server') {
|
|
77
|
+
content.push({
|
|
78
|
+
type: 'text',
|
|
79
|
+
text: promptsCache.getPrompt('lookup_entity_install_guidance'),
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
return {
|
|
83
|
+
role: 'system',
|
|
84
|
+
content,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
catch (err) {
|
|
88
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
89
|
+
await logger.error(`Error looking up entity: ${errorMessage}`);
|
|
90
|
+
await telemetryLogger.log('client_lookup_entity', {
|
|
91
|
+
success: false,
|
|
92
|
+
log_context: {
|
|
93
|
+
entity_type: params.entity_type,
|
|
94
|
+
entity_id: params.entity_id,
|
|
95
|
+
},
|
|
96
|
+
pii_sanitized_error_message: errorMessage,
|
|
97
|
+
latency_ms: Date.now() - startTime,
|
|
98
|
+
});
|
|
99
|
+
return {
|
|
100
|
+
role: 'system',
|
|
101
|
+
content: [
|
|
102
|
+
{
|
|
103
|
+
type: 'text',
|
|
104
|
+
text: promptsCache
|
|
105
|
+
.getPrompt('lookup_entity_error')
|
|
106
|
+
.replace('{entity_type}', params.entity_type)
|
|
107
|
+
.replace('{ERROR}', errorMessage),
|
|
108
|
+
},
|
|
109
|
+
],
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { FileLogger } from '../../shared/fileLogger.js';
|
|
2
|
+
import Registry from '../registry.js';
|
|
3
|
+
const logger = FileLogger;
|
|
4
|
+
export async function handleSavePlaybook(params) {
|
|
5
|
+
const startTime = Date.now();
|
|
6
|
+
await logger.info('Handling save playbook request');
|
|
7
|
+
await logger.debug(`Playbook params: ${JSON.stringify(params)}`);
|
|
8
|
+
const apiService = Registry.getToolplexApiService();
|
|
9
|
+
const telemetryLogger = Registry.getTelemetryLogger();
|
|
10
|
+
const promptsCache = Registry.getPromptsCache();
|
|
11
|
+
const policyEnforcer = Registry.getPolicyEnforcer();
|
|
12
|
+
const clientContext = Registry.getClientContext();
|
|
13
|
+
try {
|
|
14
|
+
// Check if the client is in restricted mode
|
|
15
|
+
if (clientContext.clientMode === 'restricted') {
|
|
16
|
+
throw new Error('Playbook functionality is disabled in restricted mode.');
|
|
17
|
+
}
|
|
18
|
+
// Check if read-only mode is enabled
|
|
19
|
+
if (clientContext.permissions.enable_read_only_mode) {
|
|
20
|
+
throw new Error('Saving playbooks is disabled in read-only mode');
|
|
21
|
+
}
|
|
22
|
+
// Enforce playbook policy before saving
|
|
23
|
+
policyEnforcer.enforceSavePlaybookPolicy(params);
|
|
24
|
+
const { description, actions, domain, keywords, requirements, source_playbook_id, fork_reason, } = params;
|
|
25
|
+
const response = await apiService.createPlaybook(description, actions, domain, keywords, requirements, source_playbook_id, fork_reason);
|
|
26
|
+
await logger.info(`Playbook created successfully with ID: ${response.id}`);
|
|
27
|
+
await telemetryLogger.log('client_save_playbook', {
|
|
28
|
+
success: true,
|
|
29
|
+
log_context: {
|
|
30
|
+
playbook_id: response.id,
|
|
31
|
+
source_playbook_id: source_playbook_id,
|
|
32
|
+
},
|
|
33
|
+
latency_ms: Date.now() - startTime,
|
|
34
|
+
});
|
|
35
|
+
return {
|
|
36
|
+
role: 'system',
|
|
37
|
+
content: [
|
|
38
|
+
{
|
|
39
|
+
type: 'text',
|
|
40
|
+
text: promptsCache
|
|
41
|
+
.getPrompt('save_playbook_success')
|
|
42
|
+
.replace('{PLAYBOOK_ID}', response.id),
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
49
|
+
await logger.error(`Failed to create playbook: ${errorMessage}`);
|
|
50
|
+
await telemetryLogger.log('client_save_playbook', {
|
|
51
|
+
success: false,
|
|
52
|
+
pii_sanitized_error_message: errorMessage,
|
|
53
|
+
latency_ms: Date.now() - startTime,
|
|
54
|
+
});
|
|
55
|
+
return {
|
|
56
|
+
role: 'system',
|
|
57
|
+
content: [
|
|
58
|
+
{
|
|
59
|
+
type: 'text',
|
|
60
|
+
text: promptsCache.getPrompt('save_playbook_failure').replace('{ERROR}', errorMessage),
|
|
61
|
+
},
|
|
62
|
+
],
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { FileLogger } from '../../shared/fileLogger.js';
|
|
2
|
+
import Registry from '../registry.js';
|
|
3
|
+
import { annotateInstalledServers } from '../utils/resultAnnotators.js';
|
|
4
|
+
const logger = FileLogger;
|
|
5
|
+
export async function handleSearchTool(params) {
|
|
6
|
+
const startTime = Date.now();
|
|
7
|
+
await logger.info('Handling search request');
|
|
8
|
+
await logger.debug(`Search params: ${JSON.stringify(params)}`);
|
|
9
|
+
const apiService = Registry.getToolplexApiService();
|
|
10
|
+
const telemetryLogger = Registry.getTelemetryLogger();
|
|
11
|
+
const promptsCache = Registry.getPromptsCache();
|
|
12
|
+
const serversCache = Registry.getServersCache();
|
|
13
|
+
const clientContext = Registry.getClientContext();
|
|
14
|
+
const query = params.query;
|
|
15
|
+
const expandedKeywords = params.expanded_keywords || [];
|
|
16
|
+
const filter = params.filter || 'all';
|
|
17
|
+
const size = params.size || 10;
|
|
18
|
+
try {
|
|
19
|
+
// Check if the client is in restricted mode
|
|
20
|
+
if (clientContext.clientMode === 'restricted') {
|
|
21
|
+
throw new Error('Search functionality is disabled in restricted mode.');
|
|
22
|
+
}
|
|
23
|
+
const results = await apiService.search(query, expandedKeywords, filter, size);
|
|
24
|
+
// Log telemetry event
|
|
25
|
+
await telemetryLogger.log('client_search', {
|
|
26
|
+
success: true,
|
|
27
|
+
log_context: {
|
|
28
|
+
filter,
|
|
29
|
+
size,
|
|
30
|
+
num_expanded_keywords: expandedKeywords.length,
|
|
31
|
+
num_results: (results.mcp_servers?.length ?? -1) + (results.playbooks?.length ?? -1),
|
|
32
|
+
},
|
|
33
|
+
latency_ms: Date.now() - startTime,
|
|
34
|
+
});
|
|
35
|
+
const mcpServers = Array.isArray(results.mcp_servers) ? results.mcp_servers : [];
|
|
36
|
+
const playbooks = Array.isArray(results.playbooks) ? results.playbooks : [];
|
|
37
|
+
const totalResults = mcpServers.length + playbooks.length;
|
|
38
|
+
// Annotate installed servers using resultAnnotators
|
|
39
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
40
|
+
let annotatedServers = [];
|
|
41
|
+
if (mcpServers.length > 0) {
|
|
42
|
+
try {
|
|
43
|
+
annotatedServers = annotateInstalledServers(mcpServers, serversCache);
|
|
44
|
+
}
|
|
45
|
+
catch (err) {
|
|
46
|
+
await logger.warn(`Error annotating installed servers: ${err}`);
|
|
47
|
+
annotatedServers = mcpServers;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
if (totalResults === 0) {
|
|
51
|
+
await logger.info('No search results found');
|
|
52
|
+
return {
|
|
53
|
+
role: 'system',
|
|
54
|
+
content: [
|
|
55
|
+
{
|
|
56
|
+
type: 'text',
|
|
57
|
+
text: promptsCache.getPrompt('search_no_results'),
|
|
58
|
+
},
|
|
59
|
+
],
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
await logger.debug(`Found ${totalResults} results`);
|
|
63
|
+
let response = promptsCache.getPrompt('search_results_header');
|
|
64
|
+
if ((annotatedServers.length ?? 0) > 0) {
|
|
65
|
+
response += '\n=== Servers ===\n';
|
|
66
|
+
response += JSON.stringify(annotatedServers, null, 2);
|
|
67
|
+
response += '\n';
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
response += '\n(No servers found)\n';
|
|
71
|
+
}
|
|
72
|
+
if (playbooks.length > 0) {
|
|
73
|
+
response += '\n=== Playbooks ===\n';
|
|
74
|
+
response += JSON.stringify(playbooks, null, 2);
|
|
75
|
+
response += '\n';
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
response += '\n(No playbooks found)\n';
|
|
79
|
+
}
|
|
80
|
+
await logger.info('Search completed successfully');
|
|
81
|
+
return {
|
|
82
|
+
role: 'system',
|
|
83
|
+
content: [
|
|
84
|
+
{
|
|
85
|
+
type: 'text',
|
|
86
|
+
text: response,
|
|
87
|
+
},
|
|
88
|
+
],
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
93
|
+
await logger.error(`Search failed: ${errorMessage}`);
|
|
94
|
+
await telemetryLogger.log('client_search', {
|
|
95
|
+
success: false,
|
|
96
|
+
log_context: {
|
|
97
|
+
filter,
|
|
98
|
+
size,
|
|
99
|
+
num_expanded_keywords: expandedKeywords.length,
|
|
100
|
+
},
|
|
101
|
+
pii_sanitized_error_message: errorMessage,
|
|
102
|
+
latency_ms: Date.now() - startTime,
|
|
103
|
+
});
|
|
104
|
+
return {
|
|
105
|
+
role: 'system',
|
|
106
|
+
content: [
|
|
107
|
+
{
|
|
108
|
+
type: 'text',
|
|
109
|
+
text: promptsCache.getPrompt('unexpected_error').replace('{ERROR}', errorMessage),
|
|
110
|
+
},
|
|
111
|
+
],
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export async function findServerManagerClient(serverId, serverManagerClients) {
|
|
2
|
+
for (const client of Object.values(serverManagerClients)) {
|
|
3
|
+
const response = await client.sendRequest('list_servers', {});
|
|
4
|
+
if ('error' in response) {
|
|
5
|
+
throw new Error(`Failed to list servers; error message: ${response.error.message}`);
|
|
6
|
+
}
|
|
7
|
+
// Handle both array and object responses
|
|
8
|
+
const serverList = Array.isArray(response) ? response : response.servers;
|
|
9
|
+
// Ensure serverList is an array and handle potential null/undefined
|
|
10
|
+
if (Array.isArray(serverList)) {
|
|
11
|
+
const hasServer = serverList.some((s) =>
|
|
12
|
+
// Handle both server_id and serverId properties
|
|
13
|
+
s.server_id === serverId || s.serverId === serverId);
|
|
14
|
+
if (hasServer) {
|
|
15
|
+
return client;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
throw new Error(`Server ${serverId} not found`);
|
|
20
|
+
}
|