Skip to content

Commit 5185ed6

Browse files
author
Marek Safarik
committed
Copilot pull request review fix
Signed-off-by: Marek Safarik <[email protected]>
1 parent 4a8c531 commit 5185ed6

File tree

1 file changed

+60
-9
lines changed

1 file changed

+60
-9
lines changed

mcp-client/main.go

Lines changed: 60 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ const (
1919
mcpClientName = "mcp-client"
2020
mcpClientVersion = "v1.0.0"
2121

22-
claudeModel = anthropic.ModelClaude_3_Haiku_20240307
23-
maxTokens = 1024
22+
claudeModel = anthropic.ModelClaude3_5HaikuLatest
23+
maxTokens = 2048
2424
maxAgentTurns = 5
2525

2626
systemPrompt = `You are an autonomous agent with access to Keylime system management tools. Your goal is to help users manage and monitor their Keylime infrastructure.
@@ -40,9 +40,9 @@ func main() {
4040
log.Printf("Warning: .env file not loaded: %v", err)
4141
}
4242

43-
apiKey := os.Getenv("ANTHROPIC_API_KEY")
43+
apiKey := strings.TrimSpace(os.Getenv("ANTHROPIC_API_KEY"))
4444
if apiKey == "" {
45-
log.Fatal("ANTHROPIC_API_KEY environment variable not set")
45+
log.Fatal("ANTHROPIC_API_KEY environment variable not set or is empty after trimming whitespace")
4646
}
4747

4848
if len(os.Args) <= 1 {
@@ -79,12 +79,27 @@ func connectToMCPServer(ctx context.Context) (*mcp.ClientSession, error) {
7979
Version: mcpClientVersion,
8080
}, nil)
8181

82-
transport := &mcp.CommandTransport{Command: exec.Command(serverPath)}
82+
cmd := exec.Command(serverPath)
83+
transport := &mcp.CommandTransport{Command: cmd}
8384
session, err := client.Connect(ctx, transport, nil)
8485
if err != nil {
8586
return nil, fmt.Errorf("failed to connect: %w", err)
8687
}
8788

89+
// Monitor server process for unexpected exits
90+
if cmd.Process != nil {
91+
go func() {
92+
state, err := cmd.Process.Wait()
93+
if err != nil {
94+
log.Printf("[Warning] MCP server process monitoring failed: %v", err)
95+
} else if !state.Success() {
96+
log.Printf("[Error] MCP server process exited unexpectedly with status: %v", state)
97+
} else {
98+
log.Printf("[Info] MCP server process exited normally")
99+
}
100+
}()
101+
}
102+
88103
log.Printf("Connected to MCP server: %s", serverPath)
89104
return session, nil
90105
}
@@ -113,7 +128,10 @@ func convertMCPToolToClaudeTool(tool *mcp.Tool) anthropic.ToolUnionParam {
113128
}
114129

115130
properties := inputSchemaMap["properties"]
116-
required, _ := inputSchemaMap["required"].([]string)
131+
var required []string
132+
if r, ok := inputSchemaMap["required"].([]string); ok {
133+
required = r
134+
}
117135

118136
toolParam := anthropic.ToolUnionParamOfTool(
119137
anthropic.ToolInputSchemaParam{
@@ -150,12 +168,13 @@ func runAgentLoop(ctx context.Context, client anthropic.Client, session *mcp.Cli
150168
assistantContent, toolResults, hasToolUse := processClaudeResponse(ctx, session, message)
151169

152170
if !hasToolUse {
153-
break
171+
return nil
154172
}
155173

156174
messages = append(messages, anthropic.NewAssistantMessage(assistantContent...))
157175
messages = append(messages, anthropic.NewUserMessage(toolResults...))
158176
}
177+
finalMessage(ctx, client, messages)
159178

160179
return nil
161180
}
@@ -216,22 +235,27 @@ func executeToolCall(
216235
}
217236

218237
if result.IsError {
219-
log.Printf("[Error] Tool execution failed")
238+
errorDetails := extractTextContent(result.Content)
239+
log.Printf("[Error] Tool execution failed for tool '%s': %s", toolUse.Name, errorDetails)
220240
return anthropic.NewToolResultBlock(
221241
toolUse.ID,
222-
"Tool execution failed",
242+
fmt.Sprintf("Tool '%s' execution failed: %s", toolUse.Name, errorDetails),
223243
true,
224244
)
225245
}
226246

227247
resultText := extractTextContent(result.Content)
248+
if resultText == "" {
249+
log.Printf("[Warning] Tool returned empty content - this might indicate an unexpected response from MCP server")
250+
}
228251
log.Printf("================================================")
229252
log.Printf("[Tool Result]\n%s", resultText)
230253
log.Printf("================================================")
231254

232255
return anthropic.NewToolResultBlock(toolUse.ID, resultText, false)
233256
}
234257

258+
// extractTextContent returns the concatenated text from all mcp.TextContent elements in the given slice.
235259
func extractTextContent(content []mcp.Content) string {
236260
var resultText strings.Builder
237261

@@ -243,3 +267,30 @@ func extractTextContent(content []mcp.Content) string {
243267

244268
return resultText.String()
245269
}
270+
271+
// finalMessage asks LLM to summarize what happened during session if max turns are reached
272+
func finalMessage(ctx context.Context, client anthropic.Client, messages []anthropic.MessageParam) {
273+
274+
summaryPrompt := `I've reached the maximum number of allowed turns. Please provide a summary of:\n
275+
1. What you accomplished so far
276+
2. What still needs to be done
277+
3. Any issues or blockers encountered`
278+
279+
messages = append(messages, anthropic.NewUserMessage(anthropic.NewTextBlock(summaryPrompt)))
280+
281+
finalMessage, err := client.Messages.New(ctx, anthropic.MessageNewParams{
282+
Model: claudeModel,
283+
MaxTokens: maxTokens,
284+
System: []anthropic.TextBlockParam{{Type: "text", Text: systemPrompt}},
285+
Messages: messages,
286+
})
287+
if err != nil {
288+
log.Printf("Failed to get final summary: %v", err)
289+
} else {
290+
for _, block := range finalMessage.Content {
291+
if textBlock, ok := block.AsAny().(anthropic.TextBlock); ok {
292+
fmt.Println(textBlock.Text)
293+
}
294+
}
295+
}
296+
}

0 commit comments

Comments
 (0)