kubectl_logs
Retrieve logs from Kubernetes resources including pods, deployments, jobs, and cronjobs to monitor application behavior and troubleshoot issues.
Instructions
Get logs from Kubernetes resources like pods, deployments, or jobs
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| resourceType | Yes | Type of resource to get logs from | |
| name | Yes | Name of the resource | |
| namespace | Yes | Kubernetes namespace | default |
| container | No | Container name (required when pod has multiple containers) | |
| tail | No | Number of lines to show from end of logs | |
| since | No | Show logs since relative time (e.g. '5s', '2m', '3h') | |
| sinceTime | No | Show logs since absolute time (RFC3339) | |
| timestamps | No | Include timestamps in logs | |
| previous | No | Include logs from previously terminated containers | |
| follow | No | Follow logs output (not recommended, may cause timeouts) | |
| labelSelector | No | Filter resources by label selector | |
| context | No | Kubeconfig Context to use for the command (optional - defaults to null) |
Implementation Reference
- src/tools/kubectl-logs.ts:72-274 (handler)Main handler function for the 'kubectl_logs' tool. Executes kubectl logs commands for pods, deployments, jobs, and cronjobs, supporting various options like tail, since, container selection, and label selectors. Handles multi-pod scenarios by fetching logs from all matching pods.export async function kubectlLogs( k8sManager: KubernetesManager, input: { resourceType: string; name: string; namespace: string; container?: string; tail?: number; since?: string; sinceTime?: string; timestamps?: boolean; previous?: boolean; follow?: boolean; labelSelector?: string; context?: string; } ) { try { const resourceType = input.resourceType.toLowerCase(); const name = input.name; const namespace = input.namespace || "default"; const context = input.context || ""; const command = "kubectl"; // Handle different resource types if (resourceType === "pod") { // Direct pod logs let args = ["-n", namespace, "logs", name]; // If container is specified, add it if (input.container) { args.push(`-c`, input.container); } // Add options args = addLogOptions(args, input); // Add context if provided if (context) { args.push("--context", context); } // Execute the command try { const result = execFileSync(command, args, { encoding: "utf8", maxBuffer: getSpawnMaxBuffer(), env: { ...process.env, KUBECONFIG: process.env.KUBECONFIG }, }); return formatLogOutput(name, result); } catch (error: any) { return handleCommandError(error, `pod ${name}`); } } else if ( resourceType === "deployment" || resourceType === "job" || resourceType === "cronjob" ) { // For deployments, jobs and cronjobs we need to find the pods first let selectorArgs; if (resourceType === "deployment") { selectorArgs = [ "-n", namespace, "get", "deployment", name, "-o", "jsonpath='{.spec.selector.matchLabels}'", ]; } else if (resourceType === "job") { // For jobs, we use the job-name label return getLabelSelectorLogs(`job-name=${name}`, namespace, input); } else if (resourceType === "cronjob") { // For cronjobs, it's more complex - need to find the job first const jobsArgs = [ "-n", namespace, "get", "jobs", "--selector=job-name=" + name, "-o", "jsonpath='{.items[*].metadata.name}'", ]; try { const jobs = execFileSync(command, jobsArgs, { encoding: "utf8", maxBuffer: getSpawnMaxBuffer(), env: { ...process.env, KUBECONFIG: process.env.KUBECONFIG }, }) .trim() .split(" "); if (jobs.length === 0 || (jobs.length === 1 && jobs[0] === "")) { return { content: [ { type: "text", text: JSON.stringify( { message: `No jobs found for cronjob ${name} in namespace ${namespace}`, }, null, 2 ), }, ], }; } // Get logs for all jobs const allJobLogs: Record<string, any> = {}; for (const job of jobs) { // Get logs for pods from this job const result = await getLabelSelectorLogs( `job-name=${job}`, namespace, input ); const jobLog = JSON.parse(result.content[0].text); allJobLogs[job] = jobLog.logs; } return { content: [ { type: "text", text: JSON.stringify( { cronjob: name, namespace: namespace, jobs: allJobLogs, }, null, 2 ), }, ], }; } catch (error: any) { return handleCommandError(error, `cronjob ${name}`); } } try { if (resourceType === "deployment") { // Get the deployment's selector if (!selectorArgs) { throw new Error("Selector command is undefined"); } const selectorJson = execFileSync(command, selectorArgs, { encoding: "utf8", maxBuffer: getSpawnMaxBuffer(), env: { ...process.env, KUBECONFIG: process.env.KUBECONFIG }, }).trim(); const selector = JSON.parse(selectorJson.replace(/'/g, '"')); // Convert to label selector format const labelSelector = Object.entries(selector) .map(([key, value]) => `${key}=${value}`) .join(","); return getLabelSelectorLogs(labelSelector, namespace, input); } // For jobs and cronjobs, the logic is handled above return { content: [ { type: "text", text: JSON.stringify( { error: `Unexpected resource type: ${resourceType}`, }, null, 2 ), }, ], isError: true, }; } catch (error: any) { return handleCommandError(error, `${resourceType} ${name}`); } } else if (input.labelSelector) { // Handle logs by label selector return getLabelSelectorLogs(input.labelSelector, namespace, input); } else { throw new McpError( ErrorCode.InvalidRequest, `Unsupported resource type: ${resourceType}` ); } } catch (error: any) { if (error instanceof McpError) throw error; throw new McpError( ErrorCode.InternalError, `Failed to get logs: ${error.message}` ); } }
- src/tools/kubectl-logs.ts:10-70 (schema)Input schema and metadata for the kubectl_logs tool, defining required and optional parameters.export const kubectlLogsSchema = { name: "kubectl_logs", description: "Get logs from Kubernetes resources like pods, deployments, or jobs", annotations: { readOnlyHint: true, }, inputSchema: { type: "object", properties: { resourceType: { type: "string", enum: ["pod", "deployment", "job", "cronjob"], description: "Type of resource to get logs from", }, name: { type: "string", description: "Name of the resource", }, namespace: namespaceParameter, container: { type: "string", description: "Container name (required when pod has multiple containers)", }, tail: { type: "number", description: "Number of lines to show from end of logs", }, since: { type: "string", description: "Show logs since relative time (e.g. '5s', '2m', '3h')", }, sinceTime: { type: "string", description: "Show logs since absolute time (RFC3339)", }, timestamps: { type: "boolean", description: "Include timestamps in logs", default: false, }, previous: { type: "boolean", description: "Include logs from previously terminated containers", default: false, }, follow: { type: "boolean", description: "Follow logs output (not recommended, may cause timeouts)", default: false, }, labelSelector: { type: "string", description: "Filter resources by label selector", }, context: contextParameter, }, required: ["resourceType", "name", "namespace"], }, } as const;
- src/index.ts:292-310 (registration)Registration and dispatch logic in the main CallToolRequestSchema handler that routes 'kubectl_logs' tool calls to the kubectlLogs function.if (name === "kubectl_logs") { return await kubectlLogs( k8sManager, input as { resourceType: string; name: string; namespace: string; container?: string; tail?: number; since?: string; sinceTime?: string; timestamps?: boolean; previous?: boolean; follow?: boolean; labelSelector?: string; context?: string; } ); }
- src/index.ts:100-140 (registration)Tool schema is included in the allTools array, which is used by ListToolsRequestSchema to expose the tool.const allTools = [ // Core operation tools cleanupSchema, // Unified kubectl-style tools - these replace many specific tools kubectlGetSchema, kubectlDescribeSchema, kubectlApplySchema, kubectlDeleteSchema, kubectlCreateSchema, kubectlLogsSchema, kubectlScaleSchema, kubectlPatchSchema, kubectlRolloutSchema, // Kubernetes context management kubectlContextSchema, // Special operations that aren't covered by simple kubectl commands explainResourceSchema, // Helm operations installHelmChartSchema, upgradeHelmChartSchema, uninstallHelmChartSchema, nodeManagementSchema, // Port forwarding PortForwardSchema, StopPortForwardSchema, execInPodSchema, // API resource operations listApiResourcesSchema, // Generic kubectl command kubectlGenericSchema, // Ping utility pingSchema, ];
- src/tools/kubectl-logs.ts:311-401 (helper)Helper function to retrieve logs from multiple pods matching a label selector, used for deployments, jobs, etc.async function getLabelSelectorLogs( labelSelector: string, namespace: string, input: any ): Promise<{ content: Array<{ type: string; text: string }> }> { try { const command = "kubectl"; // First, find all pods matching the label selector const podsArgs = [ "-n", namespace, "get", "pods", `--selector=${labelSelector}`, "-o", "jsonpath='{.items[*].metadata.name}'", ]; const pods = execFileSync(command, podsArgs, { encoding: "utf8", maxBuffer: getSpawnMaxBuffer(), env: { ...process.env, KUBECONFIG: process.env.KUBECONFIG }, }) .trim() .split(" "); if (pods.length === 0 || (pods.length === 1 && pods[0] === "")) { return { content: [ { type: "text", text: JSON.stringify( { message: `No pods found with label selector "${labelSelector}" in namespace ${namespace}`, }, null, 2 ), }, ], }; } // Get logs for each pod const logsMap: Record<string, string> = {}; for (const pod of pods) { // Skip empty pod names if (!pod) continue; let podArgs = ["-n", namespace, "logs", pod]; // Add container if specified if (input.container) { podArgs.push(`-c`, input.container); } // Add other options podArgs = addLogOptions(podArgs, input); try { const logs = execFileSync(command, podArgs, { encoding: "utf8", maxBuffer: getSpawnMaxBuffer(), env: { ...process.env, KUBECONFIG: process.env.KUBECONFIG }, }); logsMap[pod] = logs; } catch (error: any) { logsMap[pod] = `Error: ${error.message}`; } } return { content: [ { type: "text", text: JSON.stringify( { selector: labelSelector, namespace: namespace, logs: logsMap, }, null, 2 ), }, ], }; } catch (error: any) { return handleCommandError(error, `pods with selector "${labelSelector}"`); } }