Skip to main content
Glama
Flux159
by Flux159

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
NameRequiredDescriptionDefault
resourceTypeYesType of resource to get logs from
nameYesName of the resource
namespaceYesKubernetes namespacedefault
containerNoContainer name (required when pod has multiple containers)
tailNoNumber of lines to show from end of logs
sinceNoShow logs since relative time (e.g. '5s', '2m', '3h')
sinceTimeNoShow logs since absolute time (RFC3339)
timestampsNoInclude timestamps in logs
previousNoInclude logs from previously terminated containers
followNoFollow logs output (not recommended, may cause timeouts)
labelSelectorNoFilter resources by label selector
contextNoKubeconfig Context to use for the command (optional - defaults to null)

Implementation Reference

  • 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}`
        );
      }
    }
  • 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,
    ];
  • 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}"`);
      }
    }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Flux159/mcp-server-kubernetes'

If you have feedback or need assistance with the MCP directory API, please join our Discord server