package tool import ( "bytes" "context" "fmt" "log/slog" "os/exec" "strconv" "github.com/mark3labs/mcp-go/mcp" "github.com/mark3labs/mcp-go/server" ) // DockerTool 提供 docker logs 查询。 type DockerTool struct{} func NewDockerTool() *DockerTool { return &DockerTool{} } func (d *DockerTool) Name() string { return "docker" } func (d *DockerTool) Description() string { return "Docker 容器日志查询" } func (d *DockerTool) Initialize(_ context.Context) error { if _, err := exec.LookPath("docker"); err != nil { return fmt.Errorf("docker CLI not found: %w", err) } slog.Info("docker CLI ready") return nil } func (d *DockerTool) Shutdown(_ context.Context) error { return nil } func (d *DockerTool) HealthCheck(ctx context.Context) error { return exec.CommandContext(ctx, "docker", "version").Run() } func (d *DockerTool) Register(mcpServer *server.MCPServer) error { mcpServer.AddTool(mcp.NewTool("docker_logs", mcp.WithDescription("获取 Docker 容器日志,等价于 docker logs --tail N "), mcp.WithString("container", mcp.Required(), mcp.Description("容器名称或 ID"), ), mcp.WithNumber("tail", mcp.Description("返回最后 N 行日志,默认 100"), ), ), d.handleLogs) return nil } func (d *DockerTool) handleLogs(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { args := getArgs(req) container, _ := args["container"].(string) if container == "" { return mcp.NewToolResultError("container 参数必填"), nil } tail := 100 if t, ok := args["tail"].(float64); ok && t > 0 { tail = int(t) } cmd := exec.CommandContext(ctx, "docker", "logs", "--tail", strconv.Itoa(tail), "--timestamps", container, ) var stdout, stderr bytes.Buffer cmd.Stdout = &stdout cmd.Stderr = &stderr if err := cmd.Run(); err != nil { return mcp.NewToolResultError( fmt.Sprintf("docker logs 失败: %v\n%s", err, stderr.String()), ), nil } output := stdout.String() if output == "" { output = "(容器没有日志输出)" } return mcp.NewToolResultText(output), nil }