kubeview-mcp 1.9.0 → 1.9.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 (131) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/bin/kubeview-mcp.js +19 -2
  3. package/dist/src/version.d.ts +1 -1
  4. package/dist/src/version.js +1 -1
  5. package/package.json +4 -1
  6. package/scripts/build-version.js +19 -0
  7. package/src/agent/bridge/MCPBridge.ts +176 -0
  8. package/src/agent/codegen/CodegenManager.ts +334 -0
  9. package/src/agent/codegen/SchemaToTypeScriptMapper.ts +98 -0
  10. package/src/agent/codegen/ToolDescriptionBuilder.ts +258 -0
  11. package/src/agent/codegen/ToolSchemaIntrospector.ts +95 -0
  12. package/src/agent/codegen/types.ts +38 -0
  13. package/src/agent/config/CodeModeConfig.ts +48 -0
  14. package/src/agent/runtime/code-executor/CodeExecutor.ts +127 -0
  15. package/src/agent/runtime/code-executor/NodeVmCodeExecutor.ts +117 -0
  16. package/src/agent/runtime/createSandboxManager.ts +40 -0
  17. package/src/agent/runtime/ivm/IVMSandboxManager.ts +371 -0
  18. package/src/agent/runtime/types.ts +13 -0
  19. package/src/agent/runtime/vm/VmSandboxManager.ts +284 -0
  20. package/src/agent/security/PIITokenizer.ts +65 -0
  21. package/src/cli/cli.ts +25 -0
  22. package/src/cli/code-mode.ts +254 -0
  23. package/src/cli/run-command.js +682 -0
  24. package/src/index.ts +178 -0
  25. package/src/kubernetes/BaseResourceOperations.ts +286 -0
  26. package/src/kubernetes/CircuitBreaker.ts +485 -0
  27. package/src/kubernetes/ConnectionManager.ts +114 -0
  28. package/src/kubernetes/ConnectionPool.ts +551 -0
  29. package/src/kubernetes/ErrorHandler.ts +436 -0
  30. package/src/kubernetes/ErrorHandling.ts +401 -0
  31. package/src/kubernetes/KubernetesClient.ts +653 -0
  32. package/src/kubernetes/README.md +349 -0
  33. package/src/kubernetes/ResourceOperations.ts +116 -0
  34. package/src/kubernetes/RetryStrategy.ts +372 -0
  35. package/src/kubernetes/RetryableOperation.ts +313 -0
  36. package/src/kubernetes/docs/ResourceOperations.md +606 -0
  37. package/src/kubernetes/index.ts +116 -0
  38. package/src/kubernetes/resources/ConfigMapOperations.ts +368 -0
  39. package/src/kubernetes/resources/CustomResourceOperations.ts +392 -0
  40. package/src/kubernetes/resources/DeploymentOperations.ts +294 -0
  41. package/src/kubernetes/resources/HelmReleaseOperations.ts +536 -0
  42. package/src/kubernetes/resources/IngressOperations.ts +277 -0
  43. package/src/kubernetes/resources/MetricOperations.ts +1734 -0
  44. package/src/kubernetes/resources/NamespaceOperations.ts +129 -0
  45. package/src/kubernetes/resources/PersistentVolumeClaimOperations.ts +516 -0
  46. package/src/kubernetes/resources/PersistentVolumeOperations.ts +438 -0
  47. package/src/kubernetes/resources/PodOperations.ts +424 -0
  48. package/src/kubernetes/resources/SecretOperations.ts +314 -0
  49. package/src/kubernetes/resources/ServiceOperations.ts +283 -0
  50. package/src/kubernetes/utils/ResourceUtils.ts +553 -0
  51. package/src/plugins/ArgoCDToolsPlugin.ts +129 -0
  52. package/src/plugins/ArgoToolsPlugin.ts +130 -0
  53. package/src/plugins/BaseToolsPlugin.ts +148 -0
  54. package/src/plugins/HelmToolsPlugin.ts +129 -0
  55. package/src/plugins/KubernetesToolsPlugin.ts +232 -0
  56. package/src/plugins/SamplePlugin.ts +59 -0
  57. package/src/plugins/index.ts +4 -0
  58. package/src/server/MCPServer.ts +900 -0
  59. package/src/server/StreamableHttpRuntime.ts +391 -0
  60. package/src/server/TransportConfig.ts +95 -0
  61. package/src/tools/RunCodeTool.ts +777 -0
  62. package/src/tools/argo/ArgoCronListTool.ts +140 -0
  63. package/src/tools/argo/ArgoGetTool.ts +138 -0
  64. package/src/tools/argo/ArgoListTool.ts +262 -0
  65. package/src/tools/argo/ArgoLogsTool.ts +307 -0
  66. package/src/tools/argo/BaseTool.ts +104 -0
  67. package/src/tools/argo/index.ts +11 -0
  68. package/src/tools/argocd/ArgoCDAppGetTool.ts +109 -0
  69. package/src/tools/argocd/ArgoCDAppHistoryTool.ts +91 -0
  70. package/src/tools/argocd/ArgoCDAppListTool.ts +144 -0
  71. package/src/tools/argocd/ArgoCDAppResourcesTool.ts +139 -0
  72. package/src/tools/argocd/ArgoCDAppTool.ts +819 -0
  73. package/src/tools/argocd/BaseTool.ts +89 -0
  74. package/src/tools/argocd/index.ts +3 -0
  75. package/src/tools/helm/BaseTool.ts +61 -0
  76. package/src/tools/helm/HelmDebugTool.ts +486 -0
  77. package/src/tools/helm/HelmGetHooksTool.ts +45 -0
  78. package/src/tools/helm/HelmGetManifestTool.ts +45 -0
  79. package/src/tools/helm/HelmGetNotesTool.ts +45 -0
  80. package/src/tools/helm/HelmGetResourcesTool.ts +141 -0
  81. package/src/tools/helm/HelmGetTool.ts +260 -0
  82. package/src/tools/helm/HelmGetValuesTool.ts +71 -0
  83. package/src/tools/helm/HelmHistoryTool.ts +57 -0
  84. package/src/tools/helm/HelmListTool.ts +144 -0
  85. package/src/tools/helm/HelmListWithResourcesTool.ts +195 -0
  86. package/src/tools/helm/HelmStatusTool.ts +63 -0
  87. package/src/tools/helm/index.ts +5 -0
  88. package/src/tools/kubernetes/BaseTool.ts +76 -0
  89. package/src/tools/kubernetes/ExecTool.ts +308 -0
  90. package/src/tools/kubernetes/GetConfigMapTool.ts +33 -0
  91. package/src/tools/kubernetes/GetContainerLogsTool.ts +155 -0
  92. package/src/tools/kubernetes/GetCronJobsTool.ts +47 -0
  93. package/src/tools/kubernetes/GetDaemonSetsTool.ts +55 -0
  94. package/src/tools/kubernetes/GetDeploymentsTool.ts +33 -0
  95. package/src/tools/kubernetes/GetEndpointSlicesTool.ts +57 -0
  96. package/src/tools/kubernetes/GetEndpointsTool.ts +53 -0
  97. package/src/tools/kubernetes/GetEventsTool.ts +133 -0
  98. package/src/tools/kubernetes/GetHPATool.ts +48 -0
  99. package/src/tools/kubernetes/GetIngressTool.ts +39 -0
  100. package/src/tools/kubernetes/GetJobsTool.ts +54 -0
  101. package/src/tools/kubernetes/GetLimitRangesTool.ts +40 -0
  102. package/src/tools/kubernetes/GetMetricsTool.ts +61 -0
  103. package/src/tools/kubernetes/GetNamespacesTool.ts +36 -0
  104. package/src/tools/kubernetes/GetNodesTool.ts +55 -0
  105. package/src/tools/kubernetes/GetPDBTool.ts +48 -0
  106. package/src/tools/kubernetes/GetPersistentVolumeClaimsTool.ts +113 -0
  107. package/src/tools/kubernetes/GetPersistentVolumesTool.ts +107 -0
  108. package/src/tools/kubernetes/GetPodMetricsTool.ts +113 -0
  109. package/src/tools/kubernetes/GetPodsTool.ts +39 -0
  110. package/src/tools/kubernetes/GetReplicaSetsTool.ts +51 -0
  111. package/src/tools/kubernetes/GetResourceQuotaTool.ts +55 -0
  112. package/src/tools/kubernetes/GetResourceTool.ts +1296 -0
  113. package/src/tools/kubernetes/GetSecretsTool.ts +55 -0
  114. package/src/tools/kubernetes/GetServicesTool.ts +39 -0
  115. package/src/tools/kubernetes/GetStatefulSetsTool.ts +55 -0
  116. package/src/tools/kubernetes/KubeListTool.ts +1708 -0
  117. package/src/tools/kubernetes/KubeLogTool.ts +1063 -0
  118. package/src/tools/kubernetes/KubeMetricsTool.ts +220 -0
  119. package/src/tools/kubernetes/KubeNetTool.ts +481 -0
  120. package/src/tools/kubernetes/PortForwardTool.ts +307 -0
  121. package/src/tools/kubernetes/index.ts +10 -0
  122. package/src/tools/meta/PlanStepTool.ts +130 -0
  123. package/src/utils/CliUtils.ts +218 -0
  124. package/src/utils/CodeModeConfig.ts +47 -0
  125. package/src/utils/HelmDataParser.ts +230 -0
  126. package/src/utils/HelmLiveResources.ts +607 -0
  127. package/src/utils/McpToolResult.ts +32 -0
  128. package/src/utils/SensitiveData.ts +148 -0
  129. package/src/utils/toolNamespaces.ts +41 -0
  130. package/src/version.ts +2 -0
  131. package/tsconfig.json +37 -0
