#
tokens: 32614/50000 20/20 files
lines: off (toggle) GitHub
raw markdown copy
# Directory Structure

```
├── .gitignore
├── cmd
│   └── mcp-go-debugger
│       └── main.go
├── docs
│   ├── Implementation-Guide.md
│   ├── Phase1-Tasks.md
│   ├── PRD.md
│   └── Response-Improvements.md
├── go.mod
├── go.sum
├── LICENSE
├── Makefile
├── pkg
│   ├── debugger
│   │   ├── breakpoints.go
│   │   ├── client.go
│   │   ├── execution.go
│   │   ├── helpers.go
│   │   ├── output.go
│   │   ├── program.go
│   │   └── variables.go
│   ├── logger
│   │   └── logger.go
│   ├── mcp
│   │   ├── server_test.go
│   │   └── server.go
│   └── types
│       └── debug_types.go
├── README.md
└── testdata
    └── calculator
        ├── calculator_test.go
        └── calculator.go
```

# Files

--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------

```
bin/
*.log
testdata/sample/sample
.idea

```

--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------

```markdown
# MCP Go Debugger

A debugger interface for Go programs that integrates with MCP (Model Context Protocol).

## Features

- Launch and debug Go applications
- Attach to existing Go processes
- Set breakpoints
- Step through code (step into, step over, step out)
- Eval variables
- View stack traces
- List all variables in current scope
- Get current execution position
- Debug individual test functions with `debug_test`
- Native integration with Delve debugger API types
- Capture and display program output during debugging
- Support for custom test flags when debugging tests
- Detailed variable inspection with configurable depth

## Installation

### Prerequisites

- Go 1.20 or higher

### Using Go

The easiest way to install the MCP Go Debugger is with Go:

```bash
go install github.com/sunfmin/mcp-go-debugger/cmd/mcp-go-debugger@latest
```

This will download, compile, and install the binary to your `$GOPATH/bin` directory.

### From Source

Alternatively, you can build from source:

```bash
git clone https://github.com/sunfmin/mcp-go-debugger.git
cd mcp-go-debugger
make install
```

## Configuration

### Cursor

Add the following to your Cursor configuration (`~/.cursor/mcp.json`):

```json
{
  "mcpServers": {
    "go-debugger": {
      "command": "mcp-go-debugger",
      "args": []
    }
  }
}
```

### Claude Desktop

Add the MCP to Claude Desktop:

```bash
claude mcp add go-debugger mcp-go-debugger
```

Verify the connection:
```
/mcp
```

## Usage

This debugger is designed to be integrated with MCP-compatible clients. The tools provided include:

- `ping` - Test connection to the debugger
- `status` - Check debugger status and server uptime
- `launch` - Launch a Go program with debugging
- `attach` - Attach to a running Go process
- `debug` - Debug a Go source file directly
- `debug_test` - Debug a specific Go test function
- `set_breakpoint` - Set a breakpoint at a specific file and line
- `list_breakpoints` - List all current breakpoints
- `remove_breakpoint` - Remove a breakpoint
- `continue` - Continue execution until next breakpoint or program end
- `step` - Step into the next function call
- `step_over` - Step over the next function call
- `step_out` - Step out of the current function
- `eval_variable` - Eval a variable's value with configurable depth
- `list_scope_variables` - List all variables in current scope (local, args, package)
- `get_execution_position` - Get current execution position (file, line, function)
- `get_debugger_output` - Retrieve captured stdout and stderr from the debugged program
- `close` - Close the current debugging session

### Basic Usage Examples

#### Debugging a Go Program

Ask the AI assistant to debug your Go program:
```
> Please debug my Go application main.go
```

The AI assistant will use the MCP to:
- Launch the program with debugging enabled
- Set breakpoints as needed
- Eval variables and program state
- Help diagnose issues

#### Attaching to a Running Process

If your Go application is already running, you can attach the debugger:

```
> Attach to my Go application running with PID 12345
```

### Finding a Bug Example

```
> I'm getting a panic in my processOrders function when handling large orders. 
> Can you help me debug it?
```

The AI will help you:
1. Set breakpoints in the relevant function
2. Eval variables as execution proceeds
3. Identify the root cause of the issue

#### Debugging a Single Test

If you want to debug a specific test function instead of an entire application:

```
> Please debug the TestCalculateTotal function in my calculator_test.go file
```

The AI assistant will use the `debug_test` tool to:
- Launch only the specific test with debugging enabled
- Set breakpoints at key points in the test
- Help you inspect variables as the test executes
- Step through the test execution to identify issues
- Eval assertion failures or unexpected behavior

You can also specify test flags:

```
> Debug the TestUserAuthentication test in auth_test.go with a timeout of 30 seconds
```

This is especially useful for:
- Tests that are failing inconsistently
- Complex tests with multiple assertions
- Tests involving concurrency or timing issues
- Understanding how a test interacts with your code

#### Inspecting Complex Data Structures

When working with nested structures or complex types:

```
> Can you eval the user.Profile.Preferences object at line 45? I need to see all nested fields in detail.
```

The AI will:
- Set a breakpoint at the specified location
- Use the `eval_variable` tool with appropriate depth parameters
- Format the structure for easier understanding
- Help navigate through nested fields

#### Debugging with Command Line Arguments

To debug a program that requires command-line arguments:

```
> Debug my data_processor.go with the arguments "--input=data.json --verbose --max-records=1000"
```

The debugger will:
- Launch the program with the specified arguments
- Allow you to set breakpoints and eval how the arguments affect execution
- Help track how argument values flow through your program

#### Working with Goroutines

For debugging concurrent Go programs:

```
> I think I have a race condition in my worker pool implementation. Can you help me debug it?
```

The AI can:
- Set strategic breakpoints around goroutine creation and synchronization points
- Help eval channel states and mutex locks
- Track goroutine execution to identify race conditions
- Suggest solutions for concurrency issues

#### Stepping Through Function Calls

For understanding complex control flow:

```
> Walk me through what happens when the processPayment function is called with an invalid credit card
```

The AI assistant will:
- Set breakpoints at the entry point
- Use step-in/step-over commands strategically
- Show you the execution path
- Eval variables at key decision points
- Explain the program flow


## License

[MIT License](LICENSE) 
```

--------------------------------------------------------------------------------
/testdata/calculator/calculator.go:
--------------------------------------------------------------------------------

```go
// Package calculator provides basic arithmetic operations.
package calculator

// Add returns the sum of two integers.
func Add(a, b int) int {
	return a + b
}

// Subtract returns the difference between two integers.
func Subtract(a, b int) int {
	return a - b
}

// Multiply returns the product of two integers.
func Multiply(a, b int) int {
	return a * b
}

// Divide returns the quotient of two integers.
// If b is 0, it returns 0 and doesn't panic.
func Divide(a, b int) int {
	if b == 0 {
		return 0 // Avoid division by zero
	}
	return a / b
} 
```

--------------------------------------------------------------------------------
/cmd/mcp-go-debugger/main.go:
--------------------------------------------------------------------------------

```go
package main

import (
	"os"

	"github.com/mark3labs/mcp-go/server"
	"github.com/sunfmin/mcp-go-debugger/pkg/logger"
	"github.com/sunfmin/mcp-go-debugger/pkg/mcp"
)

// Version is set during build
var Version = "dev"

func main() {
	// Configure additional file logging if needed
	logFile, err := os.OpenFile("mcp-go-debugger.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
	if err == nil {
		defer logFile.Close()
		// We're using the logger package which already sets up logging
		// This file is just for additional logging if needed
	} else {
		logger.Warn("Failed to set up log file", "error", err)
	}

	logger.Info("Starting MCP Go Debugger", "version", Version)

	// Create MCP debug server
	debugServer := mcp.NewMCPDebugServer(Version)

	// Start the stdio server
	logger.Info("Starting MCP server...")
	if err := server.ServeStdio(debugServer.Server()); err != nil {
		logger.Error("Server error", "error", err)
		os.Exit(1)
	}
} 
```

--------------------------------------------------------------------------------
/testdata/calculator/calculator_test.go:
--------------------------------------------------------------------------------

```go
package calculator

import (
	"testing"
)

// TestAdd tests the Add function.
func TestAdd(t *testing.T) {
	// A simple test case that can be debugged
	result := Add(2, 3)
	if result != 5 {
		t.Errorf("Add(2, 3) = %d; want 5", result)
	}
	
	// Additional tests
	cases := []struct {
		a, b, want int
	}{
		{0, 0, 0},
		{-1, 1, 0},
		{10, 5, 15},
	}
	
	for _, tc := range cases {
		got := Add(tc.a, tc.b)
		if got != tc.want {
			t.Errorf("Add(%d, %d) = %d; want %d", tc.a, tc.b, got, tc.want)
		}
	}
}

// TestSubtract tests the Subtract function.
func TestSubtract(t *testing.T) {
	result := Subtract(5, 3)
	if result != 2 {
		t.Errorf("Subtract(5, 3) = %d; want 2", result)
	}
	
	// This assertion will fail deliberately
	// Useful for testing debug capabilities
	if Subtract(5, 5) != 1 { // Deliberately wrong (should be 0)
		// This comment is here to help debug - the expected value should be 0, not 1
		t.Logf("This is a deliberate failure to test debugging")
	}
}

// TestMultiply tests the Multiply function.
func TestMultiply(t *testing.T) {
	cases := []struct {
		a, b, want int
	}{
		{0, 5, 0},
		{1, 1, 1},
		{2, 3, 6},
		{-2, 3, -6},
	}
	
	for _, tc := range cases {
		got := Multiply(tc.a, tc.b)
		if got != tc.want {
			t.Errorf("Multiply(%d, %d) = %d; want %d", tc.a, tc.b, got, tc.want)
		}
	}
}

// TestDivide tests the Divide function.
func TestDivide(t *testing.T) {
	// Test normal division
	result := Divide(10, 2)
	if result != 5 {
		t.Errorf("Divide(10, 2) = %d; want 5", result)
	}
	
	// Test division by zero
	result = Divide(10, 0)
	if result != 0 {
		t.Errorf("Divide(10, 0) = %d; want 0", result)
	}
	
	// Logs for debugging
	t.Logf("Division by zero handled correctly, returned %d", result)
} 
```

--------------------------------------------------------------------------------
/pkg/logger/logger.go:
--------------------------------------------------------------------------------

```go
// Package logger provides a logging utility based on log/slog
//
// DEBUG logging can be enabled by setting the MCP_DEBUG environment variable:
//   export MCP_DEBUG=1
//
// By default, debug logging is disabled to reduce noise in normal operation.
package logger

import (
	"fmt"
	"log/slog"
	"os"
	"strings"
)

var (
	// Logger is the global logger instance
	Logger *slog.Logger
)

func init() {
	// Set up the logger based on environment variable
	debugEnabled := false
	debugEnv := os.Getenv("MCP_DEBUG")
	
	if debugEnv != "" && strings.ToLower(debugEnv) != "false" && debugEnv != "0" {
		debugEnabled = true
	}

	// Create handler with appropriate level
	logLevel := slog.LevelInfo
	if debugEnabled {
		logLevel = slog.LevelDebug
	}

	// Configure the global logger
	opts := &slog.HandlerOptions{
		Level: logLevel,
	}
	
	// You could customize the output format here if needed
	handler := slog.NewTextHandler(os.Stderr, opts)
	Logger = slog.New(handler)

	// Replace the default slog logger too
	slog.SetDefault(Logger)
}

// Debug logs a debug message if debug logging is enabled
func Debug(msg string, args ...any) {
	Logger.Debug(msg, args...)
}

// Info logs an info message
func Info(msg string, args ...any) {
	Logger.Info(msg, args...)
}

// Warn logs a warning message
func Warn(msg string, args ...any) {
	Logger.Warn(msg, args...)
}

// Error logs an error message
func Error(msg string, args ...any) {
	Logger.Error(msg, args...)
}

// Printf logs a formatted message at INFO level
// This is for compatibility with the old log.Printf calls
func Printf(format string, args ...any) {
	message := fmt.Sprintf(format, args...)
	if strings.HasPrefix(message, "DEBUG:") {
		Debug(strings.TrimPrefix(message, "DEBUG:"))
	} else if strings.HasPrefix(message, "Warning:") {
		Warn(strings.TrimPrefix(message, "Warning:"))
	} else if strings.HasPrefix(message, "Error:") {
		Error(strings.TrimPrefix(message, "Error:"))
	} else {
		Info(message)
	}
}

// Println logs a message at INFO level
// This is for compatibility with the old log.Println calls
func Println(args ...any) {
	message := fmt.Sprintln(args...)
	message = strings.TrimSuffix(message, "\n") // Remove trailing newline
	
	if strings.HasPrefix(message, "DEBUG:") {
		Debug(strings.TrimPrefix(message, "DEBUG:"))
	} else if strings.HasPrefix(message, "Warning:") {
		Warn(strings.TrimPrefix(message, "Warning:"))
	} else if strings.HasPrefix(message, "Error:") {
		Error(strings.TrimPrefix(message, "Error:"))
	} else {
		Info(message)
	}
} 
```

--------------------------------------------------------------------------------
/pkg/debugger/output.go:
--------------------------------------------------------------------------------

```go
package debugger

import (
	"fmt"
	"strings"
	"time"

	"github.com/sunfmin/mcp-go-debugger/pkg/types"
)

// OutputMessage represents a captured output message
type OutputMessage struct {
	Source    string    `json:"source"` // "stdout" or "stderr"
	Content   string    `json:"content"`
	Timestamp time.Time `json:"timestamp"`
}

// GetDebuggerOutput returns the captured stdout and stderr from the debugged program
func (c *Client) GetDebuggerOutput() types.DebuggerOutputResponse {
	if c.client == nil {
		return types.DebuggerOutputResponse{
			Status: "error",
			Context: types.DebugContext{
				Timestamp:    time.Now(),
				Operation:    "get_output",
				ErrorMessage: "no active debug session",
			},
		}
	}

	// Get the captured output regardless of state
	c.outputMutex.Lock()
	stdout := c.stdout.String()
	stderr := c.stderr.String()
	c.outputMutex.Unlock()

	// Create a summary of the output for LLM
	outputSummary := generateOutputSummary(stdout, stderr)

	// Try to get state, but don't fail if unable
	state, err := c.client.GetState()
	if err != nil {
		// Process might have exited, but we still want to return the captured output
		return types.DebuggerOutputResponse{
			Status: "success",
			Context: types.DebugContext{
				Timestamp:    time.Now(),
				Operation:    "get_output",
				ErrorMessage: fmt.Sprintf("state unavailable: %v", err),
			},
			Stdout:        stdout,
			Stderr:        stderr,
			OutputSummary: outputSummary,
		}
	}

	context := c.createDebugContext(state)
	context.Operation = "get_output"

	return types.DebuggerOutputResponse{
		Status:        "success",
		Context:       context,
		Stdout:        stdout,
		Stderr:        stderr,
		OutputSummary: outputSummary,
	}
}

// generateOutputSummary creates a concise summary of stdout and stderr for LLM use
func generateOutputSummary(stdout, stderr string) string {
	// If no output, return a simple message
	if stdout == "" && stderr == "" {
		return "No program output captured"
	}

	var summary strings.Builder

	// Add stdout summary
	if stdout != "" {
		lines := strings.Split(strings.TrimSpace(stdout), "\n")
		if len(lines) <= 5 {
			summary.WriteString(fmt.Sprintf("Program output (%d lines): %s", len(lines), stdout))
		} else {
			// Summarize with first 3 and last 2 lines
			summary.WriteString(fmt.Sprintf("Program output (%d lines): \n", len(lines)))
			for i := 0; i < 3 && i < len(lines); i++ {
				summary.WriteString(fmt.Sprintf("  %s\n", lines[i]))
			}
			summary.WriteString("  ... more lines ...\n")
			for i := len(lines) - 2; i < len(lines); i++ {
				summary.WriteString(fmt.Sprintf("  %s\n", lines[i]))
			}
		}
	}

	// Add stderr if present
	if stderr != "" {
		if summary.Len() > 0 {
			summary.WriteString("\n")
		}
		lines := strings.Split(strings.TrimSpace(stderr), "\n")
		if len(lines) <= 3 {
			summary.WriteString(fmt.Sprintf("Error output (%d lines): %s", len(lines), stderr))
		} else {
			summary.WriteString(fmt.Sprintf("Error output (%d lines): \n", len(lines)))
			for i := 0; i < 2 && i < len(lines); i++ {
				summary.WriteString(fmt.Sprintf("  %s\n", lines[i]))
			}
			summary.WriteString("  ... more lines ...\n")
			summary.WriteString(fmt.Sprintf("  %s\n", lines[len(lines)-1]))
		}
	}

	return summary.String()
}

```

