@zerry_jin/k8s-doctor-mcp 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.ko.md +330 -0
- package/README.md +330 -0
- package/dist/analyzers/log-analyzer.d.ts +17 -0
- package/dist/analyzers/log-analyzer.js +402 -0
- package/dist/diagnostics/cluster-health.d.ts +13 -0
- package/dist/diagnostics/cluster-health.js +176 -0
- package/dist/diagnostics/pod-diagnostics.d.ts +23 -0
- package/dist/diagnostics/pod-diagnostics.js +654 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +678 -0
- package/dist/types.d.ts +337 -0
- package/dist/types.js +6 -0
- package/dist/utils/cache.d.ts +59 -0
- package/dist/utils/cache.js +99 -0
- package/dist/utils/formatters.d.ts +48 -0
- package/dist/utils/formatters.js +129 -0
- package/dist/utils/k8s-client.d.ts +37 -0
- package/dist/utils/k8s-client.js +74 -0
- package/dist/utils/retry.d.ts +25 -0
- package/dist/utils/retry.js +70 -0
- package/package.json +65 -0
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kubernetes client initialization
|
|
3
|
+
*
|
|
4
|
+
* @author zerry
|
|
5
|
+
*/
|
|
6
|
+
import * as k8s from '@kubernetes/client-node';
|
|
7
|
+
/**
|
|
8
|
+
* Load K8s configuration
|
|
9
|
+
*
|
|
10
|
+
* Automatically finds and loads kubeconfig.
|
|
11
|
+
* Works in-cluster as well
|
|
12
|
+
*/
|
|
13
|
+
export function loadK8sConfig() {
|
|
14
|
+
// Try local kubeconfig first (most common case)
|
|
15
|
+
try {
|
|
16
|
+
const kc = new k8s.KubeConfig();
|
|
17
|
+
kc.loadFromDefault();
|
|
18
|
+
return kc;
|
|
19
|
+
}
|
|
20
|
+
catch (localError) {
|
|
21
|
+
// If local fails, try in-cluster config (when running inside pod)
|
|
22
|
+
try {
|
|
23
|
+
const kc = new k8s.KubeConfig();
|
|
24
|
+
kc.loadFromCluster();
|
|
25
|
+
return kc;
|
|
26
|
+
}
|
|
27
|
+
catch (clusterError) {
|
|
28
|
+
throw new Error('Cannot find kubeconfig. Please verify kubectl is configured.\n' +
|
|
29
|
+
'Hint: Check connection with "kubectl cluster-info"');
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Create K8s API clients
|
|
35
|
+
*
|
|
36
|
+
* Creates frequently used API clients at once
|
|
37
|
+
*/
|
|
38
|
+
export function createK8sClients(kc) {
|
|
39
|
+
return {
|
|
40
|
+
core: kc.makeApiClient(k8s.CoreV1Api),
|
|
41
|
+
apps: kc.makeApiClient(k8s.AppsV1Api),
|
|
42
|
+
batch: kc.makeApiClient(k8s.BatchV1Api),
|
|
43
|
+
networking: kc.makeApiClient(k8s.NetworkingV1Api),
|
|
44
|
+
storage: kc.makeApiClient(k8s.StorageV1Api),
|
|
45
|
+
log: new k8s.Log(kc),
|
|
46
|
+
metrics: new k8s.Metrics(kc),
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Validate namespace
|
|
51
|
+
*
|
|
52
|
+
* Check if namespace actually exists
|
|
53
|
+
*/
|
|
54
|
+
export async function validateNamespace(coreApi, namespace) {
|
|
55
|
+
try {
|
|
56
|
+
await coreApi.readNamespace({ name: namespace });
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
catch (e) {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Check if pod exists
|
|
65
|
+
*/
|
|
66
|
+
export async function podExists(coreApi, namespace, podName) {
|
|
67
|
+
try {
|
|
68
|
+
await coreApi.readNamespacedPod({ name: podName, namespace });
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
catch (e) {
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Retry utilities with exponential backoff
|
|
3
|
+
*
|
|
4
|
+
* @author zerry
|
|
5
|
+
*/
|
|
6
|
+
export interface RetryOptions {
|
|
7
|
+
/** Maximum number of retry attempts */
|
|
8
|
+
maxAttempts?: number;
|
|
9
|
+
/** Initial delay in milliseconds */
|
|
10
|
+
initialDelay?: number;
|
|
11
|
+
/** Maximum delay in milliseconds */
|
|
12
|
+
maxDelay?: number;
|
|
13
|
+
/** Backoff multiplier */
|
|
14
|
+
backoffMultiplier?: number;
|
|
15
|
+
/** Should retry on this error? */
|
|
16
|
+
shouldRetry?: (error: any) => boolean;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Execute function with retry logic and exponential backoff
|
|
20
|
+
*
|
|
21
|
+
* @param fn Function to execute
|
|
22
|
+
* @param options Retry options
|
|
23
|
+
* @returns Result of the function
|
|
24
|
+
*/
|
|
25
|
+
export declare function withRetry<T>(fn: () => Promise<T>, options?: RetryOptions): Promise<T>;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Retry utilities with exponential backoff
|
|
3
|
+
*
|
|
4
|
+
* @author zerry
|
|
5
|
+
*/
|
|
6
|
+
const DEFAULT_OPTIONS = {
|
|
7
|
+
maxAttempts: 3,
|
|
8
|
+
initialDelay: 1000,
|
|
9
|
+
maxDelay: 10000,
|
|
10
|
+
backoffMultiplier: 2,
|
|
11
|
+
shouldRetry: (error) => {
|
|
12
|
+
// Retry on network errors, timeouts, and 5xx errors
|
|
13
|
+
if (error.code === 'ECONNREFUSED')
|
|
14
|
+
return true;
|
|
15
|
+
if (error.code === 'ETIMEDOUT')
|
|
16
|
+
return true;
|
|
17
|
+
if (error.code === 'ENOTFOUND')
|
|
18
|
+
return true;
|
|
19
|
+
if (error.statusCode && error.statusCode >= 500)
|
|
20
|
+
return true;
|
|
21
|
+
// Don't retry on client errors (4xx)
|
|
22
|
+
if (error.statusCode && error.statusCode >= 400 && error.statusCode < 500)
|
|
23
|
+
return false;
|
|
24
|
+
// Retry on unknown errors
|
|
25
|
+
return true;
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Execute function with retry logic and exponential backoff
|
|
30
|
+
*
|
|
31
|
+
* @param fn Function to execute
|
|
32
|
+
* @param options Retry options
|
|
33
|
+
* @returns Result of the function
|
|
34
|
+
*/
|
|
35
|
+
export async function withRetry(fn, options = {}) {
|
|
36
|
+
const opts = { ...DEFAULT_OPTIONS, ...options };
|
|
37
|
+
let lastError;
|
|
38
|
+
let delay = opts.initialDelay;
|
|
39
|
+
for (let attempt = 1; attempt <= opts.maxAttempts; attempt++) {
|
|
40
|
+
try {
|
|
41
|
+
return await fn();
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
lastError = error;
|
|
45
|
+
// Don't retry if it's the last attempt
|
|
46
|
+
if (attempt === opts.maxAttempts) {
|
|
47
|
+
break;
|
|
48
|
+
}
|
|
49
|
+
// Don't retry if error is not retryable
|
|
50
|
+
if (!opts.shouldRetry(error)) {
|
|
51
|
+
console.error(`[Retry] Non-retryable error on attempt ${attempt}:`, error.message);
|
|
52
|
+
throw error;
|
|
53
|
+
}
|
|
54
|
+
console.error(`[Retry] Attempt ${attempt}/${opts.maxAttempts} failed: ${error.message}. ` +
|
|
55
|
+
`Retrying in ${delay}ms...`);
|
|
56
|
+
// Wait before retrying
|
|
57
|
+
await sleep(delay);
|
|
58
|
+
// Calculate next delay with exponential backoff
|
|
59
|
+
delay = Math.min(delay * opts.backoffMultiplier, opts.maxDelay);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
console.error(`[Retry] All ${opts.maxAttempts} attempts failed`);
|
|
63
|
+
throw lastError;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Sleep for specified milliseconds
|
|
67
|
+
*/
|
|
68
|
+
function sleep(ms) {
|
|
69
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
70
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@zerry_jin/k8s-doctor-mcp",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "AI-powered Kubernetes cluster diagnostics MCP server - analyzes pod failures, logs, resource usage, and provides intelligent debugging recommendations",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"bin": {
|
|
9
|
+
"k8s-doctor-mcp": "dist/index.js"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"dist",
|
|
13
|
+
"README.md",
|
|
14
|
+
"LICENSE"
|
|
15
|
+
],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "tsc",
|
|
18
|
+
"start": "node dist/index.js",
|
|
19
|
+
"dev": "tsx src/index.ts",
|
|
20
|
+
"demo": "tsx examples/demo.ts",
|
|
21
|
+
"prepublishOnly": "npm run build",
|
|
22
|
+
"test": "node --test"
|
|
23
|
+
},
|
|
24
|
+
"keywords": [
|
|
25
|
+
"mcp",
|
|
26
|
+
"model-context-protocol",
|
|
27
|
+
"kubernetes",
|
|
28
|
+
"k8s",
|
|
29
|
+
"debugging",
|
|
30
|
+
"diagnostics",
|
|
31
|
+
"devops",
|
|
32
|
+
"kubectl",
|
|
33
|
+
"pod",
|
|
34
|
+
"crashloop",
|
|
35
|
+
"logs",
|
|
36
|
+
"cluster",
|
|
37
|
+
"monitoring",
|
|
38
|
+
"claude",
|
|
39
|
+
"anthropic",
|
|
40
|
+
"ai"
|
|
41
|
+
],
|
|
42
|
+
"author": "zerry",
|
|
43
|
+
"license": "MIT",
|
|
44
|
+
"repository": {
|
|
45
|
+
"type": "git",
|
|
46
|
+
"url": "git+https://github.com/ongjin/k8s-doctor-mcp.git"
|
|
47
|
+
},
|
|
48
|
+
"bugs": {
|
|
49
|
+
"url": "https://github.com/ongjin/k8s-doctor-mcp/issues"
|
|
50
|
+
},
|
|
51
|
+
"homepage": "https://github.com/ongjin/k8s-doctor-mcp#readme",
|
|
52
|
+
"engines": {
|
|
53
|
+
"node": ">=18.0.0"
|
|
54
|
+
},
|
|
55
|
+
"dependencies": {
|
|
56
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
57
|
+
"@kubernetes/client-node": "^1.0.0",
|
|
58
|
+
"zod": "^3.23.0"
|
|
59
|
+
},
|
|
60
|
+
"devDependencies": {
|
|
61
|
+
"@types/node": "^20.0.0",
|
|
62
|
+
"tsx": "^4.0.0",
|
|
63
|
+
"typescript": "^5.0.0"
|
|
64
|
+
}
|
|
65
|
+
}
|