@@ -17,7 +17,6 @@ import (
1717 "github.com/mark3labs/mcp-go/server"
1818 "github.com/shirou/gopsutil/v3/cpu"
1919 "github.com/shirou/gopsutil/v3/disk"
20- "github.com/shirou/gopsutil/v3/docker"
2120 "github.com/shirou/gopsutil/v3/host"
2221 "github.com/shirou/gopsutil/v3/load"
2322 "github.com/shirou/gopsutil/v3/mem"
@@ -109,8 +108,8 @@ func (h *HandlerManager) RegisterTools(s *server.MCPServer) {
109108
110109 // Docker metrics tool
111110 s .AddTool (mcp .NewTool ("get_docker_metrics" ,
112- mcp .WithDescription ("Get Docker container metrics including CPU and memory usage via cgroups " ),
113- mcp .WithString ("container_id" , mcp .Description ("Optional container ID to filter results" ))),
111+ mcp .WithDescription ("Get Docker container metrics including CPU, memory, network, and block I/O usage " ),
112+ mcp .WithString ("container_id" , mcp .Description ("Optional container ID or name to filter results" ))),
114113 h .HandleGetDockerMetrics )
115114
116115 // Network connections tool
@@ -713,7 +712,8 @@ func (h *HandlerManager) HandleGetSystemHealth(ctx context.Context, request mcp.
713712 return mcp .NewToolResultText (string (jsonBytes )), nil
714713}
715714
716- // HandleGetDockerMetrics returns Docker container metrics
715+ // HandleGetDockerMetrics returns Docker container metrics using the docker CLI.
716+ // This approach works with both cgroups v1 and v2 systems.
717717func (h * HandlerManager ) HandleGetDockerMetrics (ctx context.Context , request mcp.CallToolRequest ) (* mcp.CallToolResult , error ) {
718718 var containerFilter string
719719
@@ -723,46 +723,104 @@ func (h *HandlerManager) HandleGetDockerMetrics(ctx context.Context, request mcp
723723 }
724724 }
725725
726- // Get Docker container stats
727- containers , err := docker .GetDockerStat ()
726+ // Verify docker is available
727+ if _ , err := exec .LookPath ("docker" ); err != nil {
728+ return mcp .NewToolResultError (fmt .Sprintf ("Docker CLI not found: %v" , err )), nil
729+ }
730+
731+ // Get container list via docker ps
732+ psArgs := []string {"ps" , "-a" , "--no-trunc" , "--format" , "{{.ID}}|{{.Names}}|{{.Image}}|{{.Status}}|{{.State}}" }
733+ psOut , err := exec .CommandContext (ctx , "docker" , psArgs ... ).Output ()
728734 if err != nil {
729- return mcp .NewToolResultError (fmt .Sprintf ("Docker not available or no containers found : %v" , err )), nil
735+ return mcp .NewToolResultError (fmt .Sprintf ("Failed to list Docker containers: %v" , err )), nil
730736 }
731737
732- containerData := []map [string ]interface {}{}
733- for _ , c := range containers {
734- // If a specific container is requested, filter
735- if containerFilter != "" && c .ContainerID != containerFilter && c .Name != containerFilter {
738+ // Parse container list
739+ type containerInfo struct {
740+ id string
741+ name string
742+ image string
743+ status string
744+ running bool
745+ }
746+ var containers []containerInfo
747+ for _ , line := range strings .Split (strings .TrimSpace (string (psOut )), "\n " ) {
748+ if line == "" {
736749 continue
737750 }
738-
739- cInfo := map [string ]interface {}{
740- "container_id" : c .ContainerID ,
741- "name" : c .Name ,
742- "image" : c .Image ,
743- "status" : c .Status ,
744- "running" : c .Running ,
751+ cols := strings .SplitN (line , "|" , 5 )
752+ if len (cols ) != 5 {
753+ continue
754+ }
755+ c := containerInfo {
756+ id : cols [0 ],
757+ name : cols [1 ],
758+ image : cols [2 ],
759+ status : cols [3 ],
760+ running : strings .EqualFold (cols [4 ], "running" ),
761+ }
762+ // Client-side filtering by container ID or name
763+ if containerFilter != "" && c .id != containerFilter && c .name != containerFilter &&
764+ ! strings .HasPrefix (c .id , containerFilter ) {
765+ continue
745766 }
767+ containers = append (containers , c )
768+ }
746769
747- // Try to get CPU stats for this container
748- cpuStat , err := docker .CgroupCPU (c .ContainerID , "" )
770+ // Get live stats via docker stats for running containers
771+ type statsInfo struct {
772+ cpuPerc string
773+ memUsage string
774+ memPerc string
775+ netIO string
776+ blockIO string
777+ pids string
778+ }
779+ statsMap := make (map [string ]statsInfo )
780+
781+ // Only fetch stats if we have containers
782+ if len (containers ) > 0 {
783+ statsArgs := []string {"stats" , "--no-stream" , "--no-trunc" , "--format" , "{{.ID}}|{{.CPUPerc}}|{{.MemUsage}}|{{.MemPerc}}|{{.NetIO}}|{{.BlockIO}}|{{.PIDs}}" }
784+ statsOut , err := exec .CommandContext (ctx , "docker" , statsArgs ... ).Output ()
749785 if err == nil {
750- cInfo ["cpu" ] = map [string ]interface {}{
751- "user" : cpuStat .User ,
752- "system" : cpuStat .System ,
753- "usage" : cpuStat .Usage ,
786+ for _ , line := range strings .Split (strings .TrimSpace (string (statsOut )), "\n " ) {
787+ if line == "" {
788+ continue
789+ }
790+ cols := strings .SplitN (line , "|" , 7 )
791+ if len (cols ) != 7 {
792+ continue
793+ }
794+ statsMap [cols [0 ]] = statsInfo {
795+ cpuPerc : strings .TrimSpace (cols [1 ]),
796+ memUsage : strings .TrimSpace (cols [2 ]),
797+ memPerc : strings .TrimSpace (cols [3 ]),
798+ netIO : strings .TrimSpace (cols [4 ]),
799+ blockIO : strings .TrimSpace (cols [5 ]),
800+ pids : strings .TrimSpace (cols [6 ]),
801+ }
754802 }
755803 }
804+ }
756805
757- // Try to get memory stats for this container
758- memStat , err := docker .CgroupMem (c .ContainerID , "" )
759- if err == nil {
760- cInfo ["memory" ] = map [string ]interface {}{
761- "cache" : memStat .Cache ,
762- "rss" : memStat .RSS ,
763- "rss_human" : config .BytesToHuman (memStat .RSS ),
764- "mapped_file" : memStat .MappedFile ,
765- }
806+ // Build result
807+ containerData := []map [string ]interface {}{}
808+ for _ , c := range containers {
809+ cInfo := map [string ]interface {}{
810+ "container_id" : c .id ,
811+ "name" : c .name ,
812+ "image" : c .image ,
813+ "status" : c .status ,
814+ "running" : c .running ,
815+ }
816+
817+ if stats , ok := statsMap [c .id ]; ok {
818+ cInfo ["cpu_percent" ] = stats .cpuPerc
819+ cInfo ["memory_usage" ] = stats .memUsage
820+ cInfo ["memory_percent" ] = stats .memPerc
821+ cInfo ["network_io" ] = stats .netIO
822+ cInfo ["block_io" ] = stats .blockIO
823+ cInfo ["pids" ] = stats .pids
766824 }
767825
768826 containerData = append (containerData , cInfo )
0 commit comments