--------------------------------------------------------------------------------
/pkg/debugger/client.go:
--------------------------------------------------------------------------------

```go
// Package debugger provides an interface to the Delve debugger
package debugger

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"net"
	"sync"
	"time"

	"github.com/go-delve/delve/service/api"
	"github.com/go-delve/delve/service/rpc2"
	"github.com/go-delve/delve/service/rpccommon"
	"github.com/sunfmin/mcp-go-debugger/pkg/types"
)

// Client encapsulates the Delve debug client functionality
type Client struct {
	client      *rpc2.RPCClient
	target      string
	pid         int
	server      *rpccommon.ServerImpl
	tempDir     string
	stdout      bytes.Buffer       // Buffer for captured stdout
	stderr      bytes.Buffer       // Buffer for captured stderr
	outputChan  chan OutputMessage // Channel for captured output
	stopOutput  chan struct{}      // Channel to signal stopping output capture
	outputMutex sync.Mutex         // Mutex for synchronizing output buffer access
}

// NewClient creates a new Delve client wrapper
func NewClient() *Client {
	return &Client{
		outputChan: make(chan OutputMessage, 100), // Buffer for output messages
		stopOutput: make(chan struct{}),
	}
}

// GetTarget returns the target program being debugged
func (c *Client) GetTarget() string {
	return c.target
}

// GetPid returns the PID of the process being debugged
func (c *Client) GetPid() int {
	return c.pid
}

// Helper function to get an available port
func getFreePort() (int, error) {
	addr, err := net.ResolveTCPAddr("tcp", "localhost:0")
	if err != nil {
		return 0, err
	}

	l, err := net.ListenTCP("tcp", addr)
	if err != nil {
		return 0, err
	}
	defer l.Close()
	return l.Addr().(*net.TCPAddr).Port, nil
}

// Helper function to wait for server to be available
func waitForServer(addr string) error {
	timeout := time.Now().Add(5 * time.Second)
	for time.Now().Before(timeout) {
		conn, err := net.DialTimeout("tcp", addr, 100*time.Millisecond)
		if err == nil {
			conn.Close()
			return nil
		}
		time.Sleep(100 * time.Millisecond)
	}
	return fmt.Errorf("timeout waiting for server")
}

// waitForStop polls the debugger until it reaches a stopped state or times out
func waitForStop(c *Client, timeout time.Duration) (*api.DebuggerState, error) {
	deadline := time.Now().Add(timeout)
	for time.Now().Before(deadline) {
		state, err := c.client.GetState()
		if err != nil {
			return nil, fmt.Errorf("failed to get debugger state: %v", err)
		}

		// Check if the program has stopped
		if !state.Running {
			return state, nil
		}

		// Wait a bit before checking again
		time.Sleep(100 * time.Millisecond)
	}

	return nil, fmt.Errorf("timeout waiting for program to stop")
}

// captureOutput reads from a reader and sends the output to the output channel and buffer
func (c *Client) captureOutput(reader io.ReadCloser, source string) {
	defer reader.Close()

	scanner := bufio.NewScanner(reader)
	for scanner.Scan() {
		line := scanner.Text()

		// Write to appropriate buffer
		if source == "stdout" {
			c.stdout.WriteString(line + "\n")
		} else if source == "stderr" {
			c.stderr.WriteString(line + "\n")
		}

		// Also send to channel for real-time monitoring
		select {
		case <-c.stopOutput:
			return
		case c.outputChan <- OutputMessage{
			Source:    source,
			Content:   line,
			Timestamp: time.Now(),
		}:
		}
	}
}

// createDebugContext creates a debug context from a state
func (c *Client) createDebugContext(state *api.DebuggerState) types.DebugContext {
	context := types.DebugContext{
		Timestamp: time.Now(),
		Operation: "",
	}

	if state != nil {
		context.DelveState = state

		// Add current position
		if state.CurrentThread != nil {
			loc := getCurrentLocation(state)
			context.CurrentLocation = loc
		}

		// Add stop reason
		context.StopReason = getStateReason(state)

		// Get local variables if we have a client
		if c != nil {
			context.LocalVariables, _ = c.getLocalVariables(state)
		}
	}

	return context
}

```

--------------------------------------------------------------------------------
/pkg/debugger/helpers.go:
--------------------------------------------------------------------------------

```go
package debugger

import (
	"fmt"
	"github.com/go-delve/delve/service/api"
	"strings"
)

// getFunctionName extracts a human-readable function name from various Delve types
func getFunctionName(thread *api.Thread) string {
	if thread == nil || thread.Function == nil {
		return "unknown"
	}
	return thread.Function.Name()
}

// getPackageName extracts the package name from a function name
func getPackageName(thread *api.Thread) string {
	if thread == nil || thread.Function == nil {
		return "unknown"
	}
	parts := strings.Split(thread.Function.Name(), ".")
	if len(parts) > 1 {
		return parts[0]
	}
	return "unknown"
}

// getFunctionNameFromBreakpoint extracts a human-readable function name from a breakpoint
func getFunctionNameFromBreakpoint(bp *api.Breakpoint) string {
	if bp == nil || bp.FunctionName == "" {
		return "unknown"
	}
	return bp.FunctionName
}

// getPackageNameFromBreakpoint extracts the package name from a breakpoint
func getPackageNameFromBreakpoint(bp *api.Breakpoint) string {
	if bp == nil || bp.FunctionName == "" {
		return "unknown"
	}
	parts := strings.Split(bp.FunctionName, ".")
	if len(parts) > 1 {
		return parts[0]
	}
	return "unknown"
}

// getThreadStatus returns a human-readable thread status
func getThreadStatus(thread *api.Thread) string {
	if thread == nil {
		return "unknown"
	}
	if thread.Breakpoint != nil {
		return "at breakpoint"
	}
	return "running"
}

// getGoroutineStatus returns a human-readable status for a goroutine
func getGoroutineStatus(g *api.Goroutine) string {
	if g == nil {
		return "unknown"
	}
	switch g.Status {
	case 0:
		return "running"
	case 1:
		return "sleeping"
	case 2:
		return "blocked"
	case 3:
		return "waiting"
	case 4:
		return "dead"
	default:
		return fmt.Sprintf("unknown status %d", g.Status)
	}
}

// getWaitReason returns a human-readable wait reason for a goroutine
func getWaitReason(g *api.Goroutine) string {
	if g == nil || g.WaitReason == 0 {
		return ""
	}

	// Based on runtime/runtime2.go waitReasons
	switch g.WaitReason {
	case 1:
		return "waiting for GC cycle"
	case 2:
		return "waiting for GC (write barrier)"
	case 3:
		return "waiting for GC (mark assist)"
	case 4:
		return "waiting for finalizer"
	case 5:
		return "waiting for channel operation"
	case 6:
		return "waiting for select operation"
	case 7:
		return "waiting for mutex/rwmutex"
	case 8:
		return "waiting for concurrent map operation"
	case 9:
		return "waiting for garbage collection scan"
	case 10:
		return "waiting for channel receive"
	case 11:
		return "waiting for channel send"
	case 12:
		return "waiting for semaphore"
	case 13:
		return "waiting for sleep"
	case 14:
		return "waiting for timer"
	case 15:
		return "waiting for defer"
	default:
		return fmt.Sprintf("unknown wait reason %d", g.WaitReason)
	}
}

// getBreakpointStatus returns a human-readable breakpoint status
func getBreakpointStatus(bp *api.Breakpoint) string {
	if bp.Disabled {
		return "disabled"
	}
	if bp.TotalHitCount > 0 {
		return "hit"
	}
	return "enabled"
}

// getStateReason returns a human-readable reason for the current state
func getStateReason(state *api.DebuggerState) string {
	if state == nil {
		return "unknown"
	}

	if state.Exited {
		return fmt.Sprintf("process exited with status %d", state.ExitStatus)
	}

	if state.Running {
		return "process is running"
	}

	if state.CurrentThread != nil && state.CurrentThread.Breakpoint != nil {
		return "hit breakpoint"
	}

	return "process is stopped"
}

// getCurrentLocation gets the current location from a DebuggerState
func getCurrentLocation(state *api.DebuggerState) *string {
	if state == nil || state.CurrentThread == nil {
		return nil
	}
	if state.CurrentThread.File == "" || state.CurrentThread.Function == nil {
		return nil
	}

	r := fmt.Sprintf("At %s:%d in %s", state.CurrentThread.File, state.CurrentThread.Line, getFunctionName(state.CurrentThread))
	return &r
}

func getBreakpointLocation(bp *api.Breakpoint) *string {
	r := fmt.Sprintf("At %s:%d in %s", bp.File, bp.Line, getFunctionNameFromBreakpoint(bp))
	return &r
}

```

--------------------------------------------------------------------------------
/docs/Implementation-Guide.md:
--------------------------------------------------------------------------------

```markdown
# MCP Go Debugger Implementation Guide

This guide outlines best practices for implementing features in the MCP Go Debugger project.

## Development Workflow

When implementing a new feature or modifying existing code, follow these steps:

1. **Understand the Task**: Thoroughly review the relevant task description.
2. **Explore the Codebase**: Identify relevant files and understand existing patterns.
3. **Make Focused Changes**: Implement one coherent feature/fix at a time.
4. **Write Tests**: Add tests for the new functionality.
5. **Validate**: Run tests to ensure all tests pass.
6. **Commit**: Commit changes with descriptive messages.
7. **Repeat**: Only after successfully completing a change, move to the next task.

## Understanding the Codebase

Before making changes:

1. Explore the project structure
2. Identify key files and packages
3. Review existing tests for similar functionality

## Implementation Strategies

### Adding a New Feature

When adding a new feature:

1. **Review Similar Features**: Look at how similar features are implemented.
2. **Follow Conventions**: Match existing code style and patterns.
3. **Implement Core Logic**: Create the main functionality first.
4. **Add Error Handling**: Ensure proper error cases are handled.
5. **Update Interfaces**: Update any interfaces or API definitions.

### Modifying Existing Code

When modifying existing code:

1. **Locate Target Code**: Find all related code sections that need changes.
2. **Understand Dependencies**: Identify code that depends on what you're changing.
3. **Make Minimal Changes**: Change only what's necessary for the feature.
4. **Preserve Behavior**: Ensure existing functionality remains intact unless deliberately changing it.

## Test-Driven Development

Follow these testing principles:

1. **Write Tests First**: When possible, write tests before implementing features.
2. **Test Edge Cases**: Include tests for error conditions and edge cases.
3. **Ensure Coverage**: Aim for good test coverage of new functionality.
4. **Test Commands**: For each MCP command, test both success and failure paths.

## Running Tests

Run tests before committing:

```sh
go test ./...
```

For specific packages:

```sh
go test ./pkg/debugger
```

With verbose output:

```sh
go test -v ./...
```

## Git Workflow

For each feature implementation:

1. **Start Clean**: Begin with a clean working directory.
2. **Feature Branch**: Consider using feature branches for significant changes.
3. **Atomic Commits**: Make focused commits that implement one logical change.
4. **Descriptive Messages**: Use clear commit messages following this format:
   ```
   [Component] Brief description of change
   
   More detailed explanation if needed
   ```
5. **Verify Before Commit**: Always run tests before committing.

### Sample Commit Workflow

```sh
# Run tests to ensure everything passes before changes
go test ./...

# Make your changes

# Run tests again to verify changes
go test ./...

# Commit changes with descriptive message
git add .
git commit -m "[Debugger] Implement set_breakpoint command"
```

## Troubleshooting

If tests fail:

1. Review the test output carefully
2. Check for any side effects from your changes
3. Verify that you've updated all necessary parts of the codebase
4. Consider running tests with the `-v` flag for more details

## Documentation

Update documentation when:

1. Adding new commands or features
2. Changing existing behavior
3. Fixing non-obvious bugs

## API Documentation

Always use `go doc` instead of searching the internet when finding out Go APIs. This ensures that you're referencing the exact API version in your dependencies:

```sh
# View documentation for a package
go doc github.com/go-delve/delve/service

# View documentation for a specific type
go doc github.com/go-delve/delve/service.Config

# View documentation for a function or method
go doc github.com/go-delve/delve/service.NewServer

# For more detailed documentation including unexported items
go doc -all github.com/go-delve/delve/service
```

This approach ensures you're working with the precise API that's included in your project's dependencies rather than potentially outdated or incompatible online resources.

## Next Steps

After implementing a feature:

1. Consider if additional tests would be valuable
2. Look for opportunities to refactor or improve the code
3. Update the task status in Phase1-Tasks.md
4. Move on to the next related task 
```

--------------------------------------------------------------------------------
/pkg/debugger/breakpoints.go:
--------------------------------------------------------------------------------

```go
package debugger

import (
	"fmt"
	"time"

	"github.com/go-delve/delve/service/api"
	"github.com/sunfmin/mcp-go-debugger/pkg/logger"
	"github.com/sunfmin/mcp-go-debugger/pkg/types"
)

// SetBreakpoint sets a breakpoint at the specified file and line
func (c *Client) SetBreakpoint(file string, line int) types.BreakpointResponse {
	if c.client == nil {
		return types.BreakpointResponse{
			Status: "error",
			Context: types.DebugContext{
				ErrorMessage: "no active debug session",
				Timestamp:    getCurrentTimestamp(),
			},
		}
	}

	logger.Debug("Setting breakpoint at %s:%d", file, line)
	bp, err := c.client.CreateBreakpoint(&api.Breakpoint{
		File: file,
		Line: line,
	})

	if err != nil {
		return types.BreakpointResponse{
			Status: "error",
			Context: types.DebugContext{
				ErrorMessage: fmt.Sprintf("failed to set breakpoint: %v", err),
				Timestamp:    getCurrentTimestamp(),
			},
		}
	}

	// Get current state for context
	state, err := c.client.GetState()
	if err != nil {
		logger.Debug("Warning: Failed to get state after setting breakpoint: %v", err)
	}

	breakpoint := &types.Breakpoint{
		DelveBreakpoint: bp,
		ID:              bp.ID,
		Status:          getBreakpointStatus(bp),
		Location:        getBreakpointLocation(bp),
		HitCount:        uint64(bp.TotalHitCount),
	}

	context := c.createDebugContext(state)
	context.Operation = "set_breakpoint"

	return types.BreakpointResponse{
		Status:     "success",
		Context:    context,
		Breakpoint: *breakpoint,
	}
}

// ListBreakpoints returns all currently set breakpoints
func (c *Client) ListBreakpoints() types.BreakpointListResponse {
	if c.client == nil {
		return types.BreakpointListResponse{
			Status: "error",
			Context: types.DebugContext{
				ErrorMessage: "no active debug session",
				Timestamp:    getCurrentTimestamp(),
			},
		}
	}

	bps, err := c.client.ListBreakpoints(false)
	if err != nil {
		return types.BreakpointListResponse{
			Status: "error",
			Context: types.DebugContext{
				ErrorMessage: fmt.Sprintf("failed to list breakpoints: %v", err),
				Timestamp:    getCurrentTimestamp(),
			},
		}
	}

	var breakpoints []types.Breakpoint
	for _, bp := range bps {
		breakpoints = append(breakpoints, types.Breakpoint{
			DelveBreakpoint: bp,
			ID:              bp.ID,
			Status:          getBreakpointStatus(bp),
			Location:        getBreakpointLocation(bp),
			HitCount:        uint64(bp.TotalHitCount),
		})
	}

	// Get current state for context
	state, err := c.client.GetState()
	if err != nil {
		logger.Debug("Warning: Failed to get state while listing breakpoints: %v", err)
	}

	context := c.createDebugContext(state)
	context.Operation = "list_breakpoints"

	return types.BreakpointListResponse{
		Status:      "success",
		Context:     context,
		Breakpoints: breakpoints,
	}
}

// RemoveBreakpoint removes a breakpoint by its ID
func (c *Client) RemoveBreakpoint(id int) types.BreakpointResponse {
	if c.client == nil {
		return types.BreakpointResponse{
			Status: "error",
			Context: types.DebugContext{
				ErrorMessage: "no active debug session",
				Timestamp:    getCurrentTimestamp(),
			},
		}
	}

	// Get breakpoint info before removing
	bps, err := c.client.ListBreakpoints(false)
	if err != nil {
		return types.BreakpointResponse{
			Status: "error",
			Context: types.DebugContext{
				ErrorMessage: fmt.Sprintf("failed to get breakpoint info: %v", err),
				Timestamp:    getCurrentTimestamp(),
			},
		}
	}

	var targetBp *api.Breakpoint
	for _, bp := range bps {
		if bp.ID == id {
			targetBp = bp
			break
		}
	}

	if targetBp == nil {
		return types.BreakpointResponse{
			Status: "error",
			Context: types.DebugContext{
				ErrorMessage: fmt.Sprintf("breakpoint %d not found", id),
				Timestamp:    getCurrentTimestamp(),
			},
		}
	}

	logger.Debug("Removing breakpoint %d at %s:%d", id, targetBp.File, targetBp.Line)
	_, err = c.client.ClearBreakpoint(id)
	if err != nil {
		return types.BreakpointResponse{
			Status: "error",
			Context: types.DebugContext{
				ErrorMessage: fmt.Sprintf("failed to remove breakpoint: %v", err),
				Timestamp:    getCurrentTimestamp(),
			},
		}
	}

	// Get current state for context
	state, err := c.client.GetState()
	if err != nil {
		logger.Debug("Warning: Failed to get state after removing breakpoint: %v", err)
	}

	breakpoint := types.Breakpoint{
		DelveBreakpoint: targetBp,
		ID:              targetBp.ID,
		Status:          "removed",
		Location:        getBreakpointLocation(targetBp),
		HitCount:        uint64(targetBp.TotalHitCount),
	}

	context := c.createDebugContext(state)
	context.Operation = "remove_breakpoint"

	return types.BreakpointResponse{
		Status:     "success",
		Context:    context,
		Breakpoint: breakpoint,
	}
}

func getCurrentTimestamp() time.Time {
	return time.Now()
}

```

