package main import ( "context" "encoding/json" "os/exec" "github.com/mark3labs/mcp-go/mcp" ) type doctorArgs struct { Subcommand string `json:"subcommand,omitempty"` } func doctorTool() mcp.Tool { return mcp.NewTool("fn_doctor", mcp.WithDescription("Run `fn doctor [subcommand] --json` and return the parsed report. Subcommands: artefacts, services, sync, uses-functions, unused. Empty = all checks. Read-only."), mcp.WithString("subcommand", mcp.Description("Subcommand. Empty runs all."), mcp.Enum("", "artefacts", "services", "sync", "uses-functions", "unused"), ), ) } func (d *deps) handleDoctor(ctx context.Context, _ mcp.CallToolRequest, args doctorArgs) (*mcp.CallToolResult, error) { bin := d.fnBin() cmdArgs := []string{"doctor"} if args.Subcommand != "" { cmdArgs = append(cmdArgs, args.Subcommand) } cmdArgs = append(cmdArgs, "--json") cmd := exec.CommandContext(ctx, bin, cmdArgs...) cmd.Dir = d.root out, err := cmd.Output() if err != nil { stderr := "" if ee, ok := err.(*exec.ExitError); ok { stderr = string(ee.Stderr) } return mcp.NewToolResultError("fn doctor: " + err.Error() + "\n" + stderr), nil } // Try to parse JSON; if it fails, return the raw output. var report any if jsonErr := json.Unmarshal(out, &report); jsonErr != nil { return mcp.NewToolResultText(string(out)), nil } b, _ := json.MarshalIndent(map[string]any{"subcommand": args.Subcommand, "report": report}, "", " ") return mcp.NewToolResultText(string(b)), nil }