computeMCPToolsDiff computes the diff between two runs' MCP tool usage. run1 is the "before" (baseline) and run2 is the "after" (comparison target).
(run1, run2 *MCPToolUsageData)
| 410 | // computeMCPToolsDiff computes the diff between two runs' MCP tool usage. |
| 411 | // run1 is the "before" (baseline) and run2 is the "after" (comparison target). |
| 412 | func computeMCPToolsDiff(run1, run2 *MCPToolUsageData) *MCPToolsDiff { |
| 413 | run1Count, run2Count := 0, 0 |
| 414 | if run1 != nil { |
| 415 | run1Count = len(run1.Summary) |
| 416 | } |
| 417 | if run2 != nil { |
| 418 | run2Count = len(run2.Summary) |
| 419 | } |
| 420 | auditDiffLog.Printf("Computing MCP tools diff: run1_tools=%d, run2_tools=%d", run1Count, run2Count) |
| 421 | run1Tools := make(map[string]MCPToolSummary) |
| 422 | run2Tools := make(map[string]MCPToolSummary) |
| 423 | |
| 424 | if run1 != nil { |
| 425 | for _, s := range run1.Summary { |
| 426 | run1Tools[mcpToolKey(s.ServerName, s.ToolName)] = s |
| 427 | } |
| 428 | } |
| 429 | if run2 != nil { |
| 430 | for _, s := range run2.Summary { |
| 431 | run2Tools[mcpToolKey(s.ServerName, s.ToolName)] = s |
| 432 | } |
| 433 | } |
| 434 | |
| 435 | allKeys := make(map[string]struct{}) |
| 436 | for k := range run1Tools { |
| 437 | allKeys[k] = struct{}{} |
| 438 | } |
| 439 | for k := range run2Tools { |
| 440 | allKeys[k] = struct{}{} |
| 441 | } |
| 442 | |
| 443 | sortedKeys := sliceutil.SortedKeys(allKeys) |
| 444 | |
| 445 | diff := &MCPToolsDiff{} |
| 446 | anomalyCount := 0 |
| 447 | |
| 448 | for _, key := range sortedKeys { |
| 449 | s1, inRun1 := run1Tools[key] |
| 450 | s2, inRun2 := run2Tools[key] |
| 451 | |
| 452 | if !inRun1 && inRun2 { |
| 453 | entry := MCPToolDiffEntry{ |
| 454 | ServerName: s2.ServerName, |
| 455 | ToolName: s2.ToolName, |
| 456 | Status: "new", |
| 457 | Run2CallCount: s2.CallCount, |
| 458 | Run2ErrorCount: s2.ErrorCount, |
| 459 | } |
| 460 | if s2.ErrorCount > 0 { |
| 461 | entry.IsAnomaly = true |
| 462 | entry.AnomalyNote = "new tool with errors" |
| 463 | anomalyCount++ |
| 464 | } |
| 465 | diff.NewTools = append(diff.NewTools, entry) |
| 466 | } else if inRun1 && !inRun2 { |
| 467 | diff.RemovedTools = append(diff.RemovedTools, MCPToolDiffEntry{ |
| 468 | ServerName: s1.ServerName, |
| 469 | ToolName: s1.ToolName, |