--------------------------------------------------------------------------------
/pkg/debugger/execution.go:
--------------------------------------------------------------------------------

```go
package debugger

import (
	"fmt"
	"time"

	"github.com/go-delve/delve/service/api"
	"github.com/sunfmin/mcp-go-debugger/pkg/logger"
	"github.com/sunfmin/mcp-go-debugger/pkg/types"
)

// Continue resumes program execution until next breakpoint or program termination
func (c *Client) Continue() types.ContinueResponse {
	if c.client == nil {
		return c.createContinueResponse(nil, fmt.Errorf("no active debug session"))
	}

	logger.Debug("Continuing execution")

	// Continue returns a channel that will receive state updates
	stateChan := c.client.Continue()

	// Wait for the state update from the channel
	delveState := <-stateChan
	if delveState.Err != nil {
		return c.createContinueResponse(nil, fmt.Errorf("continue command failed: %v", delveState.Err))
	}

	return c.createContinueResponse(delveState, nil)
}

// Step executes a single instruction, stepping into function calls
func (c *Client) Step() types.StepResponse {
	if c.client == nil {
		return c.createStepResponse(nil, "into", nil, fmt.Errorf("no active debug session"))
	}

	// Check if program is running or not stopped
	delveState, err := c.client.GetState()
	if err != nil {
		return c.createStepResponse(nil, "into", nil, fmt.Errorf("failed to get state: %v", err))
	}

	fromLocation := getCurrentLocation(delveState)

	if delveState.Running {
		logger.Debug("Warning: Cannot step when program is running, waiting for program to stop")
		stoppedState, err := waitForStop(c, 2*time.Second)
		if err != nil {
			return c.createStepResponse(nil, "into", fromLocation, fmt.Errorf("failed to wait for program to stop: %v", err))
		}
		delveState = stoppedState
	}

	logger.Debug("Stepping into")
	nextState, err := c.client.Step()
	if err != nil {
		return c.createStepResponse(nil, "into", fromLocation, fmt.Errorf("step into command failed: %v", err))
	}

	return c.createStepResponse(nextState, "into", fromLocation, nil)
}

// StepOver executes the next instruction, stepping over function calls
func (c *Client) StepOver() types.StepResponse {
	if c.client == nil {
		return c.createStepResponse(nil, "over", nil, fmt.Errorf("no active debug session"))
	}

	// Check if program is running or not stopped
	delveState, err := c.client.GetState()
	if err != nil {
		return c.createStepResponse(nil, "over", nil, fmt.Errorf("failed to get state: %v", err))
	}

	fromLocation := getCurrentLocation(delveState)

	if delveState.Running {
		logger.Debug("Warning: Cannot step when program is running, waiting for program to stop")
		stoppedState, err := waitForStop(c, 2*time.Second)
		if err != nil {
			return c.createStepResponse(nil, "over", fromLocation, fmt.Errorf("failed to wait for program to stop: %v", err))
		}
		delveState = stoppedState
	}

	logger.Debug("Stepping over next line")
	nextState, err := c.client.Next()
	if err != nil {
		return c.createStepResponse(nil, "over", fromLocation, fmt.Errorf("step over command failed: %v", err))
	}

	return c.createStepResponse(nextState, "over", fromLocation, nil)
}

// StepOut executes until the current function returns
func (c *Client) StepOut() types.StepResponse {
	if c.client == nil {
		return c.createStepResponse(nil, "out", nil, fmt.Errorf("no active debug session"))
	}

	// Check if program is running or not stopped
	delveState, err := c.client.GetState()
	if err != nil {
		return c.createStepResponse(nil, "out", nil, fmt.Errorf("failed to get state: %v", err))
	}

	fromLocation := getCurrentLocation(delveState)

	if delveState.Running {
		logger.Debug("Warning: Cannot step out when program is running, waiting for program to stop")
		stoppedState, err := waitForStop(c, 2*time.Second)
		if err != nil {
			return c.createStepResponse(nil, "out", fromLocation, fmt.Errorf("failed to wait for program to stop: %v", err))
		}
		delveState = stoppedState
	}

	logger.Debug("Stepping out")
	nextState, err := c.client.StepOut()
	if err != nil {
		return c.createStepResponse(nil, "out", fromLocation, fmt.Errorf("step out command failed: %v", err))
	}

	return c.createStepResponse(nextState, "out", fromLocation, nil)
}

// createContinueResponse creates a ContinueResponse from a DebuggerState
func (c *Client) createContinueResponse(state *api.DebuggerState, err error) types.ContinueResponse {
	context := c.createDebugContext(state)
	if err != nil {
		context.ErrorMessage = err.Error()
		return types.ContinueResponse{
			Status:  "error",
			Context: context,
		}
	}

	return types.ContinueResponse{
		Status:  "success",
		Context: context,
	}
}

// createStepResponse creates a StepResponse from a DebuggerState
func (c *Client) createStepResponse(state *api.DebuggerState, stepType string, fromLocation *string, err error) types.StepResponse {
	context := c.createDebugContext(state)
	if err != nil {
		context.ErrorMessage = err.Error()
		return types.StepResponse{
			Status:  "error",
			Context: context,
		}
	}

	return types.StepResponse{
		Status:       "success",
		Context:      context,
		StepType:     stepType,
		FromLocation: fromLocation,
	}
}

```

--------------------------------------------------------------------------------
/pkg/types/debug_types.go:
--------------------------------------------------------------------------------

```go
package types

import (
	"time"

	"github.com/go-delve/delve/service/api"
)

// DebugContext provides shared context across all debug responses
type DebugContext struct {
	DelveState      *api.DebuggerState `json:"-"`                         // Internal Delve state
	Timestamp       time.Time          `json:"timestamp"`                 // Operation timestamp
	Operation       string             `json:"operation,omitempty"`       // Last debug operation performed
	CurrentLocation *string            `json:"currentLocation,omitempty"` // Current execution position
	LocalVariables  []Variable         `json:"localVariables,omitempty"`
	// LLM-friendly additions
	StopReason   string `json:"stopReason,omitempty"` // Why the program stopped, in human terms
	ErrorMessage string `json:"error,omitempty"`      // Error message if any
}

// Variable represents a program variable with LLM-friendly additions
type Variable struct {
	// Internal Delve variable - not exposed in JSON
	DelveVar *api.Variable `json:"-"`

	// LLM-friendly fields
	Name  string `json:"name"`  // Variable name
	Value string `json:"value"` // Formatted value in human-readable form
	Type  string `json:"type"`  // Type in human-readable format
	Scope string `json:"scope"` // Variable scope (local, global, etc)
	Kind  string `json:"kind"`  // High-level kind description
}

// Breakpoint represents a breakpoint with LLM-friendly additions
type Breakpoint struct {
	// Internal Delve breakpoint - not exposed in JSON
	DelveBreakpoint *api.Breakpoint `json:"-"`

	// LLM-friendly fields
	ID          int      `json:"id"`                  // Breakpoint ID
	Status      string   `json:"status"`              // Enabled/Disabled/etc in human terms
	Location    *string  `json:"location"`            // Breakpoint location
	Variables   []string `json:"variables,omitempty"` // Variables in scope
	Condition   string   `json:"condition,omitempty"` // Human-readable condition description
	HitCount    uint64   `json:"hitCount"`            // Number of times breakpoint was hit
	LastHitInfo string   `json:"lastHit,omitempty"`   // Information about last hit in human terms
}

// DebuggerOutput represents captured program output with LLM-friendly additions
type DebuggerOutput struct {
	// Internal Delve state - not exposed in JSON
	DelveState *api.DebuggerState `json:"-"`

	// LLM-friendly fields
	Stdout        string       `json:"stdout"`        // Captured standard output
	Stderr        string       `json:"stderr"`        // Captured standard error
	OutputSummary string       `json:"outputSummary"` // Brief summary of output for LLM
	Context       DebugContext `json:"context"`       // Common debugging context
	ExitCode      int          `json:"exitCode"`      // Program exit code if available
}

// Operation-specific responses

type LaunchResponse struct {
	Context  *DebugContext `json:"context"`
	Program  string        `json:"program"`
	Args     []string      `json:"args"`
	ExitCode int           `json:"exitCode"`
}

type BreakpointResponse struct {
	Status     string       `json:"status"`
	Context    DebugContext `json:"context"`
	Breakpoint Breakpoint   `json:"breakpoint"` // The affected breakpoint
}

type BreakpointListResponse struct {
	Status      string       `json:"status"`
	Context     DebugContext `json:"context"`
	Breakpoints []Breakpoint `json:"breakpoints"` // All current breakpoints
}

type StepResponse struct {
	Status       string       `json:"status"`
	Context      DebugContext `json:"context"`
	StepType     string       `json:"stepType"`    // "into", "over", or "out"
	FromLocation *string      `json:"from"`        // Starting location
	ChangedVars  []Variable   `json:"changedVars"` // Variables that changed during step
}

type EvalVariableResponse struct {
	Status   string       `json:"status"`
	Context  DebugContext `json:"context"`
	Variable Variable     `json:"variable"` // The evald variable
}

type ContinueResponse struct {
	Status  string       `json:"status"`
	Context DebugContext `json:"context"`
}

type CloseResponse struct {
	Status   string       `json:"status"`
	Context  DebugContext `json:"context"`
	ExitCode int          `json:"exitCode"` // Program exit code
	Summary  string       `json:"summary"`  // Session summary for LLM
}

type DebuggerOutputResponse struct {
	Status        string       `json:"status"`
	Context       DebugContext `json:"context"`
	Stdout        string       `json:"stdout"`        // Captured standard output
	Stderr        string       `json:"stderr"`        // Captured standard error
	OutputSummary string       `json:"outputSummary"` // Brief summary of output for LLM
}

type AttachResponse struct {
	Status  string        `json:"status"`
	Context *DebugContext `json:"context"`
	Pid     int           `json:"pid"`
	Target  string        `json:"target"`
	Process *Process      `json:"process"`
}

type DebugSourceResponse struct {
	Status      string        `json:"status"`
	Context     *DebugContext `json:"context"`
	SourceFile  string        `json:"sourceFile"`
	DebugBinary string        `json:"debugBinary"`
	Args        []string      `json:"args"`
}

type DebugTestResponse struct {
	Status       string        `json:"status"`
	Context      *DebugContext `json:"context"`
	TestFile     string        `json:"testFile"`
	TestName     string        `json:"testName"`
	BuildCommand string        `json:"buildCommand"`
	BuildOutput  string        `json:"buildOutput"`
	DebugBinary  string        `json:"debugBinary"`
	Process      *Process      `json:"process"`
	TestFlags    []string      `json:"testFlags"`
}

// Process represents a debugged process with LLM-friendly additions
type Process struct {
	Pid         int      `json:"pid"`         // Process ID
	Name        string   `json:"name"`        // Process name
	CmdLine     []string `json:"cmdLine"`     // Command line arguments
	Status      string   `json:"status"`      // Process status (running, stopped, etc.)
	Summary     string   `json:"summary"`     // Brief description of process state
	ExitCode    int      `json:"exitCode"`    // Exit code if process has terminated
	ExitMessage string   `json:"exitMessage"` // Exit message if process has terminated
}

```

--------------------------------------------------------------------------------
/pkg/debugger/variables.go:
--------------------------------------------------------------------------------

```go
package debugger

import (
	"fmt"
	"reflect"
	"strings"

	"github.com/go-delve/delve/service/api"
	"github.com/sunfmin/mcp-go-debugger/pkg/types"
)

// EvalVariable evaluates a variable expression
func (c *Client) EvalVariable(name string, depth int) types.EvalVariableResponse {
	if c.client == nil {
		return c.createEvalVariableResponse(nil, nil, 0, fmt.Errorf("no active debug session"))
	}

	// Get current state for context
	state, err := c.client.GetState()
	if err != nil {
		return c.createEvalVariableResponse(nil, nil, 0, fmt.Errorf("failed to get state: %v", err))
	}

	if state.SelectedGoroutine == nil {
		return c.createEvalVariableResponse(state, nil, 0, fmt.Errorf("no goroutine selected"))
	}

	// Create the evaluation scope
	scope := api.EvalScope{
		GoroutineID: state.SelectedGoroutine.ID,
		Frame:       0,
	}

	// Configure loading with proper struct field handling
	loadConfig := api.LoadConfig{
		FollowPointers:     true,
		MaxVariableRecurse: depth,
		MaxStringLen:       1024,
		MaxArrayValues:     100,
		MaxStructFields:    -1, // Load all struct fields
	}

	// Evaluate the variable
	v, err := c.client.EvalVariable(scope, name, loadConfig)
	if err != nil {
		return c.createEvalVariableResponse(state, nil, 0, fmt.Errorf("failed to evaluate variable %s: %v", name, err))
	}

	// Convert to our type
	variable := &types.Variable{
		DelveVar: v,
		Name:     v.Name,
		Type:     v.Type,
		Kind:     getVariableKind(v),
	}

	// Format the value based on the variable kind
	if v.Kind == reflect.Struct {
		// For struct types, format fields
		if len(v.Children) > 0 {
			fields := make([]string, 0, len(v.Children))
			for _, field := range v.Children {
				fieldStr := fmt.Sprintf("%s:%s", field.Name, field.Value)
				fields = append(fields, fieldStr)
			}
			variable.Value = "{" + strings.Join(fields, ", ") + "}"
		} else {
			variable.Value = "{}" // Empty struct
		}
	} else if v.Kind == reflect.Array || v.Kind == reflect.Slice {
		// For array or slice types, format elements
		if len(v.Children) > 0 {
			elements := make([]string, 0, len(v.Children))
			for _, element := range v.Children {
				elements = append(elements, element.Value)
			}
			variable.Value = "[" + strings.Join(elements, ", ") + "]"
		} else {
			variable.Value = "[]" // Empty array or slice
		}
	} else {
		variable.Value = v.Value
	}

	return c.createEvalVariableResponse(state, variable, depth, nil)
}

// Helper functions for variable information
func getVariableKind(v *api.Variable) string {
	if v == nil {
		return "unknown"
	}

	switch v.Kind {
	case reflect.Bool:
		return "boolean"
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
		reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		return "integer"
	case reflect.Float32, reflect.Float64:
		return "float"
	case reflect.String:
		return "string"
	case reflect.Array, reflect.Slice:
		return "array"
	case reflect.Map:
		return "map"
	case reflect.Struct:
		return "struct"
	case reflect.Ptr:
		return "pointer"
	case reflect.Interface:
		return "interface"
	case reflect.Chan:
		return "channel"
	case reflect.Func:
		return "function"
	default:
		return "unknown"
	}
}

// Helper function to get struct field information
func getStructFields(v api.Variable) string {
	if len(v.Children) == 0 {
		return "none"
	}

	fields := make([]string, len(v.Children))
	for i, field := range v.Children {
		fields[i] = fmt.Sprintf("%s %s", field.Name, field.Type)
	}
	return strings.Join(fields, ", ")
}

// Helper function to get map key type
func getMapKeyType(v api.Variable) string {
	if len(v.Children) == 0 {
		return "unknown"
	}
	return v.Children[0].Type
}

// Helper function to get map value type
func getMapValueType(v api.Variable) string {
	if len(v.Children) < 2 {
		return "unknown"
	}
	return v.Children[1].Type
}

// getLocalVariables extracts local variables and arguments from the current scope
func (c *Client) getLocalVariables(state *api.DebuggerState) ([]types.Variable, error) {
	if state == nil || state.SelectedGoroutine == nil {
		return nil, fmt.Errorf("no active goroutine")
	}

	// Create evaluation scope for current frame
	scope := api.EvalScope{
		GoroutineID: state.SelectedGoroutine.ID,
		Frame:       0, // 0 represents the current frame
	}

	// Default load configuration
	cfg := api.LoadConfig{
		FollowPointers:     true,
		MaxVariableRecurse: 1,
		MaxStringLen:       64,
		MaxArrayValues:     64,
		MaxStructFields:    -1,
	}

	// Convert Delve variables to our format
	convertToVariable := func(v *api.Variable, scope string) types.Variable {
		var value string

		// Format the value based on the variable kind
		if v.Kind == reflect.Struct {
			// For struct types, format fields
			if len(v.Children) > 0 {
				fields := make([]string, 0, len(v.Children))
				for _, field := range v.Children {
					fieldStr := fmt.Sprintf("%s:%s", field.Name, field.Value)
					fields = append(fields, fieldStr)
				}
				value = "{" + strings.Join(fields, ", ") + "}"
			} else {
				value = "{}" // Empty struct
			}
		} else if v.Kind == reflect.Array || v.Kind == reflect.Slice {
			// For array or slice types, format elements
			if len(v.Children) > 0 {
				elements := make([]string, 0, len(v.Children))
				for _, element := range v.Children {
					elements = append(elements, element.Value)
				}
				value = "[" + strings.Join(elements, ",") + "]"
			} else {
				value = "[]" // Empty array or slice
			}
		} else {
			value = v.Value
		}

		return types.Variable{
			DelveVar: v,
			Name:     v.Name,
			Value:    value,
			Type:     v.Type,
			Scope:    scope,
			Kind:     getVariableKind(v),
		}
	}

	var variables []types.Variable

	// Get function arguments
	args, err := c.client.ListFunctionArgs(scope, cfg)
	if err != nil {
		return nil, fmt.Errorf("error getting function arguments: %v", err)
	}

	// Get local variables
	locals, err := c.client.ListLocalVariables(scope, cfg)
	if err != nil {
		return nil, fmt.Errorf("error getting local variables: %v", err)
	}

	// Process arguments first
	for _, arg := range args {
		variables = append(variables, convertToVariable(&arg, "argument"))
	}

	// Process local variables
	for _, local := range locals {
		variables = append(variables, convertToVariable(&local, "local"))
	}

	return variables, nil
}

// createEvalVariableResponse creates an EvalVariableResponse
func (c *Client) createEvalVariableResponse(state *api.DebuggerState, variable *types.Variable, depth int, err error) types.EvalVariableResponse {
	context := c.createDebugContext(state)
	if err != nil {
		context.ErrorMessage = err.Error()
		return types.EvalVariableResponse{
			Status:  "error",
			Context: context,
		}
	}

	return types.EvalVariableResponse{
		Status:   "success",
		Context:  context,
		Variable: *variable,
	}
}

```

