@@ -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.
235259func 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