@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
package/LICENSE ADDED
@@ -0,0 +1,98 @@
1
+ License text copyright (c) 2025 ToolPlex LLC. All Rights Reserved.
2
+ "Business Source License" is a trademark of MariaDB Corporation Ab.
3
+
4
+ Parameters
5
+
6
+ Licensor: ToolPlex LLC
7
+ Licensed Work: ToolPlex Client, as published in this repository. The Licensed Work is (c) 2025
8
+ ToolPlex LLC.
9
+ Additional Use Grant: You may make production use of the Licensed Work, provided
10
+ your use does not involve offering it or derivative works on a
11
+ hosted or embedded basis to compete with ToolPlex's paid platform
12
+ or services. For purposes of this license:
13
+
14
+ A "competitive offering" is a product or service that is offered
15
+ to third parties on a paid basis (including through paid support),
16
+ and that significantly overlaps with ToolPlex's platform, including:
17
+ tool discovery, tool execution, agent control, or workflow/playbook
18
+ reuse.
19
+
20
+ "Product" means software made available to end users or used to
21
+ operate hosted services.
22
+
23
+ "Embedded" includes incorporating source or binary components of
24
+ the Licensed Work into another product or requiring the Licensed
25
+ Work for that product to function.
26
+
27
+ "Internal use" within your organization is permitted, even for
28
+ commercial purposes, as long as it is not redistributed or offered
29
+ to others.
30
+
31
+ "API Integration Restrictions": Use of the ToolPlex API or services
32
+ to build, enhance, or operate a competitive offering is prohibited,
33
+ regardless of whether the Licensed Work is directly incorporated.
34
+
35
+ "Derivative Works": Includes any modification, adaptation, or custom
36
+ implementation based on the Licensed Work, including but not limited
37
+ to custom clients for ToolPlex API interaction. While permitted under
38
+ this license, such derivatives may present technical challenges and
39
+ are not officially supported.
40
+
41
+ "Research and Academic Use": Non-commercial research, educational,
42
+ and academic use is permitted regardless of competitive overlap,
43
+ provided results are not used to develop commercial competitive
44
+ offerings.
45
+
46
+ "Official Client Recommendation": While custom clients are permitted
47
+ under this license, ToolPlex strongly recommends using the official
48
+ client to ensure compatibility, security, and optimal performance.
49
+
50
+ Hosting or using the Licensed Work for internal purposes within an
51
+ organization is not considered a competitive offering. ToolPlex
52
+ considers your organization to include all of your affiliates under
53
+ common control.
54
+
55
+ Change Date: None
56
+ Change License: MPL 2.0
57
+
58
+ For information about alternative licensing arrangements for the Licensed Work,
59
+ please contact support@toolplex.ai.
60
+
61
+ Notice
62
+
63
+ Business Source License 1.1
64
+
65
+ Terms
66
+
67
+ The Licensor hereby grants you the right to copy, modify, create derivative
68
+ works, redistribute, and make non-production use of the Licensed Work. The
69
+ Licensor may make an Additional Use Grant, above, permitting limited production use.
70
+
71
+ If your use of the Licensed Work does not comply with the requirements
72
+ currently in effect as described in this License, you must purchase a
73
+ commercial license from the Licensor, its affiliated entities, or authorized
74
+ resellers, or you must refrain from using the Licensed Work.
75
+
76
+ All copies of the original and modified Licensed Work, and derivative works
77
+ of the Licensed Work, are subject to this License. This License applies
78
+ separately for each version of the Licensed Work and the Change Date may vary
79
+ for each version of the Licensed Work released by Licensor.
80
+
81
+ You must conspicuously display this License on each original or modified copy
82
+ of the Licensed Work. If you receive the Licensed Work in original or
83
+ modified form from a third party, the terms and conditions set forth in this
84
+ License apply to your use of that work.
85
+
86
+ Any use of the Licensed Work in violation of this License will automatically
87
+ terminate your rights under this License for the current and all other
88
+ versions of the Licensed Work.
89
+
90
+ This License does not grant you any right in any trademark or logo of
91
+ Licensor or its affiliates (provided that you may use a trademark or logo of
92
+ Licensor as expressly required by this License).
93
+
94
+ TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON
95
+ AN "AS IS" BASIS. LICENSOR HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS,
96
+ EXPRESS OR IMPLIED, INCLUDING (WITHOUT LIMITATION) WARRANTIES OF
97
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND
98
+ TITLE.
package/README.md ADDED
@@ -0,0 +1,112 @@
1
+ # ToolPlex Client
2
+
3
+ [![Visit ToolPlex](https://img.shields.io/badge/ToolPlex.ai-%F0%9F%9A%80-blue?style=flat)](https://toolplex.ai)
4
+ [![Discord](https://img.shields.io/badge/Join%20Discord-5865F2?style=for-the-badge&logo=discord&logoColor=white)](https://discord.gg/KpCjj8ay)
5
+
6
+ This repository contains the official **ToolPlex MCP server** — the npm package that enables AI agents to connect to the ToolPlex platform.
7
+
8
+ 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
13
+
14
+ No complex setup. Just add ToolPlex to your AI client and start automating.
15
+
16
+ ## Features
17
+
18
+ - **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
+ - **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
+ - **Seamless Server Activation** — Install and uninstall MCP servers at any time without restarting your LLM or resetting the session
22
+ - **Workflow Memory** — Successful tasks are saved as playbooks your agent can search, reuse, and adapt later
23
+ - **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
+ - **Full Agent Control** — Use the ToolPlex dashboard to manage server access, shell permissions, file visibility, feedback settings, and more
26
+ - **Local-First Execution** — By default, ToolPlex installs and runs tools on your machine for full speed, privacy, and control
27
+
28
+ ## Quick Setup
29
+
30
+ **Claude Desktop (recommended)**
31
+ 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`:
34
+
35
+ ```json
36
+ {
37
+ "mcpServers": {
38
+ "toolplex-mcp": {
39
+ "command": "npx",
40
+ "args": ["@toolplex/client"],
41
+ "env": {
42
+ "TOOLPLEX_API_KEY": "YOUR_API_KEY_HERE"
43
+ }
44
+ }
45
+ }
46
+ }
47
+ ```
48
+
49
+ Or use any AI chat client that [supports MCP](https://github.com/punkpeye/awesome-mcp-clients).
50
+
51
+ 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
+
53
+ If you *must* include ToolPlex alongside other servers, be sure to **mention `toolplex` by name** when you want to use it. This helps your agent avoid ambiguity when multiple servers are present.
54
+
55
+ ## ToolPlex Usage Guide
56
+
57
+ ToolPlex API users aren't humans — they're AI agents.
58
+
59
+ **You talk to your agent.** Your agent talks to ToolPlex. This allows agents to handle the full lifecycle of tool usage — from discovery and installation to configuration, execution, and playbook use — all without human micromanagement.
60
+
61
+ Ask your AI to activate ToolPlex:
62
+ ```
63
+ > init toolplex
64
+ > start toolplex
65
+ > open toolplex
66
+ ```
67
+
68
+ After initializing ToolPlex, just talk to your agent naturally:
69
+
70
+ ### Discover Available Tools
71
+ ```
72
+ > what tools are currently installed?
73
+ > show me what ToolPlex servers I can use
74
+ ```
75
+
76
+ ### Run Installed Tools
77
+ ```
78
+ > merge these PDFs using the File Merger tool
79
+ > fetch the latest bitcoin price from Yahoo Finance
80
+ > find some tools to generate my weekly dinner menu
81
+ > extract tables from this Excel file using the Excel MCP
82
+ > query our BigQuery sales data for Q2 totals
83
+ ```
84
+
85
+ ### Install New Tools (via search)
86
+ ```
87
+ > find a tool that can automate browser tests
88
+ > is there a server that can visualize data with charts?
89
+ > search for a tool that can read and organize research PDFs
90
+ ```
91
+
92
+ ### Manage Tools
93
+ ```
94
+ > install the google news server
95
+ > uninstall the ffmpeg server
96
+ > reinstall the Playwright automation server
97
+ ```
98
+
99
+ ### Use Playbooks
100
+ ```
101
+ > run my weekly KPI dashboard playbook
102
+ > save this as a playbook
103
+ ```
104
+
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`
111
+
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.
@@ -0,0 +1,35 @@
1
+ import { LLMContext } from '../shared/mcpServerTypes.js';
2
+ import { ClientPermissions, ClientFlags } from './toolplexApi/types.js';
3
+ /**
4
+ * Maintains client context for the ToolPlex server
5
+ */
6
+ export declare class ClientContext {
7
+ private _sessionId;
8
+ private _dev;
9
+ private _apiKey;
10
+ private _clientMode;
11
+ private _llmContext;
12
+ private _clientVersion;
13
+ private _permissions;
14
+ private _flags;
15
+ private _isOrgUser;
16
+ get sessionId(): string;
17
+ set sessionId(id: string);
18
+ get dev(): boolean;
19
+ set dev(isDev: boolean);
20
+ get apiKey(): string;
21
+ set apiKey(key: string);
22
+ get clientMode(): 'standard' | 'restricted';
23
+ set clientMode(mode: 'standard' | 'restricted');
24
+ get llmContext(): LLMContext;
25
+ set llmContext(context: LLMContext);
26
+ get clientVersion(): string;
27
+ set clientVersion(version: string);
28
+ get permissions(): ClientPermissions;
29
+ set permissions(perms: ClientPermissions);
30
+ get flags(): ClientFlags;
31
+ set flags(consts: ClientFlags);
32
+ get isOrgUser(): boolean;
33
+ set isOrgUser(isOrgUser: boolean);
34
+ isInitialized(): boolean;
35
+ }
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Maintains client context for the ToolPlex server
3
+ */
4
+ export class ClientContext {
5
+ constructor() {
6
+ this._sessionId = null;
7
+ this._dev = null;
8
+ this._apiKey = null;
9
+ this._clientMode = null;
10
+ this._llmContext = null;
11
+ this._clientVersion = null;
12
+ this._permissions = null;
13
+ this._flags = null;
14
+ this._isOrgUser = null;
15
+ }
16
+ get sessionId() {
17
+ if (!this._sessionId) {
18
+ throw new Error('Session ID not set - ToolPlex not initialized');
19
+ }
20
+ return this._sessionId;
21
+ }
22
+ set sessionId(id) {
23
+ this._sessionId = id;
24
+ }
25
+ get dev() {
26
+ if (this._dev === null) {
27
+ throw new Error('Dev mode not set - ToolPlex not initialized');
28
+ }
29
+ return this._dev;
30
+ }
31
+ set dev(isDev) {
32
+ this._dev = isDev;
33
+ }
34
+ get apiKey() {
35
+ if (!this._apiKey) {
36
+ throw new Error('API key not set - ToolPlex not initialized');
37
+ }
38
+ return this._apiKey;
39
+ }
40
+ set apiKey(key) {
41
+ this._apiKey = key;
42
+ }
43
+ get clientMode() {
44
+ if (!this._clientMode) {
45
+ throw new Error('Client mode not set - ToolPlex not initialized');
46
+ }
47
+ return this._clientMode;
48
+ }
49
+ set clientMode(mode) {
50
+ this._clientMode = mode;
51
+ }
52
+ get llmContext() {
53
+ if (!this._llmContext) {
54
+ throw new Error('LLM context not set - ToolPlex not initialized');
55
+ }
56
+ return this._llmContext;
57
+ }
58
+ set llmContext(context) {
59
+ this._llmContext = context;
60
+ }
61
+ get clientVersion() {
62
+ if (!this._clientVersion) {
63
+ throw new Error('Client version not set - ToolPlex not initialized');
64
+ }
65
+ return this._clientVersion;
66
+ }
67
+ set clientVersion(version) {
68
+ this._clientVersion = version;
69
+ }
70
+ get permissions() {
71
+ if (!this._permissions) {
72
+ throw new Error('Permissions not set - ToolPlex not initialized');
73
+ }
74
+ return this._permissions;
75
+ }
76
+ set permissions(perms) {
77
+ this._permissions = perms;
78
+ }
79
+ get flags() {
80
+ if (!this._flags) {
81
+ throw new Error('Consts not set - ToolPlex not initialized');
82
+ }
83
+ return this._flags;
84
+ }
85
+ set flags(consts) {
86
+ this._flags = consts;
87
+ }
88
+ get isOrgUser() {
89
+ if (this._isOrgUser === null) {
90
+ throw new Error('Organization user status not set - ToolPlex not initialized');
91
+ }
92
+ return this._isOrgUser;
93
+ }
94
+ set isOrgUser(isOrgUser) {
95
+ this._isOrgUser = isOrgUser;
96
+ }
97
+ isInitialized() {
98
+ return !!(this._sessionId &&
99
+ this._apiKey &&
100
+ this._clientMode &&
101
+ this._llmContext &&
102
+ this._clientVersion &&
103
+ this._permissions &&
104
+ this._flags &&
105
+ this._isOrgUser !== null);
106
+ }
107
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env node
2
+ import { serve } from './toolplexServer.js';
3
+ import dotenv from 'dotenv';
4
+ import { FileLogger } from '../shared/fileLogger.js';
5
+ dotenv.config();
6
+ FileLogger.initialize('mcp-server');
7
+ const isDev = process.env.DEV === 'true';
8
+ const apiKey = process.env.TOOLPLEX_API_KEY;
9
+ const clientMode = process.env.TOOLPLEX_CLIENT_MODE || 'standard';
10
+ const logLevel = process.env.LOG_LEVEL || 'info';
11
+ if (!apiKey) {
12
+ process.exit(1);
13
+ }
14
+ const config = {
15
+ dev: isDev,
16
+ apiKey,
17
+ clientMode,
18
+ logLevel,
19
+ };
20
+ serve(config).catch(() => {
21
+ process.exit(1);
22
+ });
@@ -0,0 +1,18 @@
1
+ import { LogTelemetryRequest } from '../toolplexApi/types.js';
2
+ export declare class TelemetryLogger {
3
+ private eventQueue;
4
+ private flushTimeout;
5
+ private readonly BATCH_SIZE;
6
+ private readonly FLUSH_INTERVAL;
7
+ constructor();
8
+ /**
9
+ * Log a telemetry event
10
+ */
11
+ log(eventType: LogTelemetryRequest['event_type'], data: Partial<Omit<LogTelemetryRequest, 'event_type'>>): Promise<void>;
12
+ private flush;
13
+ private scheduleFlush;
14
+ /**
15
+ * Clean up resources - should be called when shutting down
16
+ */
17
+ dispose(): Promise<void>;
18
+ }
@@ -0,0 +1,54 @@
1
+ import Registry from '../registry.js';
2
+ import { FileLogger } from '../../shared/fileLogger.js';
3
+ const logger = FileLogger;
4
+ export class TelemetryLogger {
5
+ constructor() {
6
+ this.eventQueue = [];
7
+ this.flushTimeout = null;
8
+ this.BATCH_SIZE = 10;
9
+ this.FLUSH_INTERVAL = 30000;
10
+ this.scheduleFlush();
11
+ }
12
+ /**
13
+ * Log a telemetry event
14
+ */
15
+ async log(eventType, data) {
16
+ this.eventQueue.push({ eventType, data });
17
+ if (this.eventQueue.length >= this.BATCH_SIZE) {
18
+ await this.flush();
19
+ }
20
+ }
21
+ async flush() {
22
+ if (this.eventQueue.length === 0)
23
+ return;
24
+ try {
25
+ const apiService = Registry.getToolplexApiService();
26
+ const events = [...this.eventQueue];
27
+ this.eventQueue = [];
28
+ await logger.debug(`Flushing ${events.length} telemetry events`);
29
+ await apiService.logTelemetryEvents(events);
30
+ }
31
+ catch (err) {
32
+ await logger.error(`Error flushing telemetry events: ${err}`);
33
+ this.eventQueue.unshift(...this.eventQueue);
34
+ }
35
+ }
36
+ scheduleFlush() {
37
+ if (this.flushTimeout) {
38
+ clearTimeout(this.flushTimeout);
39
+ }
40
+ this.flushTimeout = setTimeout(async () => {
41
+ await this.flush();
42
+ this.scheduleFlush();
43
+ }, this.FLUSH_INTERVAL);
44
+ }
45
+ /**
46
+ * Clean up resources - should be called when shutting down
47
+ */
48
+ async dispose() {
49
+ if (this.flushTimeout) {
50
+ clearTimeout(this.flushTimeout);
51
+ }
52
+ await this.flush();
53
+ }
54
+ }
@@ -0,0 +1,9 @@
1
+ declare class CallToolObserver {
2
+ private serverToolCalls;
3
+ constructor();
4
+ recordCall(serverId: string, toolName: string): void;
5
+ wasServerCalled(serverId: string): boolean;
6
+ wasToolCalled(serverId: string, toolName: string): boolean;
7
+ clear(): void;
8
+ }
9
+ export default CallToolObserver;
@@ -0,0 +1,25 @@
1
+ class CallToolObserver {
2
+ constructor() {
3
+ this.serverToolCalls = new Map();
4
+ }
5
+ // Record a call to a tool on a server
6
+ recordCall(serverId, toolName) {
7
+ if (!this.serverToolCalls.has(serverId)) {
8
+ this.serverToolCalls.set(serverId, new Set());
9
+ }
10
+ this.serverToolCalls.get(serverId).add(toolName);
11
+ }
12
+ // Check if a server was called at all
13
+ wasServerCalled(serverId) {
14
+ return this.serverToolCalls.has(serverId) && this.serverToolCalls.get(serverId).size > 0;
15
+ }
16
+ // Check if a specific tool was called on a server
17
+ wasToolCalled(serverId, toolName) {
18
+ return this.serverToolCalls.has(serverId) && this.serverToolCalls.get(serverId).has(toolName);
19
+ }
20
+ // Optionally, clear all records (for testing or reset)
21
+ clear() {
22
+ this.serverToolCalls.clear();
23
+ }
24
+ }
25
+ export default CallToolObserver;
@@ -0,0 +1,27 @@
1
+ import CallToolObserver from './callToolObserver.js';
2
+ import InstallObserver from './installObserver.js';
3
+ import { ClientContext } from '../clientContext.js';
4
+ import { SubmitFeedbackParams } from '../../shared/mcpServerTypes.js';
5
+ export declare class FeedbackPolicy {
6
+ private callToolObserver;
7
+ private installObserver;
8
+ private clientContext;
9
+ private blockedMcpServersSet;
10
+ constructor(clientContext: ClientContext, callToolObserver: CallToolObserver, installObserver: InstallObserver);
11
+ /**
12
+ * Validates feedback by checking that:
13
+ * - The referenced server has been used or had an install/uninstall action
14
+ * - The server is not in the blocked servers list
15
+ *
16
+ * For server feedback, verifies that:
17
+ * - The referenced server has been connected to and used or had an install/uninstall action
18
+ * - The server is not blocked
19
+ *
20
+ * For playbook feedback, no validation is currently performed
21
+ * since playbooks can reference other playbooks.
22
+ *
23
+ * @throws Error if feedback references a server that hasn't been used or had an install/uninstall action
24
+ * in the current session or if the server is blocked
25
+ */
26
+ enforceFeedbackPolicy(feedback: SubmitFeedbackParams): void;
27
+ }
@@ -0,0 +1,39 @@
1
+ export class FeedbackPolicy {
2
+ constructor(clientContext, callToolObserver, installObserver) {
3
+ this.callToolObserver = callToolObserver;
4
+ this.installObserver = installObserver;
5
+ this.clientContext = clientContext;
6
+ this.blockedMcpServersSet = new Set(clientContext.flags.blocked_mcp_servers || []);
7
+ }
8
+ /**
9
+ * Validates feedback by checking that:
10
+ * - The referenced server has been used or had an install/uninstall action
11
+ * - The server is not in the blocked servers list
12
+ *
13
+ * For server feedback, verifies that:
14
+ * - The referenced server has been connected to and used or had an install/uninstall action
15
+ * - The server is not blocked
16
+ *
17
+ * For playbook feedback, no validation is currently performed
18
+ * since playbooks can reference other playbooks.
19
+ *
20
+ * @throws Error if feedback references a server that hasn't been used or had an install/uninstall action
21
+ * in the current session or if the server is blocked
22
+ */
23
+ enforceFeedbackPolicy(feedback) {
24
+ const { target_type, target_id } = feedback;
25
+ if (target_type === 'server') {
26
+ // Check if server is blocked
27
+ if (this.blockedMcpServersSet.has(target_id)) {
28
+ throw new Error(`Cannot submit feedback for blocked server "${target_id}"`);
29
+ }
30
+ // For server feedback, verify the server was actually used or had an install/uninstall action
31
+ if (!this.callToolObserver.wasServerCalled(target_id) &&
32
+ !this.installObserver.wasServerInstalled(target_id) &&
33
+ !this.installObserver.wasServerUninstalled(target_id)) {
34
+ throw new Error(`Cannot submit feedback for server "${target_id}" which has not been used or had an install/uninstall action in this session.`);
35
+ }
36
+ }
37
+ // For playbook feedback, we don't validate usage since playbooks can reference other playbooks
38
+ }
39
+ }
@@ -0,0 +1,11 @@
1
+ declare class InstallObserver {
2
+ private serverInstallActions;
3
+ constructor();
4
+ recordInstall(serverId: string): void;
5
+ recordUninstall(serverId: string): void;
6
+ wasServerInstalled(serverId: string): boolean;
7
+ wasServerUninstalled(serverId: string): boolean;
8
+ clear(): void;
9
+ private recordAction;
10
+ }
11
+ export default InstallObserver;
@@ -0,0 +1,35 @@
1
+ class InstallObserver {
2
+ constructor() {
3
+ this.serverInstallActions = new Map();
4
+ }
5
+ // Record an install action on a server
6
+ recordInstall(serverId) {
7
+ this.recordAction(serverId, 'install');
8
+ }
9
+ // Record an uninstall action on a server
10
+ recordUninstall(serverId) {
11
+ this.recordAction(serverId, 'uninstall');
12
+ }
13
+ // Check if a server has been installed
14
+ wasServerInstalled(serverId) {
15
+ return (this.serverInstallActions.has(serverId) &&
16
+ this.serverInstallActions.get(serverId).has('install'));
17
+ }
18
+ // Check if a server has been uninstalled
19
+ wasServerUninstalled(serverId) {
20
+ return (this.serverInstallActions.has(serverId) &&
21
+ this.serverInstallActions.get(serverId).has('uninstall'));
22
+ }
23
+ // Optionally, clear all records (for testing or reset)
24
+ clear() {
25
+ this.serverInstallActions.clear();
26
+ }
27
+ // Private method to record an action
28
+ recordAction(serverId, action) {
29
+ if (!this.serverInstallActions.has(serverId)) {
30
+ this.serverInstallActions.set(serverId, new Set());
31
+ }
32
+ this.serverInstallActions.get(serverId).add(action);
33
+ }
34
+ }
35
+ export default InstallObserver;
@@ -0,0 +1,29 @@
1
+ import CallToolObserver from './callToolObserver.js';
2
+ import { ClientContext } from '../clientContext.js';
3
+ import { SavePlaybookParams } from '../../shared/mcpServerTypes.js';
4
+ export declare class PlaybookPolicy {
5
+ private callToolObserver;
6
+ private clientContext;
7
+ private blockedMcpServersSet;
8
+ constructor(clientContext: ClientContext, callToolObserver: CallToolObserver);
9
+ /**
10
+ * Validates a playbook before saving by checking that:
11
+ * - Referenced servers and tools have been used
12
+ * - No blocked servers are referenced
13
+ * - Private playbooks are only created when enabled
14
+ *
15
+ * For each action with a 'call' property, verifies that:
16
+ * - Any referenced server has been connected to
17
+ * - Any referenced server/tool combination has been executed
18
+ * - The server is not in the blocked servers list
19
+ *
20
+ * @throws Error if a referenced server or tool has not been used in the current session,
21
+ * if a blocked server is referenced, or if trying to create a private playbook when disabled
22
+ */
23
+ enforceSavePlaybookPolicy(playbook: SavePlaybookParams): void;
24
+ /**
25
+ * Validates if playbook usage logging is allowed based on read-only mode
26
+ * @throws Error if read-only mode is enabled
27
+ */
28
+ enforceLogPlaybookUsagePolicy(): void;
29
+ }