--------------------------------------------------------------------------------
/docs/Phase1-Tasks.md:
--------------------------------------------------------------------------------

```markdown
# MCP Go Debugger - Phase 1 Implementation Tasks

## Overview

This document outlines the detailed tasks for implementing Phase 1 (Core Functionality) of the MCP Go Debugger. The primary goal of Phase 1 is to establish a foundation for the debugger with essential capabilities, setting the stage for more advanced features in subsequent phases.

## High-Level Goals

- Set up Model Context Protocol (MCP) server using mark3labs/mcp-go
- Embed Delve as a library dependency
- Implement program launch and attach capabilities
- Implement basic debugging commands (breakpoints, step, continue)
- Simple variable inspection
- Initial testing with sample Go applications

## Detailed Tasks

### 1. Project Setup and Environment Configuration

- [x] **1.1** [P0] Create new Go module with proper structure
- [x] **1.2** [P0] Add mark3labs/mcp-go dependency *(depends on: 1.1)*
- [x] **1.3** [P0] Add Delve (go-delve/delve) dependencies *(depends on: 1.1)*
- [x] **1.4** [P1] Set up build system and compilation targets *(depends on: 1.1-1.3)*
- [x] **1.5** [P1] Create basic documentation structure *(depends on: 1.4)*
- [ ] **1.6** [P2] Set up testing framework and test fixtures *(depends on: 1.1-1.5)*

### 2. Basic MCP Server Implementation

- [x] **2.1** [P0] Implement basic Model Context Protocol server with mark3labs/mcp-go *(depends on: 1.2)*
- [x] **2.2** [P0] Configure server name, version, and metadata *(depends on: 2.1)*
- [x] **2.3** [P1] Implement error handling and logging framework *(depends on: 2.1-2.2)*
- [x] **2.4** [P1] Set up MCP stdio communication interface *(depends on: 2.1-2.3)*
- [x] **2.5** [P2] Add server health check capabilities *(depends on: 2.1-2.4)*

### 3. Delve Integration

- [x] **3.1** [P0] Create Delve client wrapper to manage debug sessions *(depends on: 1.3, 2.1)*
- [x] **3.2** [P0] Implement debug session lifecycle management (create, maintain, close) *(depends on: 3.1)*
- [x] **3.3** [P0] Implement error handling for Delve operations *(depends on: 3.1-3.2)*
- [ ] **3.4** [P1] Add support for Delve configuration options *(depends on: 3.1-3.3)*
- [x] **3.5** [P2] Implement session persistence across commands *(depends on: 3.1-3.4)*

### 4. Program Launch and Attach

- [ ] **4.7** [P1] Implement "debug" tool to compile and debug a source file directly *(depends on: 3.1-3.5)*
- [ ] **4.8** [P1] Implement "debug_test" tool to compile and debug a test function *(depends on: 3.1-3.5)*
- [x] **4.1** [P0] Implement "launch" tool to start a program with debugging *(depends on: 3.1-3.5)*
- [x] **4.2** [P0] Add support for program arguments and environment variables *(depends on: 4.1)*
- [x] **4.3** [P0] Implement "attach" tool to connect to running process *(depends on: 3.1-3.5)*
- [x] **4.4** [P1] Add validation for program path and process ID *(depends on: 4.1-4.3, 4.7)*
- [x] **4.5** [P1] Implement status reporting for running programs *(depends on: 4.1-4.4)*
- [x] **4.6** [P2] Add support for stopping debugged programs *(depends on: 4.1-4.5)*

### 5. Breakpoint Management

- [x] **5.1** [P0] Implement "set_breakpoint" tool *(depends on: 4.1-4.3)*
- [x] **5.2** [P0] Add file/line number validation for breakpoints *(depends on: 5.1)*
- [x] **5.3** [P0] Implement "list_breakpoints" tool to show all breakpoints *(depends on: 5.1-5.2)*
- [x] **5.4** [P0] Implement "remove_breakpoint" tool *(depends on: 5.1-5.3)*
- [ ] **5.5** [P1] Add breakpoint ID tracking and management *(depends on: 5.1-5.4)*
- [ ] **5.6** [P2] Add support for enabling/disabling breakpoints *(depends on: 5.1-5.5)*

### 6. Program Control

- [ ] **6.1** [P0] Implement "continue" tool for program execution *(depends on: 4.1-4.6)*
- [ ] **6.2** [P0] Add breakpoint hit notification handling *(depends on: 6.1)*
- [ ] **6.3** [P0] Implement "step" tool for line-by-line execution *(depends on: 6.1-6.2)*
- [ ] **6.4** [P0] Implement "step_over" tool *(depends on: 6.1-6.3)*
- [ ] **6.5** [P0] Implement "step_out" tool *(depends on: 6.1-6.4)*
- [ ] **6.6** [P1] Add execution state tracking and reporting *(depends on: 6.1-6.5)*

### 7. Variable Inspection

- [x] **7.1** [P0] Implement "eval_variable" tool to view variable values *(depends on: 6.1-6.6)*
- [x] **7.2** [P0] Add support for complex data structures *(depends on: 7.1)*
- [x] **7.3** [P1] Implement variable formatting and pretty printing *(depends on: 7.1-7.2)*
- [x] **7.4** [P1] Add support for scope-aware variable lookup *(depends on: 7.1-7.3)*
- [x] **7.5** [P2] Implement "evaluate" tool for expression evaluation *(depends on: 7.1-7.4)*
- [x] **7.6** [P1] Implement "list_scope_variables" tool to display all variables in current scope *(depends on: 7.1-7.4)*
- [x] **7.7** [P1] Implement "get_execution_position" tool to retrieve the current line number and file *(depends on: 7.1-7.4)*

### 8. Stack and Goroutine Inspection

- [ ] **8.1** [P0] Implement "stack_trace" tool *(depends on: 6.1-6.6)*
- [ ] **8.2** [P0] Add source line information to stack frames *(depends on: 8.1)*
- [ ] **8.3** [P0] Implement "list_goroutines" tool *(depends on: 8.1-8.2)*
- [ ] **8.4** [P1] Add goroutine selection for debugging *(depends on: 8.1-8.3)*
- [ ] **8.5** [P2] Implement goroutine state information *(depends on: 8.1-8.4)*

### 9. Testing and Validation

- [x] **9.1** [P0] Create simple test Go program for debugging *(depends on: All above)*
- [ ] **9.2** [P0] Test launch and attach workflow *(depends on: 9.1)*
- [ ] **9.3** [P0] Test breakpoint setting and hitting *(depends on: 9.1-9.2)*
- [ ] **9.4** [P0] Test program control flow (continue, step) *(depends on: 9.1-9.3)*
- [ ] **9.5** [P0] Test variable inspection *(depends on: 9.1-9.4)*
- [ ] **9.6** [P1] Test goroutine debugging with concurrent program *(depends on: 9.1-9.5)*
- [ ] **9.7** [P1] Document any issues or limitations found *(depends on: 9.1-9.6)*

### 10. Documentation and Packaging

- [ ] **10.1** [P0] Update README with installation instructions *(depends on: All above)*
- [ ] **10.2** [P0] Document all implemented MCP tools *(depends on: 10.1)*
- [ ] **10.3** [P0] Create usage examples for each tool *(depends on: 10.1-10.2)*
- [ ] **10.4** [P1] Document integration with Cursor and Claude Desktop *(depends on: 10.1-10.3)*
- [ ] **10.5** [P1] Create simple tutorial for first-time users *(depends on: 10.1-10.4)*
- [ ] **10.6** [P1] Package binary for easy installation *(depends on: All above)*
- [ ] **10.7** [P2] Create developer documentation for future contributors *(depends on: 10.1-10.6)*

## Priority Levels

- **P0**: Must have for Phase 1 - core functionality that cannot be deferred
- **P1**: Should have for Phase 1 - important but could potentially slip to early Phase 2
- **P2**: Nice to have for Phase 1 - enhances the experience but could be deferred

## Next Steps After Phase 1

- [ ] Review Phase 1 implementation and gather feedback
- [ ] Identify any issues or limitations that need to be addressed
- [ ] Begin planning for Phase 2 implementation of enhanced features
- [ ] Consider early adopter testing with selected users 
```

--------------------------------------------------------------------------------
/pkg/mcp/server.go:
--------------------------------------------------------------------------------

```go
package mcp

import (
	"context"
	"encoding/json"
	"fmt"

	"github.com/mark3labs/mcp-go/mcp"
	"github.com/mark3labs/mcp-go/server"
	"github.com/sunfmin/mcp-go-debugger/pkg/debugger"
	"github.com/sunfmin/mcp-go-debugger/pkg/logger"
)

type MCPDebugServer struct {
	server      *server.MCPServer
	debugClient *debugger.Client
	version     string
}

func NewMCPDebugServer(version string) *MCPDebugServer {
	s := &MCPDebugServer{
		server:      server.NewMCPServer("Go Debugger MCP", version),
		debugClient: debugger.NewClient(),
		version:     version,
	}

	s.registerTools()

	return s
}

func (s *MCPDebugServer) Server() *server.MCPServer {
	return s.server
}

func (s *MCPDebugServer) DebugClient() *debugger.Client {
	return s.debugClient
}

func (s *MCPDebugServer) registerTools() {
	s.addDebugSourceFileTool()
	s.addDebugTestTool()
	s.addLaunchTool()
	s.addAttachTool()
	s.addCloseTool()
	s.addSetBreakpointTool()
	s.addListBreakpointsTool()
	s.addRemoveBreakpointTool()
	s.addContinueTool()
	s.addStepTool()
	s.addStepOverTool()
	s.addStepOutTool()
	s.addEvalVariableTool()
	s.addGetDebuggerOutputTool()
}

func (s *MCPDebugServer) addLaunchTool() {
	launchTool := mcp.NewTool("launch",
		mcp.WithDescription("Launch a Go application with debugging enabled"),
		mcp.WithString("program",
			mcp.Required(),
			mcp.Description("Path to the Go program"),
		),
		mcp.WithArray("args",
			mcp.Description("Arguments to pass to the program"),
		),
	)

	s.server.AddTool(launchTool, s.Launch)
}

func (s *MCPDebugServer) addAttachTool() {
	attachTool := mcp.NewTool("attach",
		mcp.WithDescription("Attach to a running Go process"),
		mcp.WithNumber("pid",
			mcp.Required(),
			mcp.Description("Process ID to attach to"),
		),
	)

	s.server.AddTool(attachTool, s.Attach)
}

func (s *MCPDebugServer) addCloseTool() {
	closeTool := mcp.NewTool("close",
		mcp.WithDescription("Close the current debugging session"),
	)

	s.server.AddTool(closeTool, s.Close)
}

func (s *MCPDebugServer) addSetBreakpointTool() {
	breakpointTool := mcp.NewTool("set_breakpoint",
		mcp.WithDescription("Set a breakpoint at a specific file location"),
		mcp.WithString("file",
			mcp.Required(),
			mcp.Description("Path to the file"),
		),
		mcp.WithNumber("line",
			mcp.Required(),
			mcp.Description("Line number"),
		),
	)

	s.server.AddTool(breakpointTool, s.SetBreakpoint)
}

func (s *MCPDebugServer) addListBreakpointsTool() {
	listBreakpointsTool := mcp.NewTool("list_breakpoints",
		mcp.WithDescription("List all currently set breakpoints"),
	)

	s.server.AddTool(listBreakpointsTool, s.ListBreakpoints)
}

func (s *MCPDebugServer) addRemoveBreakpointTool() {
	removeBreakpointTool := mcp.NewTool("remove_breakpoint",
		mcp.WithDescription("Remove a breakpoint by its ID"),
		mcp.WithNumber("id",
			mcp.Required(),
			mcp.Description("ID of the breakpoint to remove"),
		),
	)

	s.server.AddTool(removeBreakpointTool, s.RemoveBreakpoint)
}

func (s *MCPDebugServer) addDebugSourceFileTool() {
	debugTool := mcp.NewTool("debug",
		mcp.WithDescription("Debug a Go source file directly"),
		mcp.WithString("file",
			mcp.Required(),
			mcp.Description("Absolute Path to the Go source file"),
		),
		mcp.WithArray("args",
			mcp.Description("Arguments to pass to the program"),
		),
	)

	s.server.AddTool(debugTool, s.DebugSourceFile)
}

func (s *MCPDebugServer) addDebugTestTool() {
	debugTestTool := mcp.NewTool("debug_test",
		mcp.WithDescription("Debug a Go test function"),
		mcp.WithString("testfile",
			mcp.Required(),
			mcp.Description("Absolute Path to the test file"),
		),
		mcp.WithString("testname",
			mcp.Required(),
			mcp.Description("Name of the test function to debug"),
		),
		mcp.WithArray("testflags",
			mcp.Description("Optional flags to pass to go test"),
		),
	)

	s.server.AddTool(debugTestTool, s.DebugTest)
}

func (s *MCPDebugServer) addContinueTool() {
	continueTool := mcp.NewTool("continue",
		mcp.WithDescription("Continue execution until next breakpoint or program end"),
	)

	s.server.AddTool(continueTool, s.Continue)
}

func (s *MCPDebugServer) addStepTool() {
	stepTool := mcp.NewTool("step",
		mcp.WithDescription("Step into the next function call"),
	)

	s.server.AddTool(stepTool, s.Step)
}

func (s *MCPDebugServer) addStepOverTool() {
	stepOverTool := mcp.NewTool("step_over",
		mcp.WithDescription("Step over the next function call"),
	)

	s.server.AddTool(stepOverTool, s.StepOver)
}

func (s *MCPDebugServer) addStepOutTool() {
	stepOutTool := mcp.NewTool("step_out",
		mcp.WithDescription("Step out of the current function"),
	)

	s.server.AddTool(stepOutTool, s.StepOut)
}

func (s *MCPDebugServer) addEvalVariableTool() {
	evalVarTool := mcp.NewTool("eval_variable",
		mcp.WithDescription("Evaluate the value of a variable"),
		mcp.WithString("name",
			mcp.Required(),
			mcp.Description("Name of the variable to evaluate"),
		),
		mcp.WithNumber("depth",
			mcp.Description("Depth for evaluate nested structures (default: 1)"),
		),
	)

	s.server.AddTool(evalVarTool, s.EvalVariable)
}

func (s *MCPDebugServer) addGetDebuggerOutputTool() {
	outputTool := mcp.NewTool("get_debugger_output",
		mcp.WithDescription("Get captured stdout and stderr from the debugged program"),
	)

	s.server.AddTool(outputTool, s.GetDebuggerOutput)
}

func newErrorResult(format string, args ...interface{}) *mcp.CallToolResult {
	result := mcp.NewToolResultText(fmt.Sprintf("Error: "+format, args...))
	result.IsError = true
	return result
}

func (s *MCPDebugServer) Launch(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
	logger.Debug("Received launch request")

	program := request.Params.Arguments["program"].(string)

	var args []string
	if argsVal, ok := request.Params.Arguments["args"]; ok && argsVal != nil {
		argsArray := argsVal.([]interface{})
		args = make([]string, len(argsArray))
		for i, arg := range argsArray {
			args[i] = fmt.Sprintf("%v", arg)
		}
	}

	response := s.debugClient.LaunchProgram(program, args)

	return newToolResultJSON(response)
}

func (s *MCPDebugServer) Attach(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
	logger.Debug("Received attach request")

	pidFloat := request.Params.Arguments["pid"].(float64)
	pid := int(pidFloat)

	response := s.debugClient.AttachToProcess(pid)

	return newToolResultJSON(response)
}

func (s *MCPDebugServer) Close(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
	logger.Debug("Received close request")

	response, err := s.debugClient.Close()
	if err != nil {
		logger.Error("Failed to close debug session", "error", err)
		return newErrorResult("failed to close debug session: %v", err), nil
	}

	s.debugClient = debugger.NewClient()

	return newToolResultJSON(response)
}

func (s *MCPDebugServer) SetBreakpoint(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
	logger.Debug("Received set_breakpoint request")

	file := request.Params.Arguments["file"].(string)
	line := int(request.Params.Arguments["line"].(float64))

	breakpoint := s.debugClient.SetBreakpoint(file, line)

	return newToolResultJSON(breakpoint)
}

func (s *MCPDebugServer) ListBreakpoints(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
	logger.Debug("Received list_breakpoints request")

	response := s.debugClient.ListBreakpoints()

	return newToolResultJSON(response)
}

func (s *MCPDebugServer) RemoveBreakpoint(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
	logger.Debug("Received remove_breakpoint request")

	id := int(request.Params.Arguments["id"].(float64))

	response := s.debugClient.RemoveBreakpoint(id)

	return newToolResultJSON(response)
}

func (s *MCPDebugServer) DebugSourceFile(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
	logger.Debug("Received debug_source_file request")

	file := request.Params.Arguments["file"].(string)

	var args []string
	if argsVal, ok := request.Params.Arguments["args"]; ok && argsVal != nil {
		argsArray := argsVal.([]interface{})
		args = make([]string, len(argsArray))
		for i, arg := range argsArray {
			args[i] = fmt.Sprintf("%v", arg)
		}
	}

	response := s.debugClient.DebugSourceFile(file, args)

	return newToolResultJSON(response)
}

func (s *MCPDebugServer) Continue(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
	logger.Debug("Received continue request")

	state := s.debugClient.Continue()
	return newToolResultJSON(state)
}

func (s *MCPDebugServer) Step(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
	logger.Debug("Received step request")

	state := s.debugClient.Step()

	return newToolResultJSON(state)
}

func (s *MCPDebugServer) StepOver(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
	logger.Debug("Received step_over request")

	state := s.debugClient.StepOver()

	return newToolResultJSON(state)
}

func (s *MCPDebugServer) StepOut(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
	logger.Debug("Received step_out request")

	state := s.debugClient.StepOut()
	return newToolResultJSON(state)
}

func (s *MCPDebugServer) EvalVariable(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
	logger.Debug("Received evaluate_variable request")

	name := request.Params.Arguments["name"].(string)

	var depth int
	if depthVal, ok := request.Params.Arguments["depth"]; ok && depthVal != nil {
		depth = int(depthVal.(float64))
	} else {
		depth = 1
	}

	response := s.debugClient.EvalVariable(name, depth)

	return newToolResultJSON(response)
}

func (s *MCPDebugServer) GetDebuggerOutput(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
	logger.Debug("Received get_debugger_output request")

	output := s.debugClient.GetDebuggerOutput()

	return newToolResultJSON(output)
}

func (s *MCPDebugServer) DebugTest(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
	logger.Debug("Received debug_test request")

	testfile := request.Params.Arguments["testfile"].(string)
	testname := request.Params.Arguments["testname"].(string)

	var testflags []string
	if testflagsVal, ok := request.Params.Arguments["testflags"]; ok && testflagsVal != nil {
		testflagsArray := testflagsVal.([]interface{})
		testflags = make([]string, len(testflagsArray))
		for i, flag := range testflagsArray {
			testflags[i] = fmt.Sprintf("%v", flag)
		}
	}

	response := s.debugClient.DebugTest(testfile, testname, testflags)

	return newToolResultJSON(response)
}

func newToolResultJSON(data interface{}) (*mcp.CallToolResult, error) {
	jsonBytes, err := json.Marshal(data)
	if err != nil {
		return newErrorResult("failed to serialize data: %v", err), nil
	}
	return mcp.NewToolResultText(string(jsonBytes)), nil
}

```

--------------------------------------------------------------------------------
/docs/PRD.md:
--------------------------------------------------------------------------------

```markdown
# MCP Go Debugger PRD

## Overview

MCP Go Debugger is a Model Context Protocol (MCP) server that integrates the Delve debugger for Go applications into Cursor or Claude Desktop. It enables AI assistants to debug Go applications at runtime by providing debugger functionality through an MCP server interface. The implementation will be built in Go using the [mark3labs/mcp-go](https://github.com/mark3labs/mcp-go) framework with Delve embedded directly within the MCP process.

## Objectives

- Provide AI assistants with the ability to debug Go applications at runtime
- Integrate with Delve, the Go debugger, to leverage its capabilities
- Offer a simple interface similar to the NodeJS debugger MCP
- Enable debugging actions like setting breakpoints, inspecting variables, and stepping through code
- Support both Cursor and Claude Desktop as client interfaces
- Simplify user experience by embedding Delve within the MCP process

## Target Users

- Go developers using Cursor or Claude Desktop
- AI assistants that need to debug Go applications

## Key Features

### 1. Connection to Go Programs

- Launch Go programs directly from the MCP with debug capabilities enabled
- Attach to running Go processes that support debugging
- Debug a Go source file directly (similar to `dlv debug`) by compiling and debugging it
- Debug a single Go test function directly by compiling and running the test with the debugger
- Manage the entire debugging lifecycle within a single MCP process
- Display connection status and information

### 2. Breakpoint Management

- Set breakpoints at specific file locations
- Set conditional breakpoints
- List active breakpoints
- Remove breakpoints
- Enable/disable breakpoints

### 3. Program Control

- Start/stop execution
- Step into, over, and out of functions
- Continue execution until next breakpoint
- Restart program

### 4. State Inspection

- View variable values at breakpoints
- Eval call stack
- Inspect goroutines
- View thread information
- Evaluate expressions in current context
- List all variables in current scope (local variables, function arguments, and package variables)
- Get current execution position (file name, line number, and function name)

### 5. Runtime Analysis

- Monitor goroutine creation and completion
- Track memory allocations
- Profile CPU usage during debugging sessions

## Technical Requirements

### Embedding Delve

The MCP will embed Delve directly:
- Import Delve as a Go library/dependency
- Create and manage debug sessions programmatically
- Access Delve's API directly in-process
- Handle debugger lifecycle (start, stop, restart) within the MCP

### MCP Server Implementation

- Implement using mark3labs/mcp-go framework
- Expose a standardized Model Context Protocol interface for AI assistants
- Provide direct access to Delve functionality via MCP tools
- Handle errors and provide meaningful feedback

### Installation and Setup

- Provide as a compiled Go binary
- Support for standard Go installation methods (go install)
- Minimal configuration requirements
- Clear documentation for setup in both Cursor and Claude Desktop

## User Experience

### Setup Workflow

1. Install MCP Go Debugger
2. Configure in Cursor or Claude Desktop
3. Use the MCP to either launch a Go application with debugging or attach to a running process
4. Interact with the debugging session through the AI assistant

### Usage Examples

#### Example 1: Launching and Debugging an Application

```
User: "I want to debug my Go application in the current directory."
AI: "I'll help you debug your application. Let me start it with debugging enabled."
[AI uses MCP Go Debugger to launch the application]
"I've started your application with debugging enabled. What part of the code would you like to eval?"
```

#### Example 2: Setting a Breakpoint

```
User: "I'm getting an error in my processTransaction function. Can you help debug it?"
AI: "I'll help debug this issue. Let me set a breakpoint in the processTransaction function."
[AI uses MCP Go Debugger to set breakpoint]
"I've set a breakpoint at the beginning of the processTransaction function. Let's run your application and trigger the function."
```

#### Example 3: Inspecting Variables

```
User: "The application crashed when processing this request."
AI: "Let me eval what's happening when the request is processed."
[AI sets breakpoint and application hits it]
"I can see the issue. The 'amount' variable is negative (-10.5) when it reaches line 42, but the validation check occurs later on line 65."
```

#### Example 4: Debugging a Source File Directly

```
User: "Can you debug this main.go file for me?"
AI: "I'll debug your source file directly."
[AI uses MCP Go Debugger to compile and debug the file]
"I've compiled and started debugging main.go. Let me set a breakpoint in the main function to eval how the program executes."
```

#### Example 5: Debugging a Single Test

```
User: "I need to debug the TestCalculateTotal function in my order_test.go file."
AI: "I'll help you debug that specific test function."
[AI uses MCP Go Debugger to compile and debug the test]
"I've compiled and started debugging the TestCalculateTotal test function. Let me set a breakpoint at the beginning of the test to see how it executes."
```

## Implementation Plan

### Phase 1: Core Functionality

- Set up MCP server using mark3labs/mcp-go
- Embed Delve as a library dependency
- Implement program launch and attach capabilities
- Implement basic debugging commands (breakpoints, step, continue)
- Simple variable inspection
- Initial testing with sample Go applications

### Phase 2: Enhanced Features

- Conditional breakpoints
- Advanced state inspection
- Goroutine tracking
- Error analysis and suggestions
- Improved diagnostics and debugging information

### Phase 3: Optimization and Refinement

- Performance improvements
- UX enhancements
- Extended documentation
- Integration with additional tools
- Support for complex debugging scenarios

## Success Metrics

- Number of successful debugging sessions
- User feedback on debugging effectiveness
- Time saved in debugging complex issues
- Adoption rate among Go developers using Cursor/Claude Desktop
- Reduction in context switching between different tools

## Limitations and Constraints

- Requires Go program to be compiled with debug information
- May have performance impact on the running Go application
- Can't debug optimized builds effectively
- Some features of Delve may not be accessible through the MCP interface
- Subject to limitations of mark3labs/mcp-go framework
- May require specific permissions to attach to processes

## Appendix

### Installation Instructions

#### Cursor

1. Add to Cursor (`~/.cursor/mcp.json`):
```json
{
  "mcpServers": {
    "go-debugger": {
      "command": "mcp-go-debugger",
      "args": []
    }
  }
}
```

2. Verify connection in Cursor interface

#### Claude Desktop

1. Add to Claude Desktop:
```
claude mcp add go-debugger mcp-go-debugger
```

2. Verify connection:
```
/mcp
```

### Usage Instructions

1. Launch and debug a Go program directly through the MCP:
```
User: "Debug my Go application main.go"
AI: [Uses MCP to launch the application with debugging enabled]
```

2. Attach to a running Go process:
```
User: "Attach to my running Go server with PID 12345"
AI: [Uses MCP to attach to the running process for debugging]
```

3. Debug a Go source file directly:
```
User: "Debug this main.go file"
AI: [Uses MCP to compile and debug the source file directly]
```

4. Ask the AI assistant to debug specific issues using the go-debugger MCP

### Implementation Example

Basic MCP server implementation using mark3labs/mcp-go with embedded Delve:

```go
package main

import (
	"context"
	"fmt"
	"log"
	"strconv"

	"github.com/go-delve/delve/pkg/terminal"
	"github.com/go-delve/delve/service"
	"github.com/go-delve/delve/service/api"
	"github.com/go-delve/delve/service/debugger"
	"github.com/mark3labs/mcp-go/mcp"
	"github.com/mark3labs/mcp-go/server"
)

func main() {
	// Create MCP server
	s := server.NewMCPServer(
		"Go Debugger MCP",
		"1.0.0",
	)

	// Global variable to hold the debug session
	var debugClient *service.Client
	
	// Add launch tool
	launchTool := mcp.NewTool("launch",
		mcp.WithDescription("Launch a Go application with debugging enabled"),
		mcp.WithString("program",
			mcp.Required(),
			mcp.Description("Path to the Go program"),
		),
	)
	
	s.AddTool(launchTool, func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
		program := request.Params.Arguments["program"].(string)
		
		// Set up and launch the debugger
		// This is a simplified example - real implementation would handle more config options
		config := &service.Config{
			ProcessArgs: []string{program},
			APIVersion:  2,
		}
		
		var err error
		debugClient, err = service.NewClient(config)
		if err != nil {
			return nil, fmt.Errorf("failed to create debug client: %v", err)
		}
		
		return mcp.NewToolResultText(fmt.Sprintf("Successfully launched %s with debugging enabled", program)), nil
	})
	
	// Add breakpoint tool
	breakpointTool := mcp.NewTool("set_breakpoint",
		mcp.WithDescription("Set a breakpoint at a specific file location"),
		mcp.WithString("file",
			mcp.Required(),
			mcp.Description("Path to the file"),
		),
		mcp.WithNumber("line",
			mcp.Required(),
			mcp.Description("Line number"),
		),
	)

	// Add tool handler
	s.AddTool(breakpointTool, func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
		if debugClient == nil {
			return nil, fmt.Errorf("no active debug session, please launch a program first")
		}
		
		file := request.Params.Arguments["file"].(string)
		line := int(request.Params.Arguments["line"].(float64))
		
		bp, err := debugClient.CreateBreakpoint(&api.Breakpoint{
			File: file,
			Line: line,
		})
		
		if err != nil {
			return nil, fmt.Errorf("failed to set breakpoint: %v", err)
		}
		
		return mcp.NewToolResultText(fmt.Sprintf("Breakpoint set at %s:%d (ID: %d)", file, line, bp.ID)), nil
	})

	// Add other tools...

	// Start the stdio server
	if err := server.ServeStdio(s); err != nil {
		log.Fatalf("Server error: %v\n", err)
	}
}
```

### API Reference

Exposed MCP commands:
- `launch`: Launch a Go program with debugging enabled
- `attach`: Attach to a running Go process
- `debug`: Compile and debug a Go source file directly
- `debug_test`: Compile and debug a Go test function
- `set_breakpoint`: Set a breakpoint at a specific location
- `list_breakpoints`: List all active breakpoints
- `remove_breakpoint`: Remove a specific breakpoint
- `continue`: Continue execution
- `step`: Step to next source line
- `step_out`: Step out of current function
- `step_over`: Step over current line
- `eval_variable`: Eval value of a variable
- `list_scope_variables`: List all variables in current scope
- `list_goroutines`: List all goroutines
- `stack_trace`: Show stack trace at current position
- `evaluate`: Evaluate an expression in current context
```

