Commit 7463182
Implement tool conflict resolution for Virtual MCP Server (#2365)
* Implement tool conflict resolution for Virtual MCP Server
Add three conflict resolution strategies (prefix, priority, manual) to handle
duplicate tool names across multiple backend MCP servers in vMCP aggregation.
This implements the aggregation conflict resolution portion of the Virtual MCP
Server proposal (THV-2106), enabling vMCP to merge capabilities from multiple
backends while resolving naming conflicts.
Key features:
- Prefix strategy: automatically prefixes tools with workload identifier
(supports {workload}_, {workload}., custom formats)
- Priority strategy: explicit ordering with first-wins semantics
(drops lower-priority conflicting tools with warnings)
- Manual strategy: requires explicit overrides with startup validation
(fails if any conflicts lack overrides, safest for production)
- Reuses existing mcp.WithToolsFilter/Override middleware logic
- Per-backend tool filtering and overrides applied before conflict resolution
- Tracks conflict metadata (count resolved, strategy used)
Implementation:
- Extracted shared filtering/override logic from pkg/mcp/tool_filter.go
- Created applyFilteringAndOverrides() as single source of truth
- Both HTTP middleware and aggregator use the same battle-tested code
- Updated defaultAggregator to integrate conflict resolver
- Comprehensive table-driven unit tests for all strategies
This follows DDD principles with clear bounded contexts and maintains
backward compatibility with existing middleware behavior.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* Address review comments on tool conflict resolution
- Add nil check in tool_adapter.go to prevent panic when tool not found in originalToolsByName map
- Add comment explaining O(n²) complexity is acceptable for small tool lists
- Improve warning message when priority strategy drops tools from backends not in priority list
- Show which backends were involved when dropping conflicting tools
Co-authored-by: Juan Antonio Osorio <JAORMX@users.noreply.github.com>
* Add clarifying comments for conflict resolution logic
Address review feedback by adding detailed comments explaining:
1. What "resolved conflict" means in the context of conflict resolution:
- Prefix strategy: Renaming tools proactively prevents collisions
- Manual strategy: Explicit overrides resolve existing conflicts
- Priority strategy: Drops duplicates rather than renaming
2. Manual resolver validation: Documents that collision detection after
override prevents new conflicts from being introduced by bad config
These clarifications help reviewers understand that renaming IS conflict
resolution, not just a side effect.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* Remove ConflictsResolved counter from aggregation metadata
Remove the ConflictsResolved counter as it was not defined in the proposal
and caused confusion about what constitutes a "resolved conflict" vs a
preventive renaming.
The ConflictStrategy field remains to indicate which resolution approach
was used (prefix, priority, manual), which is sufficient for observability.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* Refactor conflict resolvers into separate files
Split conflict_resolver.go into focused files for better code organization:
- prefix_resolver.go: PrefixConflictResolver implementation
- priority_resolver.go: PriorityConflictResolver implementation
- manual_resolver.go: ManualConflictResolver implementation
- conflict_resolver.go: Factory function and shared helpers
Each strategy now has its own file, making the code easier to navigate
and maintain. Shared helpers (groupToolsByName, toolWithBackend) remain
in conflict_resolver.go for reuse.
No functional changes - pure refactoring.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* Use prefix strategy as fallback in priority resolver
Change priority resolver to use prefix strategy for backends not in the
priority list instead of dropping their conflicting tools. This prevents
data loss while maintaining explicit control for prioritized backends.
Behavior:
- Backends in priority list: priority strategy (first wins)
- Backends NOT in priority list with conflicts: prefix strategy fallback
- Example: priority_order=["github"], but slack+teams both have "send_message"
Result: "slack_send_message" and "teams_send_message" (both included)
This addresses review feedback about dropping tools unnecessarily and
provides a more practical default behavior.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: Juan Antonio Osorio <JAORMX@users.noreply.github.com>1 parent 7644d08 commit 7463182
File tree
11 files changed
+1400
-71
lines changed- pkg
- mcp
- vmcp/aggregator
11 files changed
+1400
-71
lines changed| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
72 | 72 | | |
73 | 73 | | |
74 | 74 | | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
75 | 105 | | |
76 | 106 | | |
77 | 107 | | |
| |||
448 | 478 | | |
449 | 479 | | |
450 | 480 | | |
451 | | - | |
| 481 | + | |
| 482 | + | |
| 483 | + | |
| 484 | + | |
452 | 485 | | |
453 | 486 | | |
454 | 487 | | |
| |||
461 | 494 | | |
462 | 495 | | |
463 | 496 | | |
| 497 | + | |
| 498 | + | |
| 499 | + | |
| 500 | + | |
| 501 | + | |
| 502 | + | |
| 503 | + | |
| 504 | + | |
| 505 | + | |
| 506 | + | |
| 507 | + | |
| 508 | + | |
| 509 | + | |
| 510 | + | |
| 511 | + | |
| 512 | + | |
| 513 | + | |
| 514 | + | |
| 515 | + | |
| 516 | + | |
| 517 | + | |
| 518 | + | |
| 519 | + | |
| 520 | + | |
| 521 | + | |
| 522 | + | |
| 523 | + | |
| 524 | + | |
| 525 | + | |
| 526 | + | |
| 527 | + | |
| 528 | + | |
| 529 | + | |
| 530 | + | |
| 531 | + | |
| 532 | + | |
| 533 | + | |
| 534 | + | |
| 535 | + | |
| 536 | + | |
| 537 | + | |
| 538 | + | |
| 539 | + | |
| 540 | + | |
| 541 | + | |
| 542 | + | |
| 543 | + | |
| 544 | + | |
| 545 | + | |
| 546 | + | |
| 547 | + | |
| 548 | + | |
| 549 | + | |
464 | 550 | | |
465 | | - | |
| 551 | + | |
466 | 552 | | |
467 | | - | |
| 553 | + | |
468 | 554 | | |
469 | 555 | | |
470 | | - | |
| 556 | + | |
471 | 557 | | |
472 | | - | |
473 | 558 | | |
474 | 559 | | |
475 | 560 | | |
476 | | - | |
477 | | - | |
478 | | - | |
479 | | - | |
| 561 | + | |
| 562 | + | |
| 563 | + | |
| 564 | + | |
| 565 | + | |
| 566 | + | |
480 | 567 | | |
481 | 568 | | |
| 569 | + | |
| 570 | + | |
482 | 571 | | |
483 | | - | |
484 | | - | |
485 | | - | |
| 572 | + | |
| 573 | + | |
| 574 | + | |
| 575 | + | |
| 576 | + | |
| 577 | + | |
| 578 | + | |
486 | 579 | | |
487 | | - | |
488 | | - | |
| 580 | + | |
489 | 581 | | |
490 | 582 | | |
491 | 583 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
160 | 160 | | |
161 | 161 | | |
162 | 162 | | |
163 | | - | |
164 | | - | |
165 | | - | |
166 | 163 | | |
167 | 164 | | |
168 | 165 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
0 commit comments