@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.
Files changed (91) hide show
  1. package/LICENSE +98 -0
  2. package/README.md +112 -0
  3. package/dist/mcp-server/clientContext.d.ts +35 -0
  4. package/dist/mcp-server/clientContext.js +107 -0
  5. package/dist/mcp-server/index.d.ts +1 -0
  6. package/dist/mcp-server/index.js +22 -0
  7. package/dist/mcp-server/logging/telemetryLogger.d.ts +18 -0
  8. package/dist/mcp-server/logging/telemetryLogger.js +54 -0
  9. package/dist/mcp-server/policy/callToolObserver.d.ts +9 -0
  10. package/dist/mcp-server/policy/callToolObserver.js +25 -0
  11. package/dist/mcp-server/policy/feedbackPolicy.d.ts +27 -0
  12. package/dist/mcp-server/policy/feedbackPolicy.js +39 -0
  13. package/dist/mcp-server/policy/installObserver.d.ts +11 -0
  14. package/dist/mcp-server/policy/installObserver.js +35 -0
  15. package/dist/mcp-server/policy/playbookPolicy.d.ts +29 -0
  16. package/dist/mcp-server/policy/playbookPolicy.js +81 -0
  17. package/dist/mcp-server/policy/policyEnforcer.d.ts +57 -0
  18. package/dist/mcp-server/policy/policyEnforcer.js +105 -0
  19. package/dist/mcp-server/policy/serverPolicy.d.ts +39 -0
  20. package/dist/mcp-server/policy/serverPolicy.js +61 -0
  21. package/dist/mcp-server/promptsCache.d.ts +25 -0
  22. package/dist/mcp-server/promptsCache.js +51 -0
  23. package/dist/mcp-server/registry.d.ts +34 -0
  24. package/dist/mcp-server/registry.js +109 -0
  25. package/dist/mcp-server/serversCache.d.ts +53 -0
  26. package/dist/mcp-server/serversCache.js +100 -0
  27. package/dist/mcp-server/staticPrompts.d.ts +6 -0
  28. package/dist/mcp-server/staticPrompts.js +6 -0
  29. package/dist/mcp-server/toolDefinitionsCache.d.ts +33 -0
  30. package/dist/mcp-server/toolDefinitionsCache.js +67 -0
  31. package/dist/mcp-server/toolHandlers/callToolHandler.d.ts +3 -0
  32. package/dist/mcp-server/toolHandlers/callToolHandler.js +79 -0
  33. package/dist/mcp-server/toolHandlers/getServerConfigHandler.d.ts +3 -0
  34. package/dist/mcp-server/toolHandlers/getServerConfigHandler.js +69 -0
  35. package/dist/mcp-server/toolHandlers/initHandler.d.ts +3 -0
  36. package/dist/mcp-server/toolHandlers/initHandler.js +117 -0
  37. package/dist/mcp-server/toolHandlers/installServerHandler.d.ts +3 -0
  38. package/dist/mcp-server/toolHandlers/installServerHandler.js +151 -0
  39. package/dist/mcp-server/toolHandlers/listServersHandler.d.ts +2 -0
  40. package/dist/mcp-server/toolHandlers/listServersHandler.js +81 -0
  41. package/dist/mcp-server/toolHandlers/listToolsHandler.d.ts +3 -0
  42. package/dist/mcp-server/toolHandlers/listToolsHandler.js +112 -0
  43. package/dist/mcp-server/toolHandlers/logPlaybookUsageHandler.d.ts +3 -0
  44. package/dist/mcp-server/toolHandlers/logPlaybookUsageHandler.js +65 -0
  45. package/dist/mcp-server/toolHandlers/lookupEntityHandler.d.ts +3 -0
  46. package/dist/mcp-server/toolHandlers/lookupEntityHandler.js +112 -0
  47. package/dist/mcp-server/toolHandlers/savePlaybookHandler.d.ts +3 -0
  48. package/dist/mcp-server/toolHandlers/savePlaybookHandler.js +65 -0
  49. package/dist/mcp-server/toolHandlers/searchHandler.d.ts +3 -0
  50. package/dist/mcp-server/toolHandlers/searchHandler.js +114 -0
  51. package/dist/mcp-server/toolHandlers/serverManagerUtils.d.ts +2 -0
  52. package/dist/mcp-server/toolHandlers/serverManagerUtils.js +20 -0
  53. package/dist/mcp-server/toolHandlers/submitFeedbackHandler.d.ts +3 -0
  54. package/dist/mcp-server/toolHandlers/submitFeedbackHandler.js +70 -0
  55. package/dist/mcp-server/toolHandlers/uninstallServerHandler.d.ts +3 -0
  56. package/dist/mcp-server/toolHandlers/uninstallServerHandler.js +83 -0
  57. package/dist/mcp-server/toolplexApi/service.d.ts +32 -0
  58. package/dist/mcp-server/toolplexApi/service.js +222 -0
  59. package/dist/mcp-server/toolplexApi/types.d.ts +124 -0
  60. package/dist/mcp-server/toolplexApi/types.js +1 -0
  61. package/dist/mcp-server/toolplexServer.d.ts +3 -0
  62. package/dist/mcp-server/toolplexServer.js +249 -0
  63. package/dist/mcp-server/tools.d.ts +2 -0
  64. package/dist/mcp-server/tools.js +13 -0
  65. package/dist/mcp-server/utils/initServerManagers.d.ts +6 -0
  66. package/dist/mcp-server/utils/initServerManagers.js +31 -0
  67. package/dist/mcp-server/utils/resultAnnotators.d.ts +23 -0
  68. package/dist/mcp-server/utils/resultAnnotators.js +50 -0
  69. package/dist/mcp-server/utils/runtimeCheck.d.ts +4 -0
  70. package/dist/mcp-server/utils/runtimeCheck.js +30 -0
  71. package/dist/server-manager/index.d.ts +1 -0
  72. package/dist/server-manager/index.js +8 -0
  73. package/dist/server-manager/serverManager.d.ts +37 -0
  74. package/dist/server-manager/serverManager.js +419 -0
  75. package/dist/server-manager/stdioServer.d.ts +9 -0
  76. package/dist/server-manager/stdioServer.js +136 -0
  77. package/dist/server-manager/stdioTransportProtocol.d.ts +31 -0
  78. package/dist/server-manager/stdioTransportProtocol.js +67 -0
  79. package/dist/shared/enhancedPath.d.ts +7 -0
  80. package/dist/shared/enhancedPath.js +52 -0
  81. package/dist/shared/fileLogger.d.ts +13 -0
  82. package/dist/shared/fileLogger.js +66 -0
  83. package/dist/shared/mcpServerTypes.d.ts +398 -0
  84. package/dist/shared/mcpServerTypes.js +148 -0
  85. package/dist/shared/serverManagerTypes.d.ts +179 -0
  86. package/dist/shared/serverManagerTypes.js +73 -0
  87. package/dist/shared/stdioServerManagerClient.d.ts +12 -0
  88. package/dist/shared/stdioServerManagerClient.js +96 -0
  89. package/dist/version.d.ts +1 -0
  90. package/dist/version.js +1 -0
  91. 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,3 @@