--------------------------------------------------------------------------------
/docs/Response-Improvements.md:
--------------------------------------------------------------------------------

```markdown
# MCP Go Debugger Response Improvements

## Overview

This document outlines research and improvements for the MCP Go Debugger tool responses to provide better context and information for LLM interactions. The goal is to standardize response formats and include comprehensive debugging state information that helps LLMs better understand and interact with the debugging process.

## Current Challenges

1. Inconsistent response formats across different tools
2. Limited context about debugger state
3. Missing temporal information about operations
4. Incomplete variable and execution context
5. Empty or missing stdout/stderr capture
6. Inconsistent timestamp formatting
7. Poor error handling for program termination
8. Missing variable change tracking between steps
9. Limited breakpoint categorization and information

## Test Output Analysis

Analysis of the TestDebugWorkflow test output reveals several issues:

1. When getting debugger output, stdout/stderr are empty with an error message: "failed to get state: Process has exited with status 0"
2. Several responses have null values for fields like ScopeVariables even when variables should be available
3. Some responses show timestamp as "0001-01-01T00:00:00Z" (zero value) while others have valid timestamps
4. Error handling for process exit is suboptimal: "continue command failed: Process has exited with status 0"
5. System breakpoints (IDs -1 and -2) are mixed with user breakpoints without clear differentiation
6. The ChangedVars field is consistently null even when stepping through code that modifies variables
7. Debug context has inconsistent information, with some fields populated in some responses but empty in others

## Todo List

1. **Improve Output Capture**
   - Fix empty stdout/stderr issue when retrieving debugger output
   - Implement buffering of program output during debugging session
   - Add structured output capture with timestamps
   - Include outputSummary for better context

2. **Standardize Response Context**
   - Ensure all responses include complete debug context
   - Fix inconsistent timestamp formatting (eliminate zero values)
   - Add lastOperation field consistently across all responses
   - Standardize context object structure across all tools

3. **Enhance Variable Information**
   - Populate ScopeVariables array with all variables in current scope
   - Track variable changes between steps in changedVars field
   - Improve variable summaries with human-readable descriptions
   - Add support for examining complex data structures

4. **Improve Error Handling**
   - Handle program termination gracefully
   - Provide better context when processes exit
   - Include clear error messages in context
   - Create specific response types for different error conditions

5. **Refine Breakpoint Management**
   - Better categorize system vs. user breakpoints
   - Enhance breakpoint information with descriptions
   - Track and expose breakpoint hit counts consistently
   - Add support for conditional breakpoints

6. **Enrich Step Execution Feedback**
   - Add changedVars information when stepping through code
   - Provide clearer fromLocation and toLocation details
   - Include operation summaries for each step
   - Detect entry/exit from functions

7. **Implement Cross-Reference Information**
   - Add references between related variables
   - Connect variables to their containing functions
   - Link breakpoints to relevant variables
   - Create navigation hints between related code elements

8. **Add Execution Context**
   - Include thread and goroutine information
   - Provide stop reasons and next steps suggestions
   - Add human-readable summaries of program state
   - Include stack trace information where relevant

9. **Clean Up Response Format**
   - Remove internal Delve data from JSON output
   - Ensure consistent structure across all response types
   - Maintain backward compatibility while improving
   - Use pointer types for optional fields to reduce null values

10. **Enhance Human and LLM Readability**
    - Add summary fields to all major components
    - Ensure all location information includes readable context
    - Provide clear temporal information about debugging state
    - Create more descriptive operation names

## Proposed Response Format

### Response Types

Example JSON responses for each type:

```json
// Debug Context Example
{
    "currentPosition": {
        "file": "/path/to/main.go",
        "line": 42,
        "function": "main.processRequest",
        "package": "main",
        "summary": "Inside main.processRequest handling HTTP request"
    },
    "timestamp": "2024-03-24T15:04:05Z",
    "lastOperation": "step_over",
    "errorMessage": "",
    "stopReason": "breakpoint hit in main.processRequest",
    "threads": [
        {
            "id": 1,
            "status": "stopped at breakpoint",
            "location": {
                "file": "/path/to/main.go",
                "line": 42,
                "function": "main.processRequest",
                "package": "main",
                "summary": "Processing HTTP request"
            },
            "active": true,
            "summary": "Main thread stopped at breakpoint in request handler"
        }
    ],
    "goroutine": {
        "id": 1,
        "status": "running",
        "waitReason": "waiting for network response",
        "location": {
            "file": "/path/to/main.go",
            "line": 42,
            "function": "main.processRequest",
            "package": "main",
            "summary": "Processing HTTP request"
        },
        "createdAt": {
            "file": "/path/to/main.go",
            "line": 30,
            "function": "main.startWorker",
            "package": "main",
            "summary": "Worker goroutine creation point"
        },
        "userLocation": {
            "file": "/path/to/main.go",
            "line": 42,
            "function": "main.processRequest",
            "package": "main",
            "summary": "User code location"
        },
        "summary": "Main worker goroutine processing HTTP request"
    },
    "operationSummary": "Stepped over function call in main.processRequest"
}

// Variable Example
{
    "name": "request",
    "value": "*http.Request{Method:\"GET\", URL:\"/api/users\"}",
    "type": "*http.Request",
    "summary": "HTTP GET request for /api/users endpoint",
    "scope": "local",
    "kind": "pointer",
    "typeInfo": "Pointer to HTTP request structure",
    "references": ["context", "response", "params"]
}

// Breakpoint Example
{
    "id": 1,
    "status": "enabled",
    "location": {
        "file": "/path/to/main.go",
        "line": 42,
        "function": "main.processRequest",
        "package": "main",
        "summary": "Start of request processing"
    },
    "description": "Break before processing API request",
    "variables": ["request", "response", "err"],
    "package": "main",
    "condition": "request.Method == \"POST\"",
    "hitCount": 5,
    "lastHitInfo": "Last hit on POST /api/users at 15:04:05"
}

// Function Example
{
    "name": "processRequest",
    "signature": "func processRequest(w http.ResponseWriter, r *http.Request) error",
    "parameters": ["w http.ResponseWriter", "r *http.Request"],
    "returnType": "error",
    "package": "main",
    "description": "HTTP request handler for API endpoints",
    "location": {
        "file": "/path/to/main.go",
        "line": 42,
        "function": "main.processRequest",
        "package": "main",
        "summary": "Main request processing function"
    }
}

// DebuggerState Example
{
    "status": "stopped at breakpoint",
    "currentThread": {
        "id": 1,
        "status": "stopped at breakpoint",
        "location": {
            "file": "/path/to/main.go",
            "line": 42,
            "function": "main.processRequest",
            "package": "main",
            "summary": "Processing HTTP request"
        },
        "active": true,
        "summary": "Main thread stopped at breakpoint"
    },
    "currentGoroutine": {
        "id": 1,
        "status": "running",
        "location": {
            "file": "/path/to/main.go",
            "line": 42,
            "function": "main.processRequest",
            "package": "main",
            "summary": "Processing HTTP request"
        },
        "summary": "Main goroutine processing request"
    },
    "reason": "Hit breakpoint in request handler",
    "nextSteps": [
        "eval request variable",
        "step into processRequest",
        "continue execution"
    ],
    "summary": "Program paused at start of request processing"
}

// Launch Response Example
{
    "status": "success",
    "context": { /* DebugContext object as shown above */ },
    "programName": "./myapp",
    "cmdLine": ["./myapp", "--debug"],
    "buildInfo": {
        "package": "main",
        "goVersion": "go1.21.0"
    }
}

// Breakpoint Response Example
{
    "status": "success",
    "context": { /* DebugContext object */ },
    "breakpoint": { /* Breakpoint object as shown above */ },
    "allBreakpoints": [
        { /* Breakpoint object */ }
    ],
    "scopeVariables": [
        { /* Variable object */ }
    ]
}

// Step Response Example
{
    "status": "success",
    "context": { /* DebugContext object */ },
    "stepType": "over",
    "fromLocation": {
        "file": "/path/to/main.go",
        "line": 42,
        "function": "main.processRequest",
        "package": "main",
        "summary": "Before function call"
    },
    "toLocation": {
        "file": "/path/to/main.go",
        "line": 43,
        "function": "main.processRequest",
        "package": "main",
        "summary": "After function call"
    },
    "changedVars": [
        { /* Variable object showing changes */ }
    ]
}

// Eval Variable Response Example
{
    "status": "success",
    "context": { /* DebugContext object */ },
    "variable": { /* Variable object as shown above */ },
    "scopeInfo": {
        "function": "main.processRequest",
        "package": "main",
        "locals": ["request", "response", "err"]
    }
}

// Continue Response Example
{
    "status": "success",
    "context": { /* DebugContext object */ },
    "stoppedAt": {
        "file": "/path/to/main.go",
        "line": 50,
        "function": "main.processRequest",
        "package": "main",
        "summary": "After processing request"
    },
    "stopReason": "breakpoint hit",
    "hitBreakpoint": { /* Breakpoint object */ }
}

// Close Response Example
{
    "status": "success",
    "context": { /* DebugContext object */ },
    "exitCode": 0,
    "summary": "Debug session ended normally after processing 100 requests"
}

// Debugger Output Response Example
{
    "status": "success",
    "context": { /* DebugContext object */ },
    "stdout": "Processing request ID: 1\nRequest completed successfully\n",
    "stderr": "",
    "outputSummary": "Successfully processed request with ID 1"
}
```

Key Features of the JSON Format:
1. Internal Delve data stored in non-JSON fields
2. All exposed fields are human-readable and LLM-friendly
3. Consistent structure with clear field names
4. Rich contextual information in summaries
5. Cross-references between related data
6. Temporal information about operations
7. Complete debugging context

The JSON format makes it easy to:
1. Parse and process debugging information
2. Generate human-readable summaries
3. Track debugging state changes
4. Understand the context of operations
5. Follow program execution flow
6. Monitor variable changes
7. Track breakpoint interactions

Key Changes:
1. Removed embedded Delve types in favor of internal fields
2. Consistent naming across all types
3. Added summaries to all relevant types
4. Enhanced cross-referencing between related data
5. Improved human-readable descriptions
6. Better temporal information
7. More consistent structure across all types

Benefits:
1. Cleaner JSON output without internal Delve data
2. More consistent and predictable structure
3. Better readability for humans and LLMs
4. Maintained access to Delve functionality
5. Improved debugging context
6. Better relationship tracking
7. Enhanced temporal awareness

## Implementation Priority

Based on test output analysis, these are the highest priority improvements:

1. Fix output capture in GetDebuggerOutput
2. Ensure consistent timestamp handling
3. Improve error handling for program termination
4. Populate ScopeVariables and ChangedVars
5. Better categorize breakpoints
6. Standardize context object across all responses
7. Enhance step execution feedback

## Modified Tools

The following core tools will be updated to use the new response format:

1. `launch` - Program launch with debugging
2. `attach` - Attach to running process
3. `close` - End debug session
4. `set_breakpoint` - Set breakpoint
5. `remove_breakpoint` - Remove breakpoint
6. `list_breakpoints` - List all breakpoints
7. `debug_source_file` - Debug source file
8. `debug_test` - Debug test
9. `continue` - Continue execution
10. `step` - Step into
11. `step_over` - Step over
12. `step_out` - Step out
13. `eval_variable` - Eval variable
14. `get_debugger_output` - Get program output


## Implementation Notes

1. All types store Delve data in internal fields with `json:"-"` tag
2. Use pointer types for optional fields
3. Implement conversion functions between Delve and LLM-friendly types
4. Maintain consistent naming conventions
5. Include rich contextual information
6. Preserve all debugging capabilities
7. Focus on human-readable output
```

--------------------------------------------------------------------------------
/pkg/debugger/program.go:
--------------------------------------------------------------------------------

```go
package debugger

import (
	"context"
	"fmt"
	"github.com/go-delve/delve/service/api"
	"net"
	"os"
	"path/filepath"
	"regexp"
	"time"

	"github.com/go-delve/delve/pkg/gobuild"
	"github.com/go-delve/delve/pkg/logflags"
	"github.com/go-delve/delve/pkg/proc"
	"github.com/go-delve/delve/service"
	"github.com/go-delve/delve/service/debugger"
	"github.com/go-delve/delve/service/rpc2"
	"github.com/go-delve/delve/service/rpccommon"
	"github.com/sunfmin/mcp-go-debugger/pkg/logger"
	"github.com/sunfmin/mcp-go-debugger/pkg/types"
)

// LaunchProgram starts a new program with debugging enabled
func (c *Client) LaunchProgram(program string, args []string) types.LaunchResponse {
	if c.client != nil {
		return c.createLaunchResponse(nil, program, args, fmt.Errorf("debug session already active"))
	}

	logger.Debug("Starting LaunchProgram for %s", program)

	// Ensure program file exists and is executable
	absPath, err := filepath.Abs(program)
	if err != nil {
		return c.createLaunchResponse(nil, program, args, fmt.Errorf("failed to get absolute path: %v", err))
	}

	if _, err := os.Stat(absPath); os.IsNotExist(err) {
		return c.createLaunchResponse(nil, program, args, fmt.Errorf("program file not found: %s", absPath))
	}

	// Get an available port for the debug server
	port, err := getFreePort()
	if err != nil {
		return c.createLaunchResponse(nil, program, args, fmt.Errorf("failed to find available port: %v", err))
	}

	// Configure Delve logging
	logflags.Setup(false, "", "")

	// Create a listener for the debug server
	listener, err := net.Listen("tcp", fmt.Sprintf("localhost:%d", port))
	if err != nil {
		return c.createLaunchResponse(nil, program, args, fmt.Errorf("couldn't start listener: %s", err))
	}

	// Create pipes for stdout and stderr
	stdoutReader, stdoutRedirect, err := proc.Redirector()
	if err != nil {
		return c.createLaunchResponse(nil, program, args, fmt.Errorf("failed to create stdout redirector: %v", err))
	}

	stderrReader, stderrRedirect, err := proc.Redirector()
	if err != nil {
		stdoutRedirect.File.Close()
		return c.createLaunchResponse(nil, program, args, fmt.Errorf("failed to create stderr redirector: %v", err))
	}

	// Create Delve config
	config := &service.Config{
		Listener:    listener,
		APIVersion:  2,
		AcceptMulti: true,
		ProcessArgs: append([]string{absPath}, args...),
		Debugger: debugger.Config{
			WorkingDir:     "",
			Backend:        "default",
			CheckGoVersion: true,
			DisableASLR:    true,
			Stdout:         stdoutRedirect,
			Stderr:         stderrRedirect,
		},
	}

	// Start goroutines to capture output
	go c.captureOutput(stdoutReader, "stdout")
	go c.captureOutput(stderrReader, "stderr")

	// Create and start the debugging server
	server := rpccommon.NewServer(config)
	if server == nil {
		return c.createLaunchResponse(nil, program, args, fmt.Errorf("failed to create debug server"))
	}

	c.server = server

	// Create a channel to signal when the server is ready or fails
	serverReady := make(chan error, 1)

	// Start server in a goroutine
	go func() {
		err := server.Run()
		if err != nil {
			logger.Debug("Debug server error: %v", err)
			serverReady <- err
		}
	}()

	// Try to connect to the server with a timeout
	addr := listener.Addr().String()
	ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
	defer cancel()

	// Try to connect repeatedly until timeout
	var connected bool
	for !connected {
		select {
		case <-ctx.Done():
			return c.createLaunchResponse(nil, program, args, fmt.Errorf("timed out waiting for debug server to start"))
		case err := <-serverReady:
			return c.createLaunchResponse(nil, program, args, fmt.Errorf("debug server failed to start: %v", err))
		default:
			client := rpc2.NewClient(addr)
			state, err := client.GetState()
			if err == nil && state != nil {
				c.client = client
				c.target = absPath
				connected = true

				return c.createLaunchResponse(state, program, args, nil)
			}
			time.Sleep(100 * time.Millisecond)
		}
	}

	return c.createLaunchResponse(nil, program, args, fmt.Errorf("failed to launch program"))
}

// AttachToProcess attaches to an existing process with the given PID
func (c *Client) AttachToProcess(pid int) types.AttachResponse {
	if c.client != nil {
		return c.createAttachResponse(nil, pid, "", nil, fmt.Errorf("debug session already active"))
	}

	logger.Debug("Starting AttachToProcess for PID %d", pid)

	// Get an available port for the debug server
	port, err := getFreePort()
	if err != nil {
		return c.createAttachResponse(nil, pid, "", nil, fmt.Errorf("failed to find available port: %v", err))
	}

	logger.Debug("Setting up Delve logging")
	// Configure Delve logging
	logflags.Setup(false, "", "")

	logger.Debug("Creating listener on port %d", port)
	// Create a listener for the debug server
	listener, err := net.Listen("tcp", fmt.Sprintf("localhost:%d", port))
	if err != nil {
		return c.createAttachResponse(nil, pid, "", nil, fmt.Errorf("couldn't start listener: %s", err))
	}

	// Note: When attaching to an existing process, we can't easily redirect its stdout/stderr
	// as those file descriptors are already connected. Output capture is limited for attach mode.
	logger.Debug("Note: Output redirection is limited when attaching to an existing process")

	logger.Debug("Creating Delve config for attach")
	// Create Delve config for attaching to process
	config := &service.Config{
		Listener:    listener,
		APIVersion:  2,
		AcceptMulti: true,
		ProcessArgs: []string{},
		Debugger: debugger.Config{
			AttachPid:      pid,
			Backend:        "default",
			CheckGoVersion: true,
			DisableASLR:    true,
		},
	}

	logger.Debug("Creating debug server")
	// Create and start the debugging server with attach to PID
	server := rpccommon.NewServer(config)
	if server == nil {
		return c.createAttachResponse(nil, pid, "", nil, fmt.Errorf("failed to create debug server"))
	}

	c.server = server

	// Create a channel to signal when the server is ready or fails
	serverReady := make(chan error, 1)

	logger.Debug("Starting debug server in goroutine")
	// Start server in a goroutine
	go func() {
		logger.Debug("Running server")
		err := server.Run()
		if err != nil {
			logger.Debug("Debug server error: %v", err)
			serverReady <- err
		}
		logger.Debug("Server run completed")
	}()

	logger.Debug("Waiting for server to start")

	// Try to connect to the server with a timeout
	addr := listener.Addr().String()

	// Wait up to 3 seconds for server to be available
	ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
	defer cancel()

	// Try to connect repeatedly until timeout
	connected := false
	for !connected {
		select {
		case <-ctx.Done():
			// Timeout reached
			return c.createAttachResponse(nil, pid, "", nil, fmt.Errorf("timed out waiting for debug server to start"))
		case err := <-serverReady:
			// Server reported an error
			return c.createAttachResponse(nil, pid, "", nil, fmt.Errorf("debug server failed to start: %v", err))
		default:
			// Try to connect
			client := rpc2.NewClient(addr)
			// Make a simple API call to test connection
			state, err := client.GetState()
			if err == nil && state != nil {
				// Connection successful
				c.client = client
				c.pid = pid
				connected = true
				logger.Debug("Successfully attached to process with PID: %d", pid)

				// Get initial state
				return c.createAttachResponse(state, pid, "", nil, nil)
			} else {
				// Failed, wait briefly and retry
				time.Sleep(100 * time.Millisecond)
			}
		}
	}

	return c.createAttachResponse(nil, pid, "", nil, fmt.Errorf("failed to attach to process"))
}

// Close terminates the debug session
func (c *Client) Close() (*types.CloseResponse, error) {
	if c.client == nil {
		return &types.CloseResponse{
			Status: "success",
			Context: types.DebugContext{
				Timestamp: time.Now(),
				Operation: "close",
			},
			Summary: "No active debug session to close",
		}, nil
	}

	// Signal to stop output capturing goroutines
	close(c.stopOutput)

	// Create a context with timeout to prevent indefinite hanging
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()

	// Create error channel
	errChan := make(chan error, 1)

	// Attempt to detach from the debugger in a separate goroutine
	go func() {
		err := c.client.Detach(true)
		if err != nil {
			logger.Debug("Warning: Failed to detach from debugged process: %v", err)
		}
		errChan <- err
	}()

	// Wait for completion or timeout
	var detachErr error
	select {
	case detachErr = <-errChan:
		// Operation completed successfully
	case <-ctx.Done():
		logger.Debug("Warning: Detach operation timed out after 5 seconds")
		detachErr = ctx.Err()
	}

	// Reset the client
	c.client = nil

	// Clean up the debug binary if it exists
	if c.target != "" {
		gobuild.Remove(c.target)
		c.target = ""
	}

	// Create a new channel for server stop operations
	stopChan := make(chan error, 1)

	// Stop the debug server if it's running
	if c.server != nil {
		go func() {
			err := c.server.Stop()
			if err != nil {
				logger.Debug("Warning: Failed to stop debug server: %v", err)
			}
			stopChan <- err
		}()

		// Wait for completion or timeout
		select {
		case <-stopChan:
			// Operation completed
		case <-time.After(5 * time.Second):
			logger.Debug("Warning: Server stop operation timed out after 5 seconds")
		}
	}

	// Create debug context
	debugContext := types.DebugContext{
		Timestamp: time.Now(),
		Operation: "close",
	}

	// Get exit code
	exitCode := 0
	if detachErr != nil {
		exitCode = 1
	}

	// Create close response
	response := &types.CloseResponse{
		Status:   "success",
		Context:  debugContext,
		ExitCode: exitCode,
		Summary:  fmt.Sprintf("Debug session closed with exit code %d", exitCode),
	}

	logger.Debug("Close response: %+v", response)
	return response, detachErr
}

// DebugSourceFile compiles and debugs a Go source file
func (c *Client) DebugSourceFile(sourceFile string, args []string) types.DebugSourceResponse {
	if c.client != nil {
		return c.createDebugSourceResponse(nil, sourceFile, "", args, fmt.Errorf("debug session already active"))
	}

	// Ensure source file exists
	absPath, err := filepath.Abs(sourceFile)
	if err != nil {
		return c.createDebugSourceResponse(nil, sourceFile, "", args, fmt.Errorf("failed to get absolute path: %v", err))
	}

	if _, err := os.Stat(absPath); os.IsNotExist(err) {
		return c.createDebugSourceResponse(nil, sourceFile, "", args, fmt.Errorf("source file not found: %s", absPath))
	}

	// Generate a unique debug binary name
	debugBinary := gobuild.DefaultDebugBinaryPath("debug_binary")

	logger.Debug("Compiling source file %s to %s", absPath, debugBinary)

	// Compile the source file with output capture
	cmd, output, err := gobuild.GoBuildCombinedOutput(debugBinary, []string{absPath}, "-gcflags all=-N")
	if err != nil {
		logger.Debug("Build command: %s", cmd)
		logger.Debug("Build output: %s", string(output))
		gobuild.Remove(debugBinary)
		return c.createDebugSourceResponse(nil, sourceFile, debugBinary, args, fmt.Errorf("failed to compile source file: %v\nOutput: %s", err, string(output)))
	}

	// Launch the compiled binary with the debugger
	response := c.LaunchProgram(debugBinary, args)
	if response.Context.ErrorMessage != "" {
		gobuild.Remove(debugBinary)
		return c.createDebugSourceResponse(nil, sourceFile, debugBinary, args, fmt.Errorf(response.Context.ErrorMessage))
	}

	// Store the binary path for cleanup
	c.target = debugBinary

	return c.createDebugSourceResponse(response.Context.DelveState, sourceFile, debugBinary, args, nil)
}

// DebugTest compiles and debugs a Go test function
func (c *Client) DebugTest(testFilePath string, testName string, testFlags []string) types.DebugTestResponse {
	response := types.DebugTestResponse{
		TestName:  testName,
		TestFile:  testFilePath,
		TestFlags: testFlags,
	}
	if c.client != nil {
		return c.createDebugTestResponse(nil, &response, fmt.Errorf("debug session already active"))
	}

	// Ensure test file exists
	absPath, err := filepath.Abs(testFilePath)
	if err != nil {
		return c.createDebugTestResponse(nil, &response, fmt.Errorf("failed to get absolute path: %v", err))
	}

	if _, err := os.Stat(absPath); os.IsNotExist(err) {
		return c.createDebugTestResponse(nil, &response, fmt.Errorf("test file not found: %s", absPath))
	}

	// Get the directory of the test file
	testDir := filepath.Dir(absPath)
	logger.Debug("Test directory: %s", testDir)

	// Generate a unique debug binary name
	debugBinary := gobuild.DefaultDebugBinaryPath("debug.test")

	logger.Debug("Compiling test package in %s to %s", testDir, debugBinary)

	// Save current directory
	currentDir, err := os.Getwd()
	if err != nil {
		return c.createDebugTestResponse(nil, &response, fmt.Errorf("failed to get current directory: %v", err))
	}

	// Change to test directory
	if err := os.Chdir(testDir); err != nil {
		return c.createDebugTestResponse(nil, &response, fmt.Errorf("failed to change to test directory: %v", err))
	}

	// Ensure we change back to original directory
	defer func() {
		if err := os.Chdir(currentDir); err != nil {
			logger.Error("Failed to restore original directory: %v", err)
		}
	}()

	// Compile the test package with output capture using test-specific build flags
	cmd, output, err := gobuild.GoTestBuildCombinedOutput(debugBinary, []string{testDir}, "-gcflags all=-N")
	response.BuildCommand = cmd
	response.BuildOutput = string(output)
	if err != nil {
		gobuild.Remove(debugBinary)
		return c.createDebugTestResponse(nil, &response, fmt.Errorf("failed to compile test package: %v\nOutput: %s", err, string(output)))
	}

	// Create args to run the specific test
	args := []string{
		"-test.v", // Verbose output
	}

	// Add specific test pattern if provided
	if testName != "" {
		// Escape special regex characters in the test name
		escapedTestName := regexp.QuoteMeta(testName)
		// Create a test pattern that matches exactly the provided test name
		args = append(args, fmt.Sprintf("-test.run=^%s$", escapedTestName))
	}

	// Add any additional test flags
	args = append(args, testFlags...)

	logger.Debug("Launching test binary with debugger, test name: %s, args: %v", testName, args)
	// Launch the compiled test binary with the debugger
	response2 := c.LaunchProgram(debugBinary, args)
	if response2.Context.ErrorMessage != "" {
		gobuild.Remove(debugBinary)
		return c.createDebugTestResponse(nil, &response, fmt.Errorf(response.Context.ErrorMessage))
	}

	// Store the binary path for cleanup
	c.target = debugBinary

	return c.createDebugTestResponse(response2.Context.DelveState, &response, nil)
}

// createLaunchResponse creates a response for the launch command
func (c *Client) createLaunchResponse(state *api.DebuggerState, program string, args []string, err error) types.LaunchResponse {
	context := c.createDebugContext(state)
	context.Operation = "launch"

	if err != nil {
		context.ErrorMessage = err.Error()
	}

	return types.LaunchResponse{
		Context:  &context,
		Program:  program,
		Args:     args,
		ExitCode: 0,
	}
}

// createAttachResponse creates a response for the attach command
func (c *Client) createAttachResponse(state *api.DebuggerState, pid int, target string, process *types.Process, err error) types.AttachResponse {
	context := c.createDebugContext(state)
	context.Operation = "attach"

	if err != nil {
		context.ErrorMessage = err.Error()
	}

	return types.AttachResponse{
		Status:  "success",
		Context: &context,
		Pid:     pid,
		Target:  target,
		Process: process,
	}
}

// createDebugSourceResponse creates a response for the debug source command
func (c *Client) createDebugSourceResponse(state *api.DebuggerState, sourceFile string, debugBinary string, args []string, err error) types.DebugSourceResponse {
	context := c.createDebugContext(state)
	context.Operation = "debug_source"

	if err != nil {
		context.ErrorMessage = err.Error()
	}

	return types.DebugSourceResponse{
		Status:      "success",
		Context:     &context,
		SourceFile:  sourceFile,
		DebugBinary: debugBinary,
		Args:        args,
	}
}

// createDebugTestResponse creates a response for the debug test command
func (c *Client) createDebugTestResponse(state *api.DebuggerState, response *types.DebugTestResponse, err error) types.DebugTestResponse {
	context := c.createDebugContext(state)
	context.Operation = "debug_test"
	response.Context = &context

	if err != nil {
		context.ErrorMessage = err.Error()
		response.Status = "error"
	}

	return *response
}

```

--------------------------------------------------------------------------------
/pkg/mcp/server_test.go:
--------------------------------------------------------------------------------

```go
package mcp

import (
	"context"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"testing"
	"time"

	"github.com/mark3labs/mcp-go/mcp"
	"github.com/pmezard/go-difflib/difflib"
	"github.com/sunfmin/mcp-go-debugger/pkg/types"
)

// We'll use the package-level types from server.go for API responses

func getTextContent(result *mcp.CallToolResult) string {
	if result == nil || len(result.Content) == 0 {
		return ""
	}

	// TextContent is a struct implementing the Content interface
	// We need to try to cast it based on the struct's provided functions
	if tc, ok := mcp.AsTextContent(result.Content[0]); ok {
		return tc.Text
	}

	return ""
}

// Helper function to find the line number for a specific statement in a file
func findLineNumber(filePath, targetStatement string) int {
	content, err := os.ReadFile(filePath)
	if err != nil {
		panic(fmt.Sprintf("failed to read file: %v", err))
	}

	lines := strings.Split(string(content), "\n")
	for i, line := range lines {
		line = strings.TrimSpace(line)
		if strings.Contains(line, targetStatement) {
			return i + 1 // Line numbers are 1-indexed
		}
	}

	panic(fmt.Sprintf("statement not found: %s", targetStatement))
}

// Helper function to unmarshal JSON data and panic if it fails
func mustUnmarshalJSON(data string, v interface{}) {
	if err := json.Unmarshal([]byte(data), v); err != nil {
		panic(fmt.Sprintf("failed to unmarshal JSON: %v", err))
	}
}

// Helper function to get text content from result and unmarshal it to the provided variable
func mustGetAndUnmarshalJSON(result *mcp.CallToolResult, v interface{}) {
	text := getTextContent(result)
	if text == "" {
		panic("empty text content in result")
	}
	mustUnmarshalJSON(text, v)
}

func expectSuccess(t *testing.T, result *mcp.CallToolResult, err error, response interface{}) {
	t.Helper()
	if err != nil {
		t.Fatalf("Expected success, got error: %v", err)
	}

	mustGetAndUnmarshalJSON(result, response)

	output, _ := json.MarshalIndent(response, "", "  ")
	t.Logf("Response: %#+v\n %s", response, string(output))
}

func TestDebugWorkflow(t *testing.T) {
	// Skip test in short mode
	if testing.Short() {
		t.Skip("Skipping integration test in short mode")
	}

	// Create a test Go file with multiple functions and variables for debugging
	testFile := createComplexTestGoFile(t)
	defer os.RemoveAll(filepath.Dir(testFile))

	// Read and print the file content for debugging
	fileContent, err := ioutil.ReadFile(testFile)
	if err != nil {
		t.Fatalf("Failed to read test file: %v", err)
	}
	t.Logf("Generated test file content:\n%s", string(fileContent))

	// Create server
	server := NewMCPDebugServer("test-version")
	ctx := context.Background()

	// Step 1: Launch the debugger
	launchRequest := mcp.CallToolRequest{}
	launchRequest.Params.Arguments = map[string]interface{}{
		"file": testFile,
		"args": []interface{}{"test-arg1", "test-arg2"},
	}

	debugResult, err := server.DebugSourceFile(ctx, launchRequest)
	expectSuccess(t, debugResult, err, &types.DebugSourceResponse{})

	// Give the debugger time to initialize
	time.Sleep(200 * time.Millisecond)

	// Find line numbers for key statements
	fmtPrintlnLine := findLineNumber(testFile, "fmt.Println(\"Starting debug test program\")")
	t.Logf("Found fmt.Println statement at line %d", fmtPrintlnLine)

	countVarLine := findLineNumber(testFile, "count := 10")
	t.Logf("Found count variable at line %d", countVarLine)

	// Find the calculate function line
	calculateLine := findLineNumber(testFile, "func calculate(n int) int {")
	t.Logf("Found calculate function at line %d", calculateLine)

	// Find the line with a := n * 2 inside calculate
	aVarLine := findLineNumber(testFile, "a := n * 2")
	t.Logf("Found a variable assignment at line %d", aVarLine)

	// Step 2: Set a breakpoint at the start of calculate function
	setBreakpointRequest := mcp.CallToolRequest{}
	setBreakpointRequest.Params.Arguments = map[string]interface{}{
		"file": testFile,
		"line": float64(aVarLine), // Line with first statement in calculate function
	}

	breakpointResult, err := server.SetBreakpoint(ctx, setBreakpointRequest)
	breakpointResponse := &types.BreakpointResponse{}
	expectSuccess(t, breakpointResult, err, breakpointResponse)

	if breakpointResponse.Breakpoint.ID <= 0 {
		t.Errorf("Expected valid breakpoint ID, got: %d", breakpointResponse.Breakpoint.ID)
	}

	// Step 3: List breakpoints to verify
	listBreakpointsRequest := mcp.CallToolRequest{}
	listResult, err := server.ListBreakpoints(ctx, listBreakpointsRequest)
	listBreakpointsResponse := types.BreakpointListResponse{}
	expectSuccess(t, listResult, err, &listBreakpointsResponse)

	if len(listBreakpointsResponse.Breakpoints) == 0 {
		t.Errorf("Expected at least one breakpoint, got none")
	}

	// Remember the breakpoint ID for later removal
	firstBreakpointID := listBreakpointsResponse.Breakpoints[0].ID

	// Step 4: Set another breakpoint at the countVarLine
	setBreakpointRequest2 := mcp.CallToolRequest{}
	setBreakpointRequest2.Params.Arguments = map[string]interface{}{
		"file": testFile,
		"line": float64(countVarLine), // Line with count variable
	}

	_, err = server.SetBreakpoint(ctx, setBreakpointRequest2)
	if err != nil {
		t.Fatalf("Failed to set second breakpoint: %v", err)
	}

	// Give the debugger more time to initialize fully
	time.Sleep(300 * time.Millisecond)

	// Step 6: Continue execution to hit first breakpoint
	continueRequest := mcp.CallToolRequest{}
	continueResult, err := server.Continue(ctx, continueRequest)
	expectSuccess(t, continueResult, err, &types.ContinueResponse{})

	// Allow time for the breakpoint to be hit
	time.Sleep(300 * time.Millisecond)

	// Issue a second continue to reach the breakpoint in the calculate function
	continueResult2, err := server.Continue(ctx, continueRequest)
	expectSuccess(t, continueResult2, err, &types.ContinueResponse{})

	// Allow time for the second breakpoint to be hit
	time.Sleep(300 * time.Millisecond)

	// Step 8: Eval variable 'n' at the first breakpoint in calculate()
	evalRequest := mcp.CallToolRequest{}
	evalRequest.Params.Arguments = map[string]interface{}{
		"name":  "n",
		"depth": float64(1),
	}

	evalVariableResult, err := server.EvalVariable(ctx, evalRequest)
	evalVariableResponse := &types.EvalVariableResponse{}
	expectSuccess(t, evalVariableResult, err, evalVariableResponse)

	// Verify the variable value is what we expect
	if evalVariableResponse.Variable.Value != "10" {
		t.Fatalf("Expected n to be 10, got %s", evalVariableResponse.Variable.Value)
	}

	// Step 9: Use step over to go to the next line
	stepOverRequest := mcp.CallToolRequest{}
	stepResult, err := server.StepOver(ctx, stepOverRequest)
	expectSuccess(t, stepResult, err, &types.StepResponse{})

	// Allow time for the step to complete
	time.Sleep(200 * time.Millisecond)

	// Step 10: Eval variable 'a' which should be defined now
	evalRequest2 := mcp.CallToolRequest{}
	evalRequest2.Params.Arguments = map[string]interface{}{
		"name":  "a",
		"depth": float64(1),
	}

	evalVariableResult2, err := server.EvalVariable(ctx, evalRequest2)
	evalVariableResponse2 := &types.EvalVariableResponse{}
	expectSuccess(t, evalVariableResult2, err, evalVariableResponse2)

	// Verify the variable value is what we expect (a = n * 2, so a = 20)
	if evalVariableResponse2.Variable.Value != "20" {
		t.Fatalf("Expected a to be 20, got %s", evalVariableResponse2.Variable.Value)
	}

	// Step 11: Step over again
	stepResult2, err := server.StepOver(ctx, stepOverRequest)
	expectSuccess(t, stepResult2, err, &types.StepResponse{})

	// Allow time for the step to complete
	time.Sleep(200 * time.Millisecond)

	// Step 12: Remove the first breakpoint
	removeBreakpointRequest := mcp.CallToolRequest{}
	removeBreakpointRequest.Params.Arguments = map[string]interface{}{
		"id": float64(firstBreakpointID),
	}

	removeResult, err := server.RemoveBreakpoint(ctx, removeBreakpointRequest)
	expectSuccess(t, removeResult, err, &types.BreakpointResponse{})

	// Step 14: Continue execution to complete the program
	continueResult, err = server.Continue(ctx, continueRequest)
	expectSuccess(t, continueResult, err, &types.ContinueResponse{})

	// Allow time for program to complete
	time.Sleep(300 * time.Millisecond)

	// Check for captured output
	outputRequest := mcp.CallToolRequest{}
	outputResult, err := server.GetDebuggerOutput(ctx, outputRequest)
	var outputResponse = &types.DebuggerOutputResponse{}
	expectSuccess(t, outputResult, err, outputResponse)

	// Verify that output was captured
	if outputResponse.Stdout == "" {
		t.Errorf("Expected stdout to be captured, but got empty output")
	}

	// Verify output contains expected strings
	if !strings.Contains(outputResponse.Stdout, "Starting debug test program") {
		t.Errorf("Expected stdout to contain startup message, got: %s", outputResponse.Stdout)
	}

	if !strings.Contains(outputResponse.Stdout, "Arguments:") {
		t.Errorf("Expected stdout to contain arguments message, got: %s", outputResponse.Stdout)
	}

	// Verify output summary is present and contains expected content
	if outputResponse.OutputSummary == "" {
		t.Errorf("Expected output summary to be present, but got empty summary")
	}

	if !strings.Contains(outputResponse.OutputSummary, "Program output") {
		t.Errorf("Expected output summary to mention program output, got: %s", outputResponse.OutputSummary)
	}

	t.Logf("Captured output: %s", outputResponse.Stdout)
	t.Logf("Output summary: %s", outputResponse.OutputSummary)

	// Clean up by closing the debug session
	closeRequest := mcp.CallToolRequest{}
	closeResult, err := server.Close(ctx, closeRequest)
	expectSuccess(t, closeResult, err, &types.CloseResponse{})

	t.Log("TestDebugWorkflow completed successfully")
}

// Helper function to create a more complex Go file for debugging tests
func createComplexTestGoFile(t *testing.T) string {
	tempDir, err := ioutil.TempDir("", "go-debugger-complex-test")
	if err != nil {
		t.Fatalf("Failed to create temp directory: %v", err)
	}

	goFile := filepath.Join(tempDir, "main.go")
	content := `package main

import (
	"fmt"
	"os"
	"time"
)

type Person struct {
	Name string
	Age  int
}

func (p Person) Greet() string {
	return fmt.Sprintf("Hello, my name is %s and I am %d years old", p.Name, p.Age)
}

func main() {
	fmt.Println("Starting debug test program")
	
	// Process command line arguments
	args := os.Args[1:]
	if len(args) > 0 {
		fmt.Println("Arguments:")
		for i, arg := range args {
			fmt.Printf("  %d: %s\n", i+1, arg)
		}
	}
	
	// Create some variables for debugging
	count := 10
	name := "DebugTest"
	enabled := true
	
	// Call a function that we can set breakpoints in
	result := calculate(count)
	fmt.Printf("Result of calculation: %d\n", result)
	
	// Create and use a struct
	person := Person{
		Name: name,
		Age:  count * 3,
	}
	
	message := person.Greet()
	fmt.Println(message)
	
	// Add a small delay so the program doesn't exit immediately
	time.Sleep(100 * time.Millisecond)
	
	fmt.Println("Program completed, enabled:", enabled)
}

func calculate(n int) int {
	// A function with multiple steps for debugging
	a := n * 2
	b := a + 5
	c := b * b
	d := processFurther(c)
	return d
}

func processFurther(value int) int {
	// Another function to test step in/out
	result := value
	if value > 100 {
		result = value / 2
	} else {
		result = value * 2
	}
	return result
}
`
	if err := ioutil.WriteFile(goFile, []byte(content), 0644); err != nil {
		t.Fatalf("Failed to write complex test Go file: %v", err)
	}

	return goFile
}

func TestDebugTest(t *testing.T) {
	// Skip test in short mode
	if testing.Short() {
		t.Skip("Skipping integration test in short mode")
	}

	// Get paths to our calculator test files
	testFilePath, err := filepath.Abs("../../testdata/calculator/calculator_test.go")
	if err != nil {
		t.Fatalf("Failed to get absolute path to test file: %v", err)
	}

	// Make sure the test file exists
	if _, err := os.Stat(testFilePath); os.IsNotExist(err) {
		t.Fatalf("Test file does not exist: %s", testFilePath)
	}

	// Find line number for the test function and the Add call
	testFuncLine := findLineNumber(testFilePath, "func TestAdd(t *testing.T) {")

	addCallLine := findLineNumber(testFilePath, "result := Add(2, 3)")

	t.Logf("Found TestAdd function at line %d, Add call at line %d", testFuncLine, addCallLine)

	// Create server
	server := NewMCPDebugServer("test-version")
	ctx := context.Background()

	// Step 1: Launch the debug test
	debugTestRequest := mcp.CallToolRequest{}
	debugTestRequest.Params.Arguments = map[string]interface{}{
		"testfile": testFilePath,
		"testname": "TestAdd",
	}

	debugResult, err := server.DebugTest(ctx, debugTestRequest)
	expectSuccess(t, debugResult, err, &types.DebugTestResponse{})

	// Give the debugger time to initialize
	time.Sleep(300 * time.Millisecond)

	// Step 2: Set a breakpoint where Add is called in the test function
	setBreakpointRequest := mcp.CallToolRequest{}
	setBreakpointRequest.Params.Arguments = map[string]interface{}{
		"file": testFilePath,
		"line": float64(addCallLine),
	}

	breakpointResult, err := server.SetBreakpoint(ctx, setBreakpointRequest)
	expectSuccess(t, breakpointResult, err, &types.BreakpointResponse{})

	// Step 3: Continue execution to hit breakpoint
	continueRequest := mcp.CallToolRequest{}
	continueResult, err := server.Continue(ctx, continueRequest)
	expectSuccess(t, continueResult, err, &types.ContinueResponse{})

	// Allow time for the breakpoint to be hit
	time.Sleep(500 * time.Millisecond)

	// First try to eval 't', which should be available in all test functions
	evalRequest := mcp.CallToolRequest{}
	evalRequest.Params.Arguments = map[string]interface{}{
		"name":  "t",
		"depth": float64(1),
	}

	evalResult, err := server.EvalVariable(ctx, evalRequest)
	expectSuccess(t, evalResult, err, &types.EvalVariableResponse{})

	// Now try to step once to execute the Add function call
	stepRequest := mcp.CallToolRequest{}
	stepResult, err := server.Step(ctx, stepRequest)
	expectSuccess(t, stepResult, err, &types.StepResponse{})

	// Allow time for the step to complete
	time.Sleep(200 * time.Millisecond)

	// Now look for result variable, which should be populated after the Add call
	evalResultVarRequest := mcp.CallToolRequest{}
	evalResultVarRequest.Params.Arguments = map[string]interface{}{
		"name":  "result",
		"depth": float64(1),
	}

	resultVarEvalResult, err := server.EvalVariable(ctx, evalResultVarRequest)
	var evalResultVarResponse = &types.EvalVariableResponse{}
	expectSuccess(t, resultVarEvalResult, err, evalResultVarResponse)

	if evalResultVarResponse.Variable.Value != "5" {
		t.Fatalf("Expected result to be 5, got %s", evalResultVarResponse.Variable.Value)
	}

	// Step 6: Continue execution to complete the test
	continueResult, err = server.Continue(ctx, continueRequest)
	expectSuccess(t, continueResult, err, &types.ContinueResponse{})

	// Allow time for program to complete
	time.Sleep(300 * time.Millisecond)

	// Check for captured output
	outputRequest := mcp.CallToolRequest{}
	outputResult, err := server.GetDebuggerOutput(ctx, outputRequest)
	if err == nil && outputResult != nil {
		outputText := getTextContent(outputResult)
		t.Logf("Captured program output: %s", outputText)
	}

	// Clean up by closing the debug session
	closeRequest := mcp.CallToolRequest{}
	closeResult, err := server.Close(ctx, closeRequest)
	expectSuccess(t, closeResult, err, &types.CloseResponse{})

	t.Log("TestDebugTest completed successfully")
}

func prettyJSONDiff(expected, actual interface{}) string {
	expectedJSON, _ := json.MarshalIndent(expected, "", "  ")
	actualJSON, _ := json.MarshalIndent(actual, "", "  ")

	diff := difflib.UnifiedDiff{
		A:        difflib.SplitLines(string(expectedJSON)),
		B:        difflib.SplitLines(string(actualJSON)),
		FromFile: "Expected",
		ToFile:   "Actual",
		Context:  3,
	}

	diffText, err := difflib.GetUnifiedDiffString(diff)
	if err != nil {
		return fmt.Sprintf("Failed to generate diff: %v", err)
	}

	return diffText
}

func TestEvalComplexVariables(t *testing.T) {
	// Skip test in short mode
	if testing.Short() {
		t.Skip("Skipping integration test in short mode")
	}

	// Create test file
	testFile := createComplexTestGoFile(t)
	//defer os.RemoveAll(filepath.Dir(testFile))

	// Create server
	server := NewMCPDebugServer("test-version")
	ctx := context.Background()

	// Launch the debugger
	launchRequest := mcp.CallToolRequest{}
	launchRequest.Params.Arguments = map[string]interface{}{
		"file": testFile,
		"args": []interface{}{"test-arg1", "test-arg2"},
	}

	debugResult, err := server.DebugSourceFile(ctx, launchRequest)
	expectSuccess(t, debugResult, err, &types.DebugSourceResponse{})

	// Give the debugger time to initialize
	time.Sleep(200 * time.Millisecond)

	// Find line numbers for breakpoints
	personVarLine := findLineNumber(testFile, "message := person.Greet()")
	t.Logf("Found person variable declaration at line %d", personVarLine)

	// Set breakpoint at person variable declaration
	setBreakpointRequest := mcp.CallToolRequest{}
	setBreakpointRequest.Params.Arguments = map[string]interface{}{
		"file": testFile,
		"line": float64(personVarLine),
	}

	breakpointResult, err := server.SetBreakpoint(ctx, setBreakpointRequest)
	expectSuccess(t, breakpointResult, err, &types.BreakpointResponse{})

	// Continue to breakpoint
	continueRequest := mcp.CallToolRequest{}
	continueResult, err := server.Continue(ctx, continueRequest)
	expectSuccess(t, continueResult, err, &types.ContinueResponse{})

	// Allow time for breakpoint to be hit
	time.Sleep(300 * time.Millisecond)

	// Test cases for different variable types
	testCases := []struct {
		name        string
		varName     string
		depth       int
		expectedVar types.Variable
	}{
		{
			name:    "Struct variable",
			varName: "person",
			depth:   2,
			expectedVar: types.Variable{
				Name:  "person",
				Type:  "main.Person",
				Scope: "",
				Kind:  "struct",
				Value: "{Name:DebugTest, Age:30}",
			},
		},
		{
			name:    "Struct variable property",
			varName: "person.Age",
			depth:   1,
			expectedVar: types.Variable{
				Name:  "person.Age",
				Type:  "int",
				Scope: "",
				Kind:  "integer",
				Value: "30",
			},
		},
		{
			name:    "Slice variable",
			varName: "args",
			depth:   1,
			expectedVar: types.Variable{
				Name:  "args",
				Type:  "[]string",
				Scope: "",
				Kind:  "array",
				Value: "[test-arg1, test-arg2]",
			},
		},
	}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			evalRequest := mcp.CallToolRequest{}
			evalRequest.Params.Arguments = map[string]interface{}{
				"name":  tc.varName,
				"depth": float64(tc.depth),
			}

			evalResult, err := server.EvalVariable(ctx, evalRequest)
			var evalResponse = &types.EvalVariableResponse{}
			expectSuccess(t, evalResult, err, evalResponse)

			diff := prettyJSONDiff(tc.expectedVar, evalResponse.Variable)
			if diff != "" {
				t.Errorf("Variable mismatch: %s", diff)
			}
		})
	}

	// Clean up by closing the debug session
	closeRequest := mcp.CallToolRequest{}
	closeResult, err := server.Close(ctx, closeRequest)
	expectSuccess(t, closeResult, err, &types.CloseResponse{})
}

```