feat(mcp): warn on native_viz_types collision at plugin registration

If two plugins claim the same viz_type, display_name_for_viz_type()
silently resolves to the iteration-order winner. Surface a warning at
register() time so plugin authors catch the shadowing immediately.
This commit is contained in:
Amin Ghadersohi
2026-06-10 23:16:36 +00:00
parent a005a6be14
commit 3ebcf86d1c
2 changed files with 38 additions and 0 deletions

View File

@@ -160,6 +160,21 @@ def register(plugin: "ChartTypePlugin") -> None:
logger.warning(
"Overwriting existing plugin for chart_type=%r", plugin.chart_type
)
for existing in _REGISTRY.values():
if existing.chart_type == plugin.chart_type:
continue
colliding = plugin.native_viz_types.keys() & existing.native_viz_types
if colliding:
# display_name_for_viz_type() resolves to the first plugin in
# iteration order, making the later registration unreachable.
logger.warning(
"Plugin %r declares native_viz_types %s already claimed by "
"plugin %r; viz_type display-name lookups will resolve to "
"the earlier registration",
plugin.chart_type,
sorted(colliding),
existing.chart_type,
)
_REGISTRY[plugin.chart_type] = plugin
logger.debug("Registered chart plugin: %r", plugin.chart_type)

View File

@@ -98,6 +98,29 @@ def test_register_warns_on_duplicate(caplog):
assert "Overwriting" in caplog.text
def test_register_warns_on_viz_type_collision(caplog):
register(_FakePlugin())
class _CollidingPlugin(BaseChartPlugin):
chart_type = "colliding"
display_name = "Colliding Chart"
native_viz_types = {"fake_viz": "Shadowed Viz", "own_viz": "Own Viz"}
with caplog.at_level("WARNING"):
register(_CollidingPlugin())
assert "already claimed by" in caplog.text
assert "fake_viz" in caplog.text
# Earlier registration wins in display-name lookups
assert display_name_for_viz_type("fake_viz") == "Fake Viz"
def test_register_same_plugin_no_collision_warning(caplog):
register(_FakePlugin())
with caplog.at_level("WARNING"):
register(_FakePlugin())
assert "already claimed by" not in caplog.text
def test_register_raises_for_empty_chart_type():
class _BadPlugin(BaseChartPlugin):
chart_type = ""