fix(mcp): let a provider that states nothing admit a declared client

utils/roles/applications/config.py turns a missing key with a None default into False, so the provider-side probe in attachments.py read an omitted mcp_consumer flag as an explicit refusal. No MCP client ever joined a provider's shared_net: open-webui rendered no gitlab network, http://gitlabmcp:8080/mcp did not resolve from its namespace, and Open WebUI's verify call answered 400 in both GitLab#1 jobs of run 33698844864, clearnet and tor alike. Measured on that run's own inventory: without the change open-webui does not join the gitlab overlay, with it it does, and a sentinel probe shows the provider carries no mcp_consumer key at all.

utils/roles/applications/mcp.py:295 already states the rule this restores, that an omitted flag inherits the client's own declaration and only an explicit false excludes.

The stub in the render tests answered an unset path with the caller's default, which is why the regression passed them; it now coerces the way the accessor does, and the new case fails without the fix.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
kevinveenbirkenbachandClaude Opus 5 committed 2026-09-03 11:25:48 +02:00
1 parent ae880da5c9
commit c8a6b0cf77
2 files changed
+32 -4

No files matched your search

@@ -43,13 +43,18 @@ def _role_lookup_config(per_role, **values):
Args:
per_role: ``{role: {path: value}}`` consulted before the shared map.
values: paths every role answers identically.
An unset path is answered the way utils.roles.applications.config.get does
it, which turns a ``None`` default into ``False``.
"""
def _lookup(app, path, default):
scoped = per_role.get(app) or {}
if path in scoped:
return scoped[path]
return values.get(path, default)
if path in values:
return values[path]
return default if default is not None else False
return _lookup
@@ -152,6 +157,29 @@ class TestMcpClientConsumer(unittest.TestCase):
"being a client is not an admission; the self-declaration decides",
)
def test_a_provider_that_states_nothing_admits_a_declared_client(self):
"""Only an explicit false refuses; an unset flag inherits the client's
own declaration, and the accessor answers an unset path with false."""
entry = {
"role": "web-app-gitlab",
"overlay": {"consumer": {"kind": "mcp_client"}},
}
self.assertTrue(
_is_consumer(
entry,
"web-app-openwebui",
_role_lookup_config(
{"web-app-openwebui": {"services.openwebui.mcp_consumer": True}},
**{
"mcp.enabled": True,
"mcp.shared": True,
"mcp.direction": "client",
},
),
_const_lookup_database(),
)
)
def test_a_provider_override_refuses_a_declared_client(self):
entry = {
"role": "web-app-homeassistant",