1
+ import { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
2
+ import { ListToolplexToolsParams } from '../../shared/mcpServerTypes.js';
3
+ export declare function handleListTools(params: ListToolplexToolsParams): Promise<CallToolResult>;
@@ -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,3 @@
1
+ import { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
2
+ import { LogPlaybookUsageParams } from '../../shared/mcpServerTypes.js';
3
+ export declare function handleLogPlaybookUsage(params: LogPlaybookUsageParams): Promise<CallToolResult>;
@@ -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,3 @@
1
+ import { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
2
+ import { LookupEntityParams } from '../../shared/mcpServerTypes.js';
3
+ export declare function handleLookupEntityTool(params: LookupEntityParams): Promise<CallToolResult>;
@@ -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,3 @@
1
+ import { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
2
+ import { SavePlaybookParams } from '../../shared/mcpServerTypes.js';
3
+ export declare function handleSavePlaybook(params: SavePlaybookParams): Promise<CallToolResult>;
@@ -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,3 @@
1
+ import { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
2
+ import { SearchParams } from '../../shared/mcpServerTypes.js';
3
+ export declare function handleSearchTool(params: SearchParams): Promise<CallToolResult>;
@@ -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,2 @@
1
+ import { StdioServerManagerClient } from '../../shared/stdioServerManagerClient.js';
2
+ export declare function findServerManagerClient(serverId: string, serverManagerClients: Record<string, StdioServerManagerClient>): Promise<StdioServerManagerClient>;
@@ -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
+ }
@@ -0,0 +1,3 @@
1
+ import { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
2
+ import { SubmitFeedbackParams } from '../../shared/mcpServerTypes.js';
3
+ export declare function handleSubmitFeedback(params: SubmitFeedbackParams): Promise<CallToolResult>;