package/CHANGELOG.md CHANGED
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [1.9.1] - 2026-06-13
11
+
12
+ ### Fixed
13
+ - **(fix) Fix the launch issue for claude desktop**
14
+
15
+
10
16
  ## [1.9.0] - 2026-06-02
11
17
 
12
18
  ### Added
@@ -52,11 +52,11 @@ class KubeMCPCLI {
52
52
  const nodeModulesPath = path.join(this.projectRoot, 'node_modules');
53
53
  if (!fs.existsSync(nodeModulesPath)) {
54
54
  this.log('📦 Installing dependencies...', colors.blue);
55
- execSync('npm install', { cwd: this.projectRoot, stdio: 'inherit' });
55
+ this.runCommandForMcpStartup('npm install');
56
56
  }
57
57
 
58
58
  // Build the project
59
- execSync('npm run build', { cwd: this.projectRoot, stdio: 'inherit' });
59
+ this.runCommandForMcpStartup('npm run build');
60
60
  this.log('✅ Build completed successfully', colors.green);
61
61
  } catch (buildError) {
62
62
  this.error('❌ Build failed:');
@@ -66,6 +66,23 @@ class KubeMCPCLI {
66
66
  }
67
67
  }
68
68
 
69
+ runCommandForMcpStartup(command) {
70
+ try {
71
+ execSync(command, {
72
+ cwd: this.projectRoot,
73
+ stdio: ['ignore', 'pipe', 'pipe'],
74
+ });
75
+ } catch (error) {
76
+ if (error.stdout) {
77
+ process.stderr.write(error.stdout);
78
+ }
79
+ if (error.stderr) {
80
+ process.stderr.write(error.stderr);
81
+ }
82
+ throw error;
83
+ }
84
+ }
85
+
69
86
  // Display help information
70
87
  displayHelp() {
71
88
  this.log('Kube MCP - Kubernetes MCP Server', colors.cyan);
@@ -1,2 +1,2 @@
1
- export declare const VERSION = "1.9.0";
1
+ export declare const VERSION = "1.9.1";
2
2
  //# sourceMappingURL=version.d.ts.map
@@ -1,3 +1,3 @@
1
1
  // Auto-generated by scripts/build-version.js - DO NOT EDIT
2
- export const VERSION = '1.9.0';
2
+ export const VERSION = '1.9.1';
3
3
  //# sourceMappingURL=version.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kubeview-mcp",
3
- "version": "1.9.0",
3
+ "version": "1.9.1",
4
4
  "description": "Read-only MCP server enabling code-driven AI analysis of Kubernetes clusters",
5
5
  "homepage": "https://github.com/mikhae1/kubeview-mcp",
6
6
  "mcpName": "io.github.mikhae1/kubeview",
@@ -20,6 +20,9 @@
20
20
  "files": [
21
21
  "bin/",
22
22
  "dist/",
23
+ "scripts/build-version.js",
24
+ "src/",
25
+ "tsconfig.json",
23
26
  "*.md"
24
27
  ],
25
28
  "scripts": {
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Generates src/version.ts from package.json version.
4
+ * Run before TypeScript compilation to bake version into the build.
5
+ */
6
+
7
+ import { readFileSync, writeFileSync } from 'fs';
8
+ import { fileURLToPath } from 'url';
9
+ import { dirname, join } from 'path';
10
+
11
+ const __dirname = dirname(fileURLToPath(import.meta.url));
12
+ const rootDir = join(__dirname, '..');
13
+
14
+ const packageJson = JSON.parse(readFileSync(join(rootDir, 'package.json'), 'utf8'));
15
+ const versionTs = `// Auto-generated by scripts/build-version.js - DO NOT EDIT
16
+ export const VERSION = '${packageJson.version}';\n`;
17
+
18
+ writeFileSync(join(rootDir, 'src', 'version.ts'), versionTs);
19
+ console.error(`Generated src/version.ts with version ${packageJson.version}`);
@@ -0,0 +1,176 @@
1
+ import { Client } from '@modelcontextprotocol/sdk/client/index.js';
2
+ import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
3
+ import type { ListToolsResult, Tool } from '@modelcontextprotocol/sdk/types.js';
4
+ import type { Logger } from 'winston';
5
+ import { PIITokenizer } from '../security/PIITokenizer.js';
6
+
7
+ export interface MCPServerConfig {
8
+ name: string;
9
+ command: string;
10
+ args?: string[];
11
+ env?: Record<string, string>;
12
+ timeoutMs?: number;
13
+ }
14
+
15
+ export interface ToolRegistration {
16
+ qualifiedName: string;
17
+ server: string;
18
+ toolName: string;
19
+ tool: Tool;
20
+ }
21
+
22
+ export interface MCPBridgeOptions {
23
+ enablePII?: boolean;
24
+ logger?: Logger;
25
+ defaultTimeoutMs?: number;
26
+ }
27
+
28
+ export class MCPBridge {
29
+ private clients: Map<string, Client> = new Map();
30
+ private toolRegistry: Map<string, ToolRegistration> = new Map();
31
+ private piiTokenizer?: PIITokenizer;
32
+ private toolDiscoveryCache: Map<string, Tool[]> = new Map();
33
+ private configByServer: Map<string, MCPServerConfig> = new Map();
34
+
35
+ constructor(
36
+ private readonly configs: MCPServerConfig[],
37
+ private readonly options: MCPBridgeOptions = {},
38
+ ) {
39
+ if (options.enablePII) {
40
+ this.piiTokenizer = new PIITokenizer();
41
+ }
42
+ }
43
+
44
+ public async initialize(): Promise<void> {
45
+ for (const config of this.configs) {
46
+ if (this.configByServer.has(config.name)) {
47
+ throw new Error(`Duplicate MCP server name: ${config.name}`);
48
+ }
49
+ this.configByServer.set(config.name, config);
50
+ await this.initializeServer(config);
51
+ }
52
+ }
53
+
54
+ private async initializeServer(config: MCPServerConfig): Promise<void> {
55
+ const envEntries = Object.entries({
56
+ ...process.env,
57
+ ...config.env,
58
+ }).filter((entry): entry is [string, string] => typeof entry[1] === 'string');
59
+
60
+ const mergedEnv = Object.fromEntries(envEntries);
61
+
62
+ const transport = new StdioClientTransport({
63
+ command: config.command,
64
+ args: config.args ?? [],
65
+ env: mergedEnv,
66
+ });
67
+
68
+ const client = new Client(
69
+ {
70
+ name: 'kube-mcp-code-mode-client',
71
+ version: '1.0.0',
72
+ },
73
+ {
74
+ capabilities: {},
75
+ },
76
+ );
77
+
78
+ if (this.options.logger) {
79
+ this.options.logger.info(`Connecting to MCP server '${config.name}'`);
80
+ }
81
+
82
+ await client.connect(transport);
83
+ this.clients.set(config.name, client);
84
+
85
+ const toolsResponse = await client.listTools();
86
+ this.cacheToolMetadata(config.name, toolsResponse);
87
+ }
88
+
89
+ private cacheToolMetadata(serverName: string, toolsResponse: ListToolsResult): void {
90
+ this.toolDiscoveryCache.set(serverName, toolsResponse.tools);
91
+
92
+ for (const tool of toolsResponse.tools) {
93
+ const qualifiedName = `${serverName}__${tool.name}`;
94
+ this.toolRegistry.set(qualifiedName, {
95
+ qualifiedName,
96
+ server: serverName,
97
+ toolName: tool.name,
98
+ tool,
99
+ });
100
+ }
101
+ }
102
+
103
+ public getRegisteredTools(): ToolRegistration[] {
104
+ return Array.from(this.toolRegistry.values());
105
+ }
106
+
107
+ public getToolMetadata(qualifiedName: string): ToolRegistration | undefined {
108
+ return this.toolRegistry.get(qualifiedName);
109
+ }
110
+
111
+ public listServers(): string[] {
112
+ return Array.from(this.clients.keys());
113
+ }
114
+
115
+ public listToolsForServer(serverName: string): Tool[] {
116
+ return this.toolDiscoveryCache.get(serverName) ?? [];
117
+ }
118
+
119
+ public async callTool<T = any>(qualifiedName: string, args: any): Promise<T> {
120
+ const registration = this.toolRegistry.get(qualifiedName);
121
+ if (!registration) {
122
+ throw new Error(`Tool ${qualifiedName} not found`);
123
+ }
124
+
125
+ const client = this.clients.get(registration.server);
126
+ if (!client) {
127
+ throw new Error(`Server ${registration.server} not connected`);
128
+ }
129
+
130
+ const sanitizedArgs = this.piiTokenizer ? this.piiTokenizer.tokenize(args) : args;
131
+
132
+ const timeoutMs =
133
+ this.configByServer.get(registration.server)?.timeoutMs ?? this.options.defaultTimeoutMs;
134
+ const callPromise = client.callTool({
135
+ name: registration.toolName,
136
+ arguments: sanitizedArgs,
137
+ });
138
+
139
+ let result: Awaited<ReturnType<typeof client.callTool>>;
140
+ if (timeoutMs && timeoutMs > 0) {
141
+ result = await this.withTimeout(callPromise, timeoutMs, qualifiedName);
142
+ } else {
143
+ result = await callPromise;
144
+ }
145
+
146
+ const detokenized = this.piiTokenizer ? this.piiTokenizer.detokenize(result) : result;
147
+ return detokenized as T;
148
+ }
149
+
150
+ public async close(): Promise<void> {
151
+ for (const client of this.clients.values()) {
152
+ await client.close();
153
+ }
154
+ this.clients.clear();
155
+ this.toolRegistry.clear();
156
+ this.toolDiscoveryCache.clear();
157
+ }
158
+
159
+ private async withTimeout<T>(promise: Promise<T>, timeoutMs: number, label: string): Promise<T> {
160
+ return new Promise<T>((resolve, reject) => {
161
+ const timer = setTimeout(() => {
162
+ reject(new Error(`${label} timed out after ${timeoutMs}ms`));
163
+ }, timeoutMs);
164
+
165
+ promise
166
+ .then((value) => {
167
+ clearTimeout(timer);
168
+ resolve(value);
169
+ })
170
+ .catch((err) => {
171
+ clearTimeout(timer);
172
+ reject(err);
173
+ });
174
+ });
175
+ }
176
+ }
@@ -0,0 +1,334 @@
1
+ import { promises as fs } from 'fs';
2
+ import path from 'path';
3
+ import ts from 'typescript';
4
+ import { ToolSchemaIntrospector } from './ToolSchemaIntrospector.js';
5
+ import { SchemaToTypeScriptMapper } from './SchemaToTypeScriptMapper.js';
6
+ import type { ToolSchemaSummary } from './types.js';
7
+
8
+ export interface CodegenManagerOptions {
9
+ outputDir: string;
10
+ runtimeImportPath?: string;
11
+ manifestPath?: string;
12
+ }
13
+
14
+ const DEFAULT_OPTIONS: CodegenManagerOptions = {
15
+ outputDir: path.resolve(process.cwd(), 'generated/servers'),
16
+ runtimeImportPath: '../../runtime/callMCPTool.ts',
17
+ manifestPath: path.resolve(process.cwd(), 'generated/servers/manifest.json'),
18
+ };
19
+
20
+ export class CodegenManager {
21
+ private readonly mapper = new SchemaToTypeScriptMapper();
22
+ private readonly printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
23
+ private readonly options: CodegenManagerOptions;
24
+
25
+ constructor(
26
+ private readonly introspector: ToolSchemaIntrospector,
27
+ options?: Partial<CodegenManagerOptions>,
28
+ ) {
29
+ this.options = { ...DEFAULT_OPTIONS, ...options };
30
+ }
31
+
32
+ public async generate(): Promise<void> {
33
+ const summaries = this.introspector.collectToolSchemas();
34
+ if (!summaries.length) {
35
+ return;
36
+ }
37
+
38
+ await fs.mkdir(this.options.outputDir, { recursive: true });
39
+ await this.ensureRuntimeHelpers();
40
+
41
+ const grouped = this.groupByServer(summaries);
42
+ for (const [server, tools] of grouped.entries()) {
43
+ const serverDir = path.join(this.options.outputDir, server);
44
+ await fs.rm(serverDir, { recursive: true, force: true });
45
+ await fs.mkdir(serverDir, { recursive: true });
46
+ await Promise.all(tools.map((tool) => this.writeToolFile(serverDir, server, tool)));
47
+ await this.writeServerIndex(serverDir, tools);
48
+ }
49
+
50
+ await this.writeRootIndex(Array.from(grouped.keys()));
51
+ await this.writeManifest(grouped);
52
+ }
53
+
54
+ private groupByServer(summaries: ToolSchemaSummary[]): Map<string, ToolSchemaSummary[]> {
55
+ const grouped = new Map<string, ToolSchemaSummary[]>();
56
+ for (const summary of summaries) {
57
+ if (!grouped.has(summary.server)) {
58
+ grouped.set(summary.server, []);
59
+ }
60
+ grouped.get(summary.server)!.push(summary);
61
+ }
62
+ return grouped;
63
+ }
64
+
65
+ private async writeToolFile(
66
+ baseDir: string,
67
+ _server: string,
68
+ tool: ToolSchemaSummary,
69
+ ): Promise<void> {
70
+ const statements: ts.Statement[] = [];
71
+ statements.push(this.createRuntimeImport());
72
+
73
+ const pascalName = this.toPascalCase(tool.toolName);
74
+ const inputTypeName = `${pascalName}Input`;
75
+ const resultTypeName = `${pascalName}Result`;
76
+
77
+ statements.push(this.mapper.createTypeAliasDeclaration(inputTypeName, tool.inputSchema));
78
+ statements.push(this.mapper.createTypeAliasDeclaration(resultTypeName, undefined));
79
+
80
+ statements.push(this.createToolFunction(tool, inputTypeName, resultTypeName));
81
+
82
+ const content = this.printStatements(statements);
83
+ const filePath = path.join(baseDir, `${tool.toolName}.ts`);
84
+ await fs.writeFile(filePath, content, 'utf-8');
85
+ }
86
+
87
+ private createRuntimeImport(): ts.ImportDeclaration {
88
+ const importPath = this.options.runtimeImportPath ?? DEFAULT_OPTIONS.runtimeImportPath!;
89
+ return ts.factory.createImportDeclaration(
90
+ undefined,
91
+ ts.factory.createImportClause(
92
+ false,
93
+ undefined,
94
+ ts.factory.createNamedImports([
95
+ ts.factory.createImportSpecifier(
96
+ false,
97
+ undefined,
98
+ ts.factory.createIdentifier('callMCPTool'),
99
+ ),
100
+ ]),
101
+ ),
102
+ ts.factory.createStringLiteral(importPath),
103
+ undefined,
104
+ );
105
+ }
106
+
107
+ private createToolFunction(
108
+ tool: ToolSchemaSummary,
109
+ inputTypeName: string,
110
+ resultTypeName: string,
111
+ ): ts.FunctionDeclaration {
112
+ const docComment = tool.description
113
+ ? ts.factory.createJSDocComment(tool.description)
114
+ : undefined;
115
+
116
+ const fn = ts.factory.createFunctionDeclaration(
117
+ [ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)],
118
+ undefined,
119
+ ts.factory.createIdentifier(this.toCamelCase(tool.toolName)),
120
+ undefined,
121
+ [
122
+ ts.factory.createParameterDeclaration(
123
+ undefined,
124
+ undefined,
125
+ ts.factory.createIdentifier('input'),
126
+ undefined,
127
+ ts.factory.createTypeReferenceNode(ts.factory.createIdentifier(inputTypeName)),
128
+ undefined,
129
+ ),
130
+ ],
131
+ ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('Promise'), [
132
+ ts.factory.createTypeReferenceNode(ts.factory.createIdentifier(resultTypeName)),
133
+ ]),
134
+ ts.factory.createBlock(
135
+ [
136
+ ts.factory.createReturnStatement(
137
+ ts.factory.createCallExpression(
138
+ ts.factory.createIdentifier('callMCPTool'),
139
+ [ts.factory.createTypeReferenceNode(ts.factory.createIdentifier(resultTypeName))],
140
+ [
141
+ ts.factory.createStringLiteral(tool.qualifiedName),
142
+ ts.factory.createIdentifier('input'),
143
+ ],
144
+ ),
145
+ ),
146
+ ],
147
+ true,
148
+ ),
149
+ );
150
+
151
+ if (docComment) {
152
+ ts.addSyntheticLeadingComment(
153
+ fn,
154
+ ts.SyntaxKind.MultiLineCommentTrivia,
155
+ `*\n * ${tool.description}\n `,
156
+ true,
157
+ );
158
+ }
159
+
160
+ return fn;
161
+ }
162
+
163
+ private async writeServerIndex(serverDir: string, tools: ToolSchemaSummary[]): Promise<void> {
164
+ const statements = tools.map((tool) =>
165
+ ts.factory.createExportDeclaration(
166
+ undefined,
167
+ false,
168
+ ts.factory.createNamedExports([
169
+ ts.factory.createExportSpecifier(
170
+ false,
171
+ undefined,
172
+ ts.factory.createIdentifier(this.toCamelCase(tool.toolName)),
173
+ ),
174
+ ]),
175
+ ts.factory.createStringLiteral(`./${tool.toolName}.ts`),
176
+ undefined,
177
+ ),
178
+ );
179
+
180
+ const content = this.printStatements(statements);
181
+ await fs.writeFile(path.join(serverDir, 'index.ts'), content, 'utf-8');
182
+ }
183
+
184
+ private async writeRootIndex(servers: string[]): Promise<void> {
185
+ const statements = servers.map((server) =>
186
+ ts.factory.createExportDeclaration(
187
+ undefined,
188
+ false,
189
+ undefined,
190
+ ts.factory.createStringLiteral(`./${server}/index.ts`),
191
+ undefined,
192
+ ),
193
+ );
194
+ const content = this.printStatements(statements);
195
+ await fs.writeFile(path.join(this.options.outputDir, 'index.ts'), content, 'utf-8');
196
+ }
197
+
198
+ private async writeManifest(grouped: Map<string, ToolSchemaSummary[]>): Promise<void> {
199
+ const manifest = Array.from(grouped.entries()).map(([server, tools]) => ({
200
+ server,
201
+ tools: tools.map((tool) => ({
202
+ name: tool.toolName,
203
+ qualifiedName: tool.qualifiedName,
204
+ description: tool.description,
205
+ inputSchema: tool.inputSchema, // Include full schema for runtime introspection
206
+ })),
207
+ }));
208
+
209
+ if (!this.options.manifestPath) return;
210
+ await fs.writeFile(this.options.manifestPath, JSON.stringify(manifest, null, 2), 'utf-8');
211
+ }
212
+
213
+ private async ensureRuntimeHelpers(): Promise<void> {
214
+ const runtimeDir = path.resolve(this.options.outputDir, '../runtime');
215
+ await fs.mkdir(runtimeDir, { recursive: true });
216
+ const filePath = path.join(runtimeDir, 'callMCPTool.ts');
217
+ const content = `declare global {
218
+ // eslint-disable-next-line no-var
219
+ var __callMCPTool: ((qualifiedName: string, args: unknown) => Promise<unknown>) | undefined;
220
+ }
221
+
222
+ export async function callMCPTool<T = unknown>(qualifiedName: string, args: unknown): Promise<T> {
223
+ if (typeof globalThis.__callMCPTool !== 'function') {
224
+ throw new Error('callMCPTool bridge not initialized');
225
+ }
226
+ const fn = globalThis.__callMCPTool as (name: string, params: unknown) => Promise<T>;
227
+ return fn(qualifiedName, args);
228
+ }
229
+ `;
230
+ await fs.writeFile(filePath, content, 'utf-8');
231
+
232
+ const searchHelperPath = path.join(runtimeDir, 'toolSearch.ts');
233
+ const searchContent = `import manifest from '../servers/manifest.json';
234
+
235
+ export interface ToolManifestEntry {
236
+ server: string;
237
+ name: string;
238
+ qualifiedName: string;
239
+ description?: string;
240
+ }
241
+
242
+ const tools: ToolManifestEntry[] = manifest.flatMap((serverEntry) =>
243
+ serverEntry.tools.map((tool) => ({
244
+ server: serverEntry.server,
245
+ ...tool,
246
+ })),
247
+ );
248
+
249
+ export function listServers(): string[] {
250
+ return manifest.map((entry) => entry.server);
251
+ }
252
+
253
+ export function listTools(server?: string): ToolManifestEntry[] {
254
+ if (!server) return tools;
255
+ return tools.filter((tool) => tool.server === server);
256
+ }
257
+
258
+ export function searchTools(query: string, limit = 10): ToolManifestEntry[] {
259
+ const normalized = query.toLowerCase();
260
+ return tools
261
+ .filter(
262
+ (tool) =>
263
+ tool.name.toLowerCase().includes(normalized) ||
264
+ (tool.description ?? '').toLowerCase().includes(normalized),
265
+ )
266
+ .slice(0, limit);
267
+ }
268
+ `;
269
+ await fs.writeFile(searchHelperPath, searchContent, 'utf-8');
270
+
271
+ const workspaceHelperPath = path.join(runtimeDir, 'workspaceFs.ts');
272
+ const workspaceContent = `declare global {
273
+ // eslint-disable-next-line no-var
274
+ var __workspaceFs:
275
+ | {
276
+ readFile(path: string): Promise<string>;
277
+ writeFile(path: string, data: string): Promise<void>;
278
+ listDir(path?: string): Promise<string[]>;
279
+ exists(path: string): Promise<boolean>;
280
+ }
281
+ | undefined;
282
+ }
283
+
284
+ function ensureWorkspaceFs() {
285
+ if (!globalThis.__workspaceFs) {
286
+ throw new Error('Workspace filesystem bridge not initialized');
287
+ }
288
+ return globalThis.__workspaceFs;
289
+ }
290
+
291
+ export async function readWorkspaceFile(path: string): Promise<string> {
292
+ return ensureWorkspaceFs().readFile(path);
293
+ }
294
+
295
+ export async function writeWorkspaceFile(path: string, data: string): Promise<void> {
296
+ return ensureWorkspaceFs().writeFile(path, data);
297
+ }
298
+
299
+ export async function listWorkspaceDir(path?: string): Promise<string[]> {
300
+ return ensureWorkspaceFs().listDir(path);
301
+ }
302
+
303
+ export async function workspacePathExists(path: string): Promise<boolean> {
304
+ return ensureWorkspaceFs().exists(path);
305
+ }
306
+ `;
307
+ await fs.writeFile(workspaceHelperPath, workspaceContent, 'utf-8');
308
+ }
309
+
310
+ private printStatements(statements: ts.Statement[]): string {
311
+ const sourceFile = ts.createSourceFile(
312
+ 'temp.ts',
313
+ '',
314
+ ts.ScriptTarget.ES2020,
315
+ false,
316
+ ts.ScriptKind.TS,
317
+ );
318
+ return statements
319
+ .map((statement) => this.printer.printNode(ts.EmitHint.Unspecified, statement, sourceFile))
320
+ .join('\n\n');
321
+ }
322
+
323
+ private toPascalCase(value: string): string {
324
+ return value
325
+ .replace(/[_-]+/g, ' ')
326
+ .replace(/(?:^\w|[A-Z]|\b\w)/g, (word) => word.toUpperCase())
327
+ .replace(/\s+/g, '');
328
+ }
329
+
330
+ private toCamelCase(value: string): string {
331
+ const pascal = this.toPascalCase(value);
332
+ return pascal.charAt(0).toLowerCase() + pascal.slice(1);
333
+ }
334
+ }