@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.
@@ -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
+ }