Skip to content

Aggregator does not resolve conflicts for resources, templates or prompts #6060

Description

@JAORMX

Summary

pkg/vmcp/aggregator/default_aggregator.go resolves tool name conflicts through a map keyed by name (ResolveToolConflictsmap[string]*ResolvedTool), so duplicate tool names across backends are handled. Resources, resource templates and prompts are a plain append across backends, under a comment that says so:

// Collect resources, resource templates, and prompts (no conflict resolution for these yet)

Nothing downstream deduplicates them — FilterResources only filters. So two backends exposing the same Resource.URI, ResourceTemplate.URITemplate or Prompt.Name produce a list containing both, with no resolution and no diagnostic.

This is ordinary, not pathological. Two instances of the same server type, or any two backends both exposing file:///README.md, hit it.

Why it's being filed now

It was the root cause of a data-loss defect in Modern list pagination, found during review of #6050. That code assumed the aggregator had already made keys unique — its comment cited this exact resolver — and used the key as a cursor position. With a duplicate at a page boundary, every item sharing the boundary key was permanently dropped: measured 1100 items in, 1097 delivered, scaling with the size of the duplicate run and reaching a full page when all keys collide.

The paginator has since been made collision-safe (it carries (LastKey, Delivered) and resumes inside a run of equal keys), so pagination is fixed and this issue is not a regression risk there. But the underlying non-uniqueness remains, and it is a trap for anything else that treats these fields as identities.

There is at least one other consumer already relying on it: MergeCapabilities keys routing as map[string]*vmcp.BackendTarget by URI, so duplicate resource URIs already collapse to one nondeterministically-chosen backend for reads. A client asking for a duplicated URI gets whichever backend won the map write, and which one that is may vary between runs.

Options

  1. Resolve conflicts for the other three types, mirroring the tool path. Needs a decision on the policy — prefix with the backend name as tools do, prefer a priority order, or drop on collision as composite tools do — and on whether a duplicate URI should be surfaced to clients as one entry or two.
  2. Leave the lists unresolved but make it explicit: document the property at the aggregation boundary and require consumers to handle collisions. This is closer to today's behaviour, and is what Complete Modern client-facing dispatch: listen + pagination #6050 now does locally.

Option 1 is the more correct fix but it changes what clients see on both revisions, including Legacy, so it wants deliberate scoping rather than being folded into unrelated work.

Whichever way it goes, MergeCapabilities' nondeterministic routing collapse should be addressed — silently picking a backend is worse than either resolving or erroring.

Notes

docs/arch/10-virtual-mcp-architecture.md now documents the tools-only uniqueness property in its Conflict Resolution section, added in #6050, so the trap is at least written down.

Refs #6050.

Metadata

Metadata

Assignees

No one assigned

    Labels

    needs-triageIssue needs initial triage by a maintainer

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions