Add mcp logs command for streaming container logs

New server-streaming Logs RPC streams container output to the CLI.
Supports --tail/-n, --follow/-f, --timestamps/-t, --since.

Detects journald log driver and falls back to journalctl (podman logs
can't read journald outside the originating user session). New containers
default to k8s-file via mcp user's containers.conf.

Also adds stream auth interceptor for the agent gRPC server (required
for streaming RPCs).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-28 17:54:48 -07:00
parent 18365cc0a8
commit 14b978861f
10 changed files with 490 additions and 25 deletions

View File

@@ -178,6 +178,49 @@ func (p *Podman) Inspect(ctx context.Context, name string) (ContainerInfo, error
return info, nil
}
// Logs returns an exec.Cmd that streams container logs. For containers
// using the journald log driver, it uses journalctl (podman logs can't
// read journald outside the originating user session). For k8s-file or
// other drivers, it uses podman logs directly.
func (p *Podman) Logs(ctx context.Context, containerName string, tail int, follow, timestamps bool, since string) *exec.Cmd {
// Check if this container uses the journald log driver.
inspectCmd := exec.CommandContext(ctx, p.command(), "inspect", "--format", "{{.HostConfig.LogConfig.Type}}", containerName) //nolint:gosec
if out, err := inspectCmd.Output(); err == nil && strings.TrimSpace(string(out)) == "journald" {
return p.journalLogs(ctx, containerName, tail, follow, since)
}
args := []string{"logs"}
if tail > 0 {
args = append(args, "--tail", fmt.Sprintf("%d", tail))
}
if follow {
args = append(args, "--follow")
}
if timestamps {
args = append(args, "--timestamps")
}
if since != "" {
args = append(args, "--since", since)
}
args = append(args, containerName)
return exec.CommandContext(ctx, p.command(), args...) //nolint:gosec // args built programmatically
}
// journalLogs returns a journalctl command filtered by container name.
func (p *Podman) journalLogs(ctx context.Context, containerName string, tail int, follow bool, since string) *exec.Cmd {
args := []string{"--no-pager", "--output", "cat", "CONTAINER_NAME=" + containerName}
if tail > 0 {
args = append(args, "--lines", fmt.Sprintf("%d", tail))
}
if follow {
args = append(args, "--follow")
}
if since != "" {
args = append(args, "--since", since)
}
return exec.CommandContext(ctx, "journalctl", args...) //nolint:gosec // args built programmatically
}
// Login authenticates to a container registry using the given token as
// the password. This enables non-interactive push with service account
// tokens (MCR accepts MCIAS JWTs as passwords).