@prodisco/k8s-mcp 0.1.4 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +56 -12
- package/dist/server.js +46 -9
- package/dist/server.js.map +1 -1
- package/dist/tools/kubernetes/metadata.d.ts +38 -8
- package/dist/tools/kubernetes/metadata.d.ts.map +1 -1
- package/dist/tools/kubernetes/metadata.js +9 -2
- package/dist/tools/kubernetes/metadata.js.map +1 -1
- package/dist/tools/kubernetes/runSandbox.d.ts +34 -0
- package/dist/tools/kubernetes/runSandbox.d.ts.map +1 -0
- package/dist/tools/kubernetes/runSandbox.js +264 -0
- package/dist/tools/kubernetes/runSandbox.js.map +1 -0
- package/dist/tools/kubernetes/searchTools.d.ts +8 -3
- package/dist/tools/kubernetes/searchTools.d.ts.map +1 -1
- package/dist/tools/kubernetes/searchTools.js +98 -54
- package/dist/tools/kubernetes/searchTools.js.map +1 -1
- package/dist/util/paths.d.ts +5 -0
- package/dist/util/paths.d.ts.map +1 -1
- package/dist/util/paths.js +5 -0
- package/dist/util/paths.js.map +1 -1
- package/package.json +2 -1
- package/dist/tools/api/apiTools.d.ts +0 -6
- package/dist/tools/api/apiTools.d.ts.map +0 -1
- package/dist/tools/api/apiTools.js +0 -132
- package/dist/tools/api/apiTools.js.map +0 -1
- package/dist/tools/kubernetes/searchTools-old.d.ts +0 -35
- package/dist/tools/kubernetes/searchTools-old.d.ts.map +0 -1
- package/dist/tools/kubernetes/searchTools-old.js +0 -414
- package/dist/tools/kubernetes/searchTools-old.js.map +0 -1
- package/dist/tools/kubernetes/searchTools-v2.d.ts +0 -35
- package/dist/tools/kubernetes/searchTools-v2.d.ts.map +0 -1
- package/dist/tools/kubernetes/searchTools-v2.js +0 -269
- package/dist/tools/kubernetes/searchTools-v2.js.map +0 -1
- package/dist/tools/kubernetes/typeDefinitions.d.ts +0 -24
- package/dist/tools/kubernetes/typeDefinitions.d.ts.map +0 -1
- package/dist/tools/kubernetes/typeDefinitions.js +0 -518
- package/dist/tools/kubernetes/typeDefinitions.js.map +0 -1
- package/dist/util/apiClient.d.ts +0 -20
- package/dist/util/apiClient.d.ts.map +0 -1
- package/dist/util/apiClient.js +0 -90
- package/dist/util/apiClient.js.map +0 -1
- package/dist/util/k8sClient.d.ts +0 -38
- package/dist/util/k8sClient.d.ts.map +0 -1
- package/dist/util/k8sClient.js +0 -85
- package/dist/util/k8sClient.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# ProDisco (Progressive Disclosure Kubernetes MCP Server)
|
|
2
2
|
|
|
3
|
-
ProDisco gives AI agents **Kubernetes access + Prometheus metrics analysis** through
|
|
3
|
+
ProDisco gives AI agents **Kubernetes access + Prometheus metrics analysis** through two unified tools. It follows Anthropic's [Progressive Disclosure](https://www.anthropic.com/engineering/code-execution-with-mcp) pattern: the MCP server exposes search tools which surface API methods, agents discover them to write code, execute it in a sandbox, and only the final console output returns to the agent.
|
|
4
4
|
|
|
5
|
-
**
|
|
6
|
-
- **
|
|
7
|
-
- **
|
|
5
|
+
**Two tools:**
|
|
6
|
+
- **kubernetes.searchTools** - Discover API methods, type definitions, cached scripts, and Prometheus methods
|
|
7
|
+
- **kubernetes.runSandbox** - Execute TypeScript code in a sandboxed VM environment
|
|
8
8
|
|
|
9
9
|
## Why Progressive Disclosure Matters
|
|
10
10
|
|
|
@@ -58,6 +58,8 @@ claude mcp remove ProDisco
|
|
|
58
58
|
| `K8S_CONTEXT` | No | Kubernetes context (defaults to current context) |
|
|
59
59
|
| `PROMETHEUS_URL` | No | Prometheus server URL for metrics queries |
|
|
60
60
|
|
|
61
|
+
> **Important:** These environment variables must be set where the MCP server runs. The `runSandbox` tool executes code within the MCP server process, which needs access to your kubeconfig and/or Prometheus endpoint.
|
|
62
|
+
|
|
61
63
|
> **Tip:** If you're using a kind cluster for local testing, you can port-forward to Prometheus:
|
|
62
64
|
> ```bash
|
|
63
65
|
> kubectl port-forward -n monitoring svc/prometheus-server 9090:80
|
|
@@ -77,17 +79,63 @@ claude mcp add --transport stdio prodisco -- node dist/server.js
|
|
|
77
79
|
claude mcp remove prodisco # remove when you're done
|
|
78
80
|
```
|
|
79
81
|
|
|
80
|
-
###
|
|
82
|
+
### Startup Options
|
|
81
83
|
|
|
82
|
-
|
|
84
|
+
| Flag | Description |
|
|
85
|
+
|------|-------------|
|
|
86
|
+
| `--clear-cache` | Clear the scripts cache before starting |
|
|
83
87
|
|
|
84
|
-
|
|
88
|
+
Example:
|
|
89
|
+
```bash
|
|
90
|
+
node dist/server.js --clear-cache
|
|
91
|
+
```
|
|
85
92
|
|
|
86
93
|
---
|
|
87
94
|
|
|
88
95
|
## Available Tools
|
|
89
96
|
|
|
90
|
-
ProDisco exposes
|
|
97
|
+
ProDisco exposes two tools:
|
|
98
|
+
|
|
99
|
+
### kubernetes.runSandbox
|
|
100
|
+
|
|
101
|
+
Execute TypeScript code in a sandboxed VM environment for Kubernetes and Prometheus operations.
|
|
102
|
+
|
|
103
|
+
**Input:**
|
|
104
|
+
```typescript
|
|
105
|
+
{
|
|
106
|
+
// Provide one of:
|
|
107
|
+
code?: string; // TypeScript code to execute directly
|
|
108
|
+
cached?: string; // Name of a cached script to run (from searchTools results)
|
|
109
|
+
|
|
110
|
+
timeout?: number; // Execution timeout in ms (default: 30000, max: 120000)
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
**Sandbox Environment:**
|
|
115
|
+
- `k8s` - Full `@kubernetes/client-node` library
|
|
116
|
+
- `kc` - Pre-configured KubeConfig instance
|
|
117
|
+
- `console` - Captured output (log, error, warn, info)
|
|
118
|
+
- `require()` - Whitelisted modules: `@kubernetes/client-node`, `prometheus-query`
|
|
119
|
+
- `process.env` - Environment variables (PROMETHEUS_URL, etc.)
|
|
120
|
+
|
|
121
|
+
**Examples:**
|
|
122
|
+
```typescript
|
|
123
|
+
// Execute code directly
|
|
124
|
+
{
|
|
125
|
+
code: `
|
|
126
|
+
const api = kc.makeApiClient(k8s.CoreV1Api);
|
|
127
|
+
const pods = await api.listNamespacedPod({ namespace: 'default' });
|
|
128
|
+
console.log(\`Found \${pods.items.length} pods\`);
|
|
129
|
+
`
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Run a cached script by name
|
|
133
|
+
{ cached: "script-2025-01-01T12-00-00-abc123.ts" }
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### kubernetes.searchTools
|
|
137
|
+
|
|
138
|
+
A unified search interface with four modes:
|
|
91
139
|
|
|
92
140
|
| Mode | Purpose | Example |
|
|
93
141
|
|------|---------|---------|
|
|
@@ -98,10 +146,6 @@ ProDisco exposes a single unified tool with four modes:
|
|
|
98
146
|
|
|
99
147
|
For comprehensive documentation including architecture details and example workflows, see [docs/search-tools.md](docs/search-tools.md).
|
|
100
148
|
|
|
101
|
-
### kubernetes.searchTools
|
|
102
|
-
|
|
103
|
-
A unified search interface for Kubernetes operations and metrics analysis.
|
|
104
|
-
|
|
105
149
|
**Input:**
|
|
106
150
|
```typescript
|
|
107
151
|
{
|
package/dist/server.js
CHANGED
|
@@ -3,15 +3,16 @@ import { createRequire } from 'node:module';
|
|
|
3
3
|
import * as path from 'node:path';
|
|
4
4
|
import { fileURLToPath } from 'node:url';
|
|
5
5
|
import * as fs from 'node:fs';
|
|
6
|
-
import * as os from 'node:os';
|
|
7
6
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
8
7
|
import { logger } from './util/logger.js';
|
|
9
8
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
10
9
|
const require = createRequire(import.meta.url);
|
|
11
10
|
const pkg = require('../package.json');
|
|
12
11
|
import { searchToolsTool, warmupSearchIndex, shutdownSearchIndex } from './tools/kubernetes/searchTools.js';
|
|
12
|
+
import { runSandboxTool } from './tools/kubernetes/runSandbox.js';
|
|
13
13
|
import { PUBLIC_GENERATED_ROOT_PATH_WITH_SLASH, listGeneratedFiles, readGeneratedFile, } from './resources/filesystem.js';
|
|
14
14
|
import { probeClusterConnectivity } from './kube/client.js';
|
|
15
|
+
import { SCRIPTS_CACHE_DIR } from './util/paths.js';
|
|
15
16
|
const __filename = fileURLToPath(import.meta.url);
|
|
16
17
|
const __dirname = path.dirname(__filename);
|
|
17
18
|
const GENERATED_DIR = path.resolve(__dirname, 'tools/kubernetes');
|
|
@@ -19,10 +20,10 @@ const server = new McpServer({
|
|
|
19
20
|
name: 'kubernetes-mcp',
|
|
20
21
|
version: typeof pkg.version === 'string' ? pkg.version : '0.0.0',
|
|
21
22
|
}, {
|
|
22
|
-
instructions: 'Kubernetes operations via Progressive Disclosure.
|
|
23
|
-
'
|
|
24
|
-
'
|
|
25
|
-
'The
|
|
23
|
+
instructions: 'Kubernetes and Prometheus operations via Progressive Disclosure. ' +
|
|
24
|
+
'Use kubernetes.searchTools to discover available APIs. ' +
|
|
25
|
+
'Use kubernetes.runSandbox to execute TypeScript scripts directly. ' +
|
|
26
|
+
'The sandbox provides: k8s, kc (pre-configured KubeConfig), console, and require("prometheus-query").',
|
|
26
27
|
});
|
|
27
28
|
// Expose generated TypeScript files as MCP resources using ResourceTemplate
|
|
28
29
|
import { ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
@@ -157,7 +158,44 @@ server.registerTool(searchToolsTool.name, {
|
|
|
157
158
|
};
|
|
158
159
|
}
|
|
159
160
|
});
|
|
161
|
+
// Register kubernetes.runSandbox tool for executing scripts in a sandboxed environment
|
|
162
|
+
server.registerTool(runSandboxTool.name, {
|
|
163
|
+
title: 'Kubernetes Run Sandbox',
|
|
164
|
+
description: runSandboxTool.description,
|
|
165
|
+
inputSchema: runSandboxTool.schema,
|
|
166
|
+
}, async (args) => {
|
|
167
|
+
const parsedArgs = await runSandboxTool.schema.parseAsync(args);
|
|
168
|
+
const result = await runSandboxTool.execute(parsedArgs);
|
|
169
|
+
// Build the output message
|
|
170
|
+
const cachedInfo = result.cachedScript ? ` [cached: ${result.cachedScript}]` : '';
|
|
171
|
+
const successMsg = `Execution successful${cachedInfo} (${result.executionTime}ms)\n\nOutput:\n${result.output}`;
|
|
172
|
+
const failMsg = `Execution failed${cachedInfo} (${result.executionTime}ms)\n\nError: ${result.error}\n\nOutput:\n${result.output}`;
|
|
173
|
+
return {
|
|
174
|
+
content: [
|
|
175
|
+
{
|
|
176
|
+
type: 'text',
|
|
177
|
+
text: result.success ? successMsg : failMsg,
|
|
178
|
+
},
|
|
179
|
+
],
|
|
180
|
+
structuredContent: result,
|
|
181
|
+
};
|
|
182
|
+
});
|
|
160
183
|
async function main() {
|
|
184
|
+
// Parse command line arguments
|
|
185
|
+
const args = process.argv.slice(2);
|
|
186
|
+
const clearCache = args.includes('--clear-cache');
|
|
187
|
+
// Handle --clear-cache flag
|
|
188
|
+
if (clearCache) {
|
|
189
|
+
logger.info('Clearing scripts cache...');
|
|
190
|
+
try {
|
|
191
|
+
await fs.promises.rm(SCRIPTS_CACHE_DIR, { recursive: true, force: true });
|
|
192
|
+
logger.info('Scripts cache cleared');
|
|
193
|
+
}
|
|
194
|
+
catch (error) {
|
|
195
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
196
|
+
logger.warn(`Failed to clear cache: ${message}`);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
161
199
|
// Probe cluster connectivity before starting the server
|
|
162
200
|
// This ensures we fail fast if the cluster is not reachable
|
|
163
201
|
logger.info('Probing Kubernetes cluster connectivity...');
|
|
@@ -170,10 +208,9 @@ async function main() {
|
|
|
170
208
|
logger.error(`Failed to connect to Kubernetes cluster: ${message}`);
|
|
171
209
|
throw new Error(`Kubernetes cluster is not accessible: ${message}`);
|
|
172
210
|
}
|
|
173
|
-
// Ensure
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
logger.info(`Scripts directory: ${scriptsDir}`);
|
|
211
|
+
// Ensure scripts cache directory exists (server-controlled location)
|
|
212
|
+
await fs.promises.mkdir(SCRIPTS_CACHE_DIR, { recursive: true });
|
|
213
|
+
logger.info(`Scripts cache directory: ${SCRIPTS_CACHE_DIR}`);
|
|
177
214
|
// Pre-warm the Orama search index to avoid delay on first search
|
|
178
215
|
await warmupSearchIndex();
|
|
179
216
|
const transport = new StdioServerTransport();
|
package/dist/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Kubernetes tool metadata
|
|
3
3
|
*
|
|
4
|
-
* Exports
|
|
5
|
-
*
|
|
4
|
+
* Exports:
|
|
5
|
+
* - searchTools: API method discovery (mode: 'methods'), type definitions (mode: 'types'),
|
|
6
|
+
* script search (mode: 'scripts'), and Prometheus methods (mode: 'prometheus')
|
|
7
|
+
* - runSandbox: Execute TypeScript scripts in a sandboxed environment
|
|
6
8
|
*/
|
|
7
|
-
export declare const kubernetesToolMetadata: {
|
|
9
|
+
export declare const kubernetesToolMetadata: ({
|
|
8
10
|
tool: import("../types.js").ToolDefinition<{
|
|
9
11
|
mode: "methods";
|
|
10
12
|
summary: string;
|
|
@@ -53,7 +55,6 @@ export declare const kubernetesToolMetadata: {
|
|
|
53
55
|
cachedScripts: string[];
|
|
54
56
|
relevantScripts: {
|
|
55
57
|
filename: string;
|
|
56
|
-
filePath: string;
|
|
57
58
|
description: string;
|
|
58
59
|
apiClasses: string[];
|
|
59
60
|
}[];
|
|
@@ -82,7 +83,6 @@ export declare const kubernetesToolMetadata: {
|
|
|
82
83
|
summary: string;
|
|
83
84
|
scripts: {
|
|
84
85
|
filename: string;
|
|
85
|
-
filePath: string;
|
|
86
86
|
description: string;
|
|
87
87
|
apiClasses: string[];
|
|
88
88
|
}[];
|
|
@@ -217,7 +217,7 @@ export declare const kubernetesToolMetadata: {
|
|
|
217
217
|
scope: "namespaced" | "cluster" | "all";
|
|
218
218
|
category: "all" | "query" | "metadata" | "alerts" | "metrics";
|
|
219
219
|
types?: string[] | undefined;
|
|
220
|
-
mode?: "
|
|
220
|
+
mode?: "scripts" | "methods" | "types" | "prometheus" | undefined;
|
|
221
221
|
resourceType?: string | undefined;
|
|
222
222
|
action?: string | undefined;
|
|
223
223
|
exclude?: {
|
|
@@ -231,7 +231,7 @@ export declare const kubernetesToolMetadata: {
|
|
|
231
231
|
offset?: number | undefined;
|
|
232
232
|
}, {
|
|
233
233
|
types?: string[] | undefined;
|
|
234
|
-
mode?: "
|
|
234
|
+
mode?: "scripts" | "methods" | "types" | "prometheus" | undefined;
|
|
235
235
|
resourceType?: string | undefined;
|
|
236
236
|
action?: string | undefined;
|
|
237
237
|
scope?: "namespaced" | "cluster" | "all" | undefined;
|
|
@@ -247,5 +247,35 @@ export declare const kubernetesToolMetadata: {
|
|
|
247
247
|
offset?: number | undefined;
|
|
248
248
|
}>>;
|
|
249
249
|
sourceModulePath: string;
|
|
250
|
-
}
|
|
250
|
+
} | {
|
|
251
|
+
tool: import("../types.js").ToolDefinition<{
|
|
252
|
+
success: boolean;
|
|
253
|
+
output: string;
|
|
254
|
+
returnValue?: unknown;
|
|
255
|
+
error?: string;
|
|
256
|
+
executionTime: number;
|
|
257
|
+
cachedScript?: string;
|
|
258
|
+
}, import("zod").ZodEffects<import("zod").ZodObject<{
|
|
259
|
+
code: import("zod").ZodOptional<import("zod").ZodString>;
|
|
260
|
+
cached: import("zod").ZodOptional<import("zod").ZodString>;
|
|
261
|
+
timeout: import("zod").ZodOptional<import("zod").ZodDefault<import("zod").ZodNumber>>;
|
|
262
|
+
}, "strip", import("zod").ZodTypeAny, {
|
|
263
|
+
code?: string | undefined;
|
|
264
|
+
cached?: string | undefined;
|
|
265
|
+
timeout?: number | undefined;
|
|
266
|
+
}, {
|
|
267
|
+
code?: string | undefined;
|
|
268
|
+
cached?: string | undefined;
|
|
269
|
+
timeout?: number | undefined;
|
|
270
|
+
}>, {
|
|
271
|
+
code?: string | undefined;
|
|
272
|
+
cached?: string | undefined;
|
|
273
|
+
timeout?: number | undefined;
|
|
274
|
+
}, {
|
|
275
|
+
code?: string | undefined;
|
|
276
|
+
cached?: string | undefined;
|
|
277
|
+
timeout?: number | undefined;
|
|
278
|
+
}>>;
|
|
279
|
+
sourceModulePath: string;
|
|
280
|
+
})[];
|
|
251
281
|
//# sourceMappingURL=metadata.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"metadata.d.ts","sourceRoot":"","sources":["../../../src/tools/kubernetes/metadata.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"metadata.d.ts","sourceRoot":"","sources":["../../../src/tools/kubernetes/metadata.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;2BAU0qH,CAAC;;;;;;;;;+BAAmP,CAAC;4BAAkB,CAAC;;;;;;;;;;;;;;qBAAuR,CAAC;sBAAoB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2BAAgzD,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2BAAD,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IADljM,CAAC"}
|
|
@@ -1,14 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Kubernetes tool metadata
|
|
3
3
|
*
|
|
4
|
-
* Exports
|
|
5
|
-
*
|
|
4
|
+
* Exports:
|
|
5
|
+
* - searchTools: API method discovery (mode: 'methods'), type definitions (mode: 'types'),
|
|
6
|
+
* script search (mode: 'scripts'), and Prometheus methods (mode: 'prometheus')
|
|
7
|
+
* - runSandbox: Execute TypeScript scripts in a sandboxed environment
|
|
6
8
|
*/
|
|
7
9
|
import { searchToolsTool } from './searchTools.js';
|
|
10
|
+
import { runSandboxTool } from './runSandbox.js';
|
|
8
11
|
export const kubernetesToolMetadata = [
|
|
9
12
|
{
|
|
10
13
|
tool: searchToolsTool,
|
|
11
14
|
sourceModulePath: './searchTools.ts',
|
|
12
15
|
},
|
|
16
|
+
{
|
|
17
|
+
tool: runSandboxTool,
|
|
18
|
+
sourceModulePath: './runSandbox.ts',
|
|
19
|
+
},
|
|
13
20
|
];
|
|
14
21
|
//# sourceMappingURL=metadata.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"metadata.js","sourceRoot":"","sources":["../../../src/tools/kubernetes/metadata.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"metadata.js","sourceRoot":"","sources":["../../../src/tools/kubernetes/metadata.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEjD,MAAM,CAAC,MAAM,sBAAsB,GAAG;IACpC;QACE,IAAI,EAAE,eAAe;QACrB,gBAAgB,EAAE,kBAAkB;KACrC;IACD;QACE,IAAI,EAAE,cAAc;QACpB,gBAAgB,EAAE,iBAAiB;KACpC;CACF,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { ToolDefinition } from '../types.js';
|
|
3
|
+
declare const RunSandboxInputSchema: z.ZodEffects<z.ZodObject<{
|
|
4
|
+
code: z.ZodOptional<z.ZodString>;
|
|
5
|
+
cached: z.ZodOptional<z.ZodString>;
|
|
6
|
+
timeout: z.ZodOptional<z.ZodDefault<z.ZodNumber>>;
|
|
7
|
+
}, "strip", z.ZodTypeAny, {
|
|
8
|
+
code?: string | undefined;
|
|
9
|
+
cached?: string | undefined;
|
|
10
|
+
timeout?: number | undefined;
|
|
11
|
+
}, {
|
|
12
|
+
code?: string | undefined;
|
|
13
|
+
cached?: string | undefined;
|
|
14
|
+
timeout?: number | undefined;
|
|
15
|
+
}>, {
|
|
16
|
+
code?: string | undefined;
|
|
17
|
+
cached?: string | undefined;
|
|
18
|
+
timeout?: number | undefined;
|
|
19
|
+
}, {
|
|
20
|
+
code?: string | undefined;
|
|
21
|
+
cached?: string | undefined;
|
|
22
|
+
timeout?: number | undefined;
|
|
23
|
+
}>;
|
|
24
|
+
type RunSandboxResult = {
|
|
25
|
+
success: boolean;
|
|
26
|
+
output: string;
|
|
27
|
+
returnValue?: unknown;
|
|
28
|
+
error?: string;
|
|
29
|
+
executionTime: number;
|
|
30
|
+
cachedScript?: string;
|
|
31
|
+
};
|
|
32
|
+
export declare const runSandboxTool: ToolDefinition<RunSandboxResult, typeof RunSandboxInputSchema>;
|
|
33
|
+
export {};
|
|
34
|
+
//# sourceMappingURL=runSandbox.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runSandbox.d.ts","sourceRoot":"","sources":["../../../src/tools/kubernetes/runSandbox.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAQxB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAmFlD,QAAA,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;EAU1B,CAAC;AAEF,KAAK,gBAAgB,GAAG;IACtB,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC;AA4CF,eAAO,MAAM,cAAc,EAAE,cAAc,CAAC,gBAAgB,EAAE,OAAO,qBAAqB,CA8JzF,CAAC"}
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import vm from 'node:vm';
|
|
3
|
+
import { createHash } from 'node:crypto';
|
|
4
|
+
import { mkdirSync, writeFileSync, existsSync, readdirSync, readFileSync } from 'node:fs';
|
|
5
|
+
import { join } from 'node:path';
|
|
6
|
+
import { transform } from 'esbuild';
|
|
7
|
+
import * as k8s from '@kubernetes/client-node';
|
|
8
|
+
import * as prometheusQuery from 'prometheus-query';
|
|
9
|
+
import { SCRIPTS_CACHE_DIR } from '../../util/paths.js';
|
|
10
|
+
import { searchToolsService } from './searchTools.js';
|
|
11
|
+
// ============================================================================
|
|
12
|
+
// Simple Mutex for Concurrent Script Caching
|
|
13
|
+
// ============================================================================
|
|
14
|
+
/**
|
|
15
|
+
* Simple async mutex to prevent race conditions during script caching.
|
|
16
|
+
* Ensures only one cacheAndIndex operation runs at a time.
|
|
17
|
+
*/
|
|
18
|
+
class CacheMutex {
|
|
19
|
+
locked = false;
|
|
20
|
+
queue = [];
|
|
21
|
+
async acquire() {
|
|
22
|
+
if (!this.locked) {
|
|
23
|
+
this.locked = true;
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
return new Promise((resolve) => {
|
|
27
|
+
this.queue.push(resolve);
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
release() {
|
|
31
|
+
const next = this.queue.shift();
|
|
32
|
+
if (next) {
|
|
33
|
+
next();
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
this.locked = false;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
const cacheMutex = new CacheMutex();
|
|
41
|
+
/**
|
|
42
|
+
* Cache a successfully executed script to disk and index it.
|
|
43
|
+
* Uses a mutex to handle concurrent requests safely.
|
|
44
|
+
* Uses a hash of the code to create a unique filename, avoiding duplicates.
|
|
45
|
+
*/
|
|
46
|
+
async function cacheAndIndexScript(code) {
|
|
47
|
+
await cacheMutex.acquire();
|
|
48
|
+
try {
|
|
49
|
+
// Ensure cache directory exists
|
|
50
|
+
if (!existsSync(SCRIPTS_CACHE_DIR)) {
|
|
51
|
+
mkdirSync(SCRIPTS_CACHE_DIR, { recursive: true });
|
|
52
|
+
}
|
|
53
|
+
// Create a hash-based filename to deduplicate by content
|
|
54
|
+
const hash = createHash('sha256').update(code).digest('hex').slice(0, 12);
|
|
55
|
+
// Check if any script with this hash already exists (regardless of timestamp)
|
|
56
|
+
const existingFiles = readdirSync(SCRIPTS_CACHE_DIR);
|
|
57
|
+
const existingScript = existingFiles.find(f => f.includes(hash) && f.endsWith('.ts'));
|
|
58
|
+
if (existingScript) {
|
|
59
|
+
// Script with same content already cached, nothing to do
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
// Create new script file with timestamp and hash
|
|
63
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
|
|
64
|
+
const filename = `script-${timestamp}-${hash}.ts`;
|
|
65
|
+
const filepath = join(SCRIPTS_CACHE_DIR, filename);
|
|
66
|
+
// Add a header comment with execution timestamp
|
|
67
|
+
const header = `// Executed via runSandbox at ${new Date().toISOString()}\n`;
|
|
68
|
+
writeFileSync(filepath, header + code, 'utf-8');
|
|
69
|
+
// Index immediately so it's searchable
|
|
70
|
+
await searchToolsService.indexScriptImmediately(filepath);
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
// Silently ignore caching errors - don't fail the execution
|
|
74
|
+
}
|
|
75
|
+
finally {
|
|
76
|
+
cacheMutex.release();
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
const RunSandboxInputSchema = z.object({
|
|
80
|
+
code: z.string().optional()
|
|
81
|
+
.describe('TypeScript code to execute (required if "cached" is not provided)'),
|
|
82
|
+
cached: z.string().optional()
|
|
83
|
+
.describe('Name of a cached script to execute (from searchTools results). Can be filename or partial match.'),
|
|
84
|
+
timeout: z.number().int().positive().max(120000).default(30000).optional()
|
|
85
|
+
.describe('Execution timeout in milliseconds (default: 30000, max: 120000)'),
|
|
86
|
+
}).refine((data) => data.code !== undefined || data.cached !== undefined, { message: 'Either "code" or "cached" must be provided' });
|
|
87
|
+
/**
|
|
88
|
+
* Find a cached script by name (exact or partial match).
|
|
89
|
+
* Returns the full path and filename if found, or null if not found.
|
|
90
|
+
*/
|
|
91
|
+
function findCachedScript(scriptName) {
|
|
92
|
+
if (!existsSync(SCRIPTS_CACHE_DIR)) {
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
const files = readdirSync(SCRIPTS_CACHE_DIR).filter(f => f.endsWith('.ts'));
|
|
96
|
+
// Try exact match first (with or without .ts extension)
|
|
97
|
+
const exactName = scriptName.endsWith('.ts') ? scriptName : `${scriptName}.ts`;
|
|
98
|
+
if (files.includes(exactName)) {
|
|
99
|
+
return { path: join(SCRIPTS_CACHE_DIR, exactName), filename: exactName };
|
|
100
|
+
}
|
|
101
|
+
// Try partial match (script name contains the search term)
|
|
102
|
+
const partialMatch = files.find(f => f.toLowerCase().includes(scriptName.toLowerCase()));
|
|
103
|
+
if (partialMatch) {
|
|
104
|
+
return { path: join(SCRIPTS_CACHE_DIR, partialMatch), filename: partialMatch };
|
|
105
|
+
}
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Read and return the code from a cached script file.
|
|
110
|
+
* Strips the auto-generated header comment if present.
|
|
111
|
+
*/
|
|
112
|
+
function readCachedScript(filePath) {
|
|
113
|
+
const content = readFileSync(filePath, 'utf-8');
|
|
114
|
+
// Strip the auto-generated header comment (first line if it starts with "// Executed via runSandbox")
|
|
115
|
+
const lines = content.split('\n');
|
|
116
|
+
if (lines[0]?.startsWith('// Executed via runSandbox')) {
|
|
117
|
+
return lines.slice(1).join('\n').trim();
|
|
118
|
+
}
|
|
119
|
+
return content;
|
|
120
|
+
}
|
|
121
|
+
export const runSandboxTool = {
|
|
122
|
+
name: 'kubernetes.runSandbox',
|
|
123
|
+
description: 'Execute TypeScript code in a sandboxed environment for Kubernetes and Prometheus operations. ' +
|
|
124
|
+
'TWO MODES: ' +
|
|
125
|
+
'(1) code: Provide TypeScript code directly. Start with a descriptive comment for indexing. ' +
|
|
126
|
+
'(2) cached: Run a previously cached script by name (from searchTools results). ' +
|
|
127
|
+
'The sandbox provides: k8s, kc (pre-configured KubeConfig), console, process.env, require("prometheus-query"). ' +
|
|
128
|
+
'Use searchTools first to discover APIs and find cached scripts.',
|
|
129
|
+
schema: RunSandboxInputSchema,
|
|
130
|
+
async execute(input) {
|
|
131
|
+
const { code: inputCode, cached, timeout = 30000 } = input;
|
|
132
|
+
const startTime = Date.now();
|
|
133
|
+
const outputLines = [];
|
|
134
|
+
// Resolve the code to execute
|
|
135
|
+
let code;
|
|
136
|
+
let cachedScriptName;
|
|
137
|
+
if (cached) {
|
|
138
|
+
// Find and load the cached script
|
|
139
|
+
const found = findCachedScript(cached);
|
|
140
|
+
if (!found) {
|
|
141
|
+
return {
|
|
142
|
+
success: false,
|
|
143
|
+
output: '',
|
|
144
|
+
error: `Cached script "${cached}" not found. Use searchTools with mode: "scripts" to list available scripts.`,
|
|
145
|
+
executionTime: Date.now() - startTime,
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
code = readCachedScript(found.path);
|
|
149
|
+
cachedScriptName = found.filename;
|
|
150
|
+
}
|
|
151
|
+
else if (inputCode) {
|
|
152
|
+
code = inputCode;
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
return {
|
|
156
|
+
success: false,
|
|
157
|
+
output: '',
|
|
158
|
+
error: 'Either "code" or "cached" must be provided',
|
|
159
|
+
executionTime: Date.now() - startTime,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
try {
|
|
163
|
+
// 1. Wrap code in async IIFE BEFORE transforming so esbuild sees await inside a function
|
|
164
|
+
const wrappedTs = `(async () => {\n${code}\n})()`;
|
|
165
|
+
// 2. Transform TypeScript to JavaScript
|
|
166
|
+
const { code: jsCode } = await transform(wrappedTs, {
|
|
167
|
+
loader: 'ts',
|
|
168
|
+
format: 'cjs', // CommonJS for vm compatibility
|
|
169
|
+
target: 'es2022',
|
|
170
|
+
});
|
|
171
|
+
// 3. Create sandbox context with full capabilities
|
|
172
|
+
const kc = new k8s.KubeConfig();
|
|
173
|
+
kc.loadFromDefault();
|
|
174
|
+
const sandbox = {
|
|
175
|
+
console: {
|
|
176
|
+
log: (...args) => outputLines.push(args.map(String).join(' ')),
|
|
177
|
+
error: (...args) => outputLines.push('[ERROR] ' + args.map(String).join(' ')),
|
|
178
|
+
warn: (...args) => outputLines.push('[WARN] ' + args.map(String).join(' ')),
|
|
179
|
+
info: (...args) => outputLines.push('[INFO] ' + args.map(String).join(' ')),
|
|
180
|
+
},
|
|
181
|
+
// Kubernetes (pre-configured for convenience)
|
|
182
|
+
k8s, // Full @kubernetes/client-node library
|
|
183
|
+
kc, // Pre-configured KubeConfig
|
|
184
|
+
// Module loading (for all libraries including prometheus-query)
|
|
185
|
+
require: (mod) => {
|
|
186
|
+
if (mod === '@kubernetes/client-node')
|
|
187
|
+
return k8s;
|
|
188
|
+
if (mod === 'prometheus-query')
|
|
189
|
+
return prometheusQuery;
|
|
190
|
+
throw new Error(`Module '${mod}' not available in sandbox`);
|
|
191
|
+
},
|
|
192
|
+
// Environment & globals
|
|
193
|
+
process: { env: process.env }, // Environment access (PROMETHEUS_URL, etc.)
|
|
194
|
+
setTimeout,
|
|
195
|
+
setInterval,
|
|
196
|
+
clearTimeout,
|
|
197
|
+
clearInterval,
|
|
198
|
+
Promise,
|
|
199
|
+
JSON,
|
|
200
|
+
Buffer,
|
|
201
|
+
Date,
|
|
202
|
+
Math,
|
|
203
|
+
Array,
|
|
204
|
+
Object,
|
|
205
|
+
String,
|
|
206
|
+
Number,
|
|
207
|
+
Boolean,
|
|
208
|
+
Error,
|
|
209
|
+
};
|
|
210
|
+
// 4. Create a promise that will be resolved when the async code completes
|
|
211
|
+
let resolveResult;
|
|
212
|
+
let rejectResult;
|
|
213
|
+
const resultPromise = new Promise((resolve, reject) => {
|
|
214
|
+
resolveResult = resolve;
|
|
215
|
+
rejectResult = reject;
|
|
216
|
+
});
|
|
217
|
+
// Add the resolver to sandbox context
|
|
218
|
+
sandbox.__resolve__ = resolveResult;
|
|
219
|
+
sandbox.__reject__ = rejectResult;
|
|
220
|
+
const context = vm.createContext(sandbox);
|
|
221
|
+
// 5. Wrap the transformed code to capture completion/errors
|
|
222
|
+
// esbuild adds a trailing semicolon, so we need to remove it before adding .then()
|
|
223
|
+
const trimmedJsCode = jsCode.trim().replace(/;$/, '');
|
|
224
|
+
const finalCode = `
|
|
225
|
+
${trimmedJsCode}
|
|
226
|
+
.then(() => __resolve__(undefined))
|
|
227
|
+
.catch((e) => __reject__(e));
|
|
228
|
+
`;
|
|
229
|
+
// 6. Execute in sandbox
|
|
230
|
+
const script = new vm.Script(finalCode, {
|
|
231
|
+
filename: 'sandbox-script.js',
|
|
232
|
+
});
|
|
233
|
+
// Start execution (returns immediately, async work continues)
|
|
234
|
+
script.runInContext(context);
|
|
235
|
+
// Wait for the async code to complete with timeout
|
|
236
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
237
|
+
setTimeout(() => reject(new Error('Script execution timed out')), timeout);
|
|
238
|
+
});
|
|
239
|
+
await Promise.race([resultPromise, timeoutPromise]);
|
|
240
|
+
// Cache successful scripts for searchability and index immediately
|
|
241
|
+
// Skip caching if we're running a cached script (it's already cached)
|
|
242
|
+
if (!cachedScriptName) {
|
|
243
|
+
await cacheAndIndexScript(code);
|
|
244
|
+
}
|
|
245
|
+
return {
|
|
246
|
+
success: true,
|
|
247
|
+
output: outputLines.join('\n'),
|
|
248
|
+
returnValue: undefined,
|
|
249
|
+
executionTime: Date.now() - startTime,
|
|
250
|
+
cachedScript: cachedScriptName,
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
catch (error) {
|
|
254
|
+
return {
|
|
255
|
+
success: false,
|
|
256
|
+
output: outputLines.join('\n'),
|
|
257
|
+
error: error instanceof Error ? error.message : String(error),
|
|
258
|
+
executionTime: Date.now() - startTime,
|
|
259
|
+
cachedScript: cachedScriptName,
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
},
|
|
263
|
+
};
|
|
264
|
+
//# sourceMappingURL=runSandbox.js.map
|