Skip to content

Commit 6c0c194

Browse files
Enhance debug logging and fix DevPod JSON parsing (#11)
* Add comprehensive debug logging and fix DevPod JSON parsing - Add executeDevPodCommandWithDebug() helper function with detailed stdout/stderr logging - Update DevPodWorkspace struct to match current DevPod JSON output format - Add debug logging to devpod_listWorkspaces and devpod_addProvider handlers - Fix JSON parsing issues that were causing minimal tool output - Enhanced error reporting and command execution visibility - All DevPod tools now return full detailed output instead of minimal responses * Add comprehensive response logging and fix DevPod provider JSON parsing - Add RESPONSE logging to stdout for all DevPod tool responses - Update DevPodProvider struct to match actual DevPod JSON structure with Config/State - Fix devpod_listProviders to handle provider map structure correctly - Add debug logging to devpod_listProviders with executeDevPodCommandWithDebug - All DevPod tools now return complete structured data with full debug visibility - Provider listing now shows complete configuration and state information - Enhanced debugging capabilities for troubleshooting MCP protocol issues * Fix Go formatting issues - Run gofmt to fix whitespace and alignment issues - Ensure consistent code formatting for linting compliance --------- Co-authored-by: openhands <[email protected]>
1 parent b145bb1 commit 6c0c194

File tree

1 file changed

+160
-25
lines changed

1 file changed

+160
-25
lines changed

main.go

Lines changed: 160 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package main
22

33
import (
4+
"bytes"
45
"context"
56
"encoding/json"
67
"flag"
@@ -21,18 +22,101 @@ var version = "dev"
2122

2223
// DevPodWorkspace represents a DevPod workspace
2324
type DevPodWorkspace struct {
24-
Name string `json:"name"`
25-
Provider string `json:"provider"`
26-
Status string `json:"status"`
27-
IDE string `json:"ide,omitempty"`
25+
ID string `json:"id"`
26+
UID string `json:"uid"`
27+
Picture string `json:"picture,omitempty"`
28+
Provider DevPodWorkspaceProvider `json:"provider"`
29+
Machine map[string]interface{} `json:"machine"`
30+
IDE DevPodWorkspaceIDE `json:"ide"`
31+
Source DevPodWorkspaceSource `json:"source"`
32+
CreationTimestamp string `json:"creationTimestamp"`
33+
LastUsed string `json:"lastUsed"`
34+
Context string `json:"context"`
35+
}
36+
37+
// DevPodWorkspaceProvider represents the provider configuration for a workspace
38+
type DevPodWorkspaceProvider struct {
39+
Name string `json:"name"`
40+
Options map[string]interface{} `json:"options"`
41+
}
42+
43+
// DevPodWorkspaceIDE represents the IDE configuration for a workspace
44+
type DevPodWorkspaceIDE struct {
45+
Name string `json:"name"`
46+
}
47+
48+
// DevPodWorkspaceSource represents the source configuration for a workspace
49+
type DevPodWorkspaceSource struct {
50+
Image string `json:"image,omitempty"`
51+
GitRepository string `json:"gitRepository,omitempty"`
2852
}
2953

3054
// DevPodProvider represents a DevPod provider
3155
type DevPodProvider struct {
32-
Name string `json:"name"`
33-
Description string `json:"description"`
34-
Version string `json:"version"`
35-
Default bool `json:"default"`
56+
Config DevPodProviderConfig `json:"config"`
57+
State DevPodProviderState `json:"state"`
58+
}
59+
60+
// DevPodProviderConfig represents the configuration of a DevPod provider
61+
type DevPodProviderConfig struct {
62+
Name string `json:"name"`
63+
Version string `json:"version"`
64+
Description string `json:"description"`
65+
Icon string `json:"icon,omitempty"`
66+
Home string `json:"home,omitempty"`
67+
Source map[string]interface{} `json:"source"`
68+
OptionGroups []interface{} `json:"optionGroups"`
69+
Options map[string]interface{} `json:"options"`
70+
Agent map[string]interface{} `json:"agent"`
71+
Exec map[string]interface{} `json:"exec"`
72+
}
73+
74+
// DevPodProviderState represents the state of a DevPod provider
75+
type DevPodProviderState struct {
76+
Initialized bool `json:"initialized"`
77+
Options map[string]interface{} `json:"options"`
78+
CreationTimestamp string `json:"creationTimestamp"`
79+
}
80+
81+
// executeDevPodCommandWithDebug executes a DevPod command with comprehensive debug logging
82+
func executeDevPodCommandWithDebug(ctx context.Context, args []string) ([]byte, error) {
83+
log.Printf("DEBUG: Executing devpod command with args: %v", args)
84+
fmt.Fprintf(os.Stderr, "DEBUG: Executing devpod command with args: %v\n", args)
85+
86+
cmd := exec.CommandContext(ctx, "devpod", args...)
87+
88+
// Set environment variables
89+
cmd.Env = os.Environ()
90+
91+
// Capture both stdout and stderr separately for better debugging
92+
var stdout, stderr bytes.Buffer
93+
cmd.Stdout = &stdout
94+
cmd.Stderr = &stderr
95+
96+
err := cmd.Run()
97+
98+
stdoutBytes := stdout.Bytes()
99+
stderrBytes := stderr.Bytes()
100+
stdoutStr := string(stdoutBytes)
101+
stderrStr := string(stderrBytes)
102+
103+
log.Printf("DEBUG: Command completed with error: %v", err)
104+
log.Printf("DEBUG: Command stdout (%d bytes): %q", len(stdoutBytes), stdoutStr)
105+
log.Printf("DEBUG: Command stderr (%d bytes): %q", len(stderrBytes), stderrStr)
106+
107+
fmt.Fprintf(os.Stderr, "DEBUG: Command completed with error: %v\n", err)
108+
fmt.Fprintf(os.Stderr, "DEBUG: Command stdout (%d bytes): %q\n", len(stdoutBytes), stdoutStr)
109+
fmt.Fprintf(os.Stderr, "DEBUG: Command stderr (%d bytes): %q\n", len(stderrBytes), stderrStr)
110+
111+
if err != nil {
112+
log.Printf("ERROR: devpod command failed: %v", err)
113+
fmt.Fprintf(os.Stderr, "ERROR: devpod command failed: %v\n", err)
114+
return nil, fmt.Errorf("devpod command failed: %v, stdout: %s, stderr: %s", err, stdoutStr, stderrStr)
115+
}
116+
117+
log.Printf("DEBUG: Command completed successfully, returning %d bytes", len(stdoutBytes))
118+
fmt.Fprintf(os.Stderr, "DEBUG: Command completed successfully, returning %d bytes\n", len(stdoutBytes))
119+
return stdoutBytes, nil
36120
}
37121

38122
func checkDevPodAvailable() error {
@@ -395,24 +479,44 @@ func registerDevPodHandlers(server *mcp.Server) {
395479
log.Printf("Registering devpod_listWorkspaces handler")
396480
fmt.Fprintf(os.Stderr, "Registering devpod_listWorkspaces handler\n")
397481
server.RegisterHandler("devpod_listWorkspaces", func(ctx context.Context, params json.RawMessage) (interface{}, error) {
482+
log.Printf("DEBUG: devpod_listWorkspaces called with params: %s", string(params))
483+
fmt.Fprintf(os.Stderr, "DEBUG: devpod_listWorkspaces called with params: %s\n", string(params))
484+
398485
if !devpodAvailable {
486+
log.Printf("ERROR: DevPod is not available on this system")
487+
fmt.Fprintf(os.Stderr, "ERROR: DevPod is not available on this system\n")
399488
return nil, fmt.Errorf("DevPod is not available on this system")
400489
}
401-
cmd := exec.CommandContext(ctx, "devpod", "list", "--output", "json")
402-
output, err := cmd.Output()
490+
491+
output, err := executeDevPodCommandWithDebug(ctx, []string{"list", "--output", "json"})
403492
if err != nil {
493+
log.Printf("ERROR: devpod_listWorkspaces failed: %v", err)
494+
fmt.Fprintf(os.Stderr, "ERROR: devpod_listWorkspaces failed: %v\n", err)
404495
return nil, fmt.Errorf("failed to list workspaces: %w", err)
405496
}
406497

407498
var workspaces []DevPodWorkspace
408499
if err := json.Unmarshal(output, &workspaces); err != nil {
500+
log.Printf("DEBUG: JSON parsing failed, trying text parsing. Error: %v", err)
501+
fmt.Fprintf(os.Stderr, "DEBUG: JSON parsing failed, trying text parsing. Error: %v\n", err)
409502
// If JSON parsing fails, try to parse the text output
410-
return parseTextWorkspaceList(string(output)), nil
503+
textResult := parseTextWorkspaceList(string(output))
504+
result := map[string]interface{}{
505+
"workspaces": textResult,
506+
}
507+
log.Printf("DEBUG: devpod_listWorkspaces returning text-parsed result: %v", result)
508+
fmt.Fprintf(os.Stderr, "DEBUG: devpod_listWorkspaces returning text-parsed result: %v\n", result)
509+
fmt.Printf("RESPONSE: devpod_listWorkspaces text-parsed result: %v\n", result)
510+
return result, nil
411511
}
412512

413-
return map[string]interface{}{
513+
result := map[string]interface{}{
414514
"workspaces": workspaces,
415-
}, nil
515+
}
516+
log.Printf("DEBUG: devpod_listWorkspaces returning JSON-parsed result: %v", result)
517+
fmt.Fprintf(os.Stderr, "DEBUG: devpod_listWorkspaces returning JSON-parsed result: %v\n", result)
518+
fmt.Printf("RESPONSE: devpod_listWorkspaces result: %v\n", result)
519+
return result, nil
416520
})
417521

418522
// Create workspace
@@ -548,35 +652,57 @@ func registerDevPodHandlers(server *mcp.Server) {
548652

549653
// List providers
550654
server.RegisterHandler("devpod_listProviders", func(ctx context.Context, params json.RawMessage) (interface{}, error) {
551-
cmd := exec.CommandContext(ctx, "devpod", "provider", "list", "--output", "json")
552-
output, err := cmd.Output()
655+
output, err := executeDevPodCommandWithDebug(ctx, []string{"provider", "list", "--output", "json"})
553656
if err != nil {
657+
log.Printf("ERROR: devpod_listProviders failed: %v", err)
658+
fmt.Fprintf(os.Stderr, "ERROR: devpod_listProviders failed: %v\n", err)
554659
return nil, fmt.Errorf("failed to list providers: %w", err)
555660
}
556661

557-
var providers []DevPodProvider
558-
if err := json.Unmarshal(output, &providers); err != nil {
662+
// DevPod provider list returns an object with provider names as keys
663+
var providersMap map[string]DevPodProvider
664+
if err := json.Unmarshal(output, &providersMap); err != nil {
665+
log.Printf("DEBUG: JSON parsing failed, trying text parsing. Error: %v", err)
666+
fmt.Fprintf(os.Stderr, "DEBUG: JSON parsing failed, trying text parsing. Error: %v\n", err)
559667
// If JSON parsing fails, try to parse the text output
560-
return parseTextProviderList(string(output)), nil
668+
textResult := parseTextProviderList(string(output))
669+
result := map[string]interface{}{
670+
"providers": textResult,
671+
}
672+
log.Printf("DEBUG: devpod_listProviders returning text-parsed result: %v", result)
673+
fmt.Fprintf(os.Stderr, "DEBUG: devpod_listProviders returning text-parsed result: %v\n", result)
674+
fmt.Printf("RESPONSE: devpod_listProviders text-parsed result: %v\n", result)
675+
return result, nil
561676
}
562677

563-
return map[string]interface{}{
564-
"providers": providers,
565-
}, nil
678+
result := map[string]interface{}{
679+
"providers": providersMap,
680+
}
681+
log.Printf("DEBUG: devpod_listProviders returning JSON-parsed result: %v", result)
682+
fmt.Fprintf(os.Stderr, "DEBUG: devpod_listProviders returning JSON-parsed result: %v\n", result)
683+
fmt.Printf("RESPONSE: devpod_listProviders result: %v\n", result)
684+
return result, nil
566685
})
567686

568687
// Add provider
569688
server.RegisterHandler("devpod_addProvider", func(ctx context.Context, params json.RawMessage) (interface{}, error) {
689+
log.Printf("DEBUG: devpod_addProvider called with params: %s", string(params))
690+
fmt.Fprintf(os.Stderr, "DEBUG: devpod_addProvider called with params: %s\n", string(params))
691+
570692
var addParams struct {
571693
Name string `json:"name"`
572694
Options map[string]string `json:"options,omitempty"`
573695
}
574696

575697
if err := json.Unmarshal(params, &addParams); err != nil {
698+
log.Printf("ERROR: Failed to unmarshal addProvider params: %v", err)
699+
fmt.Fprintf(os.Stderr, "ERROR: Failed to unmarshal addProvider params: %v\n", err)
576700
return nil, mcp.NewInvalidParamsError("Invalid add provider parameters")
577701
}
578702

579703
if addParams.Name == "" {
704+
log.Printf("ERROR: Provider name is required")
705+
fmt.Fprintf(os.Stderr, "ERROR: Provider name is required\n")
580706
return nil, mcp.NewInvalidParamsError("Provider name is required")
581707
}
582708

@@ -585,17 +711,26 @@ func registerDevPodHandlers(server *mcp.Server) {
585711
args = append(args, "-o", fmt.Sprintf("%s=%s", key, value))
586712
}
587713

588-
cmd := exec.CommandContext(ctx, "devpod", args...)
589-
output, err := cmd.CombinedOutput()
714+
log.Printf("DEBUG: Executing devpod provider add with args: %v", args)
715+
fmt.Fprintf(os.Stderr, "DEBUG: Executing devpod provider add with args: %v\n", args)
716+
717+
output, err := executeDevPodCommandWithDebug(ctx, args)
590718
if err != nil {
719+
log.Printf("ERROR: devpod_addProvider failed: %v", err)
720+
fmt.Fprintf(os.Stderr, "ERROR: devpod_addProvider failed: %v\n", err)
591721
return nil, fmt.Errorf("failed to add provider: %w\nOutput: %s", err, string(output))
592722
}
593723

594-
return map[string]interface{}{
724+
result := map[string]interface{}{
595725
"name": addParams.Name,
596726
"message": "Provider added successfully",
597727
"output": string(output),
598-
}, nil
728+
}
729+
730+
log.Printf("DEBUG: devpod_addProvider returning result: %v", result)
731+
fmt.Fprintf(os.Stderr, "DEBUG: devpod_addProvider returning result: %v\n", result)
732+
fmt.Printf("RESPONSE: devpod_addProvider result: %v\n", result)
733+
return result, nil
599734
})
600735

601736
// SSH into workspace

0 commit comments

Comments
 (0)