From bacab4cfe75dd307f0dec0972c6165feffcbccd5 Mon Sep 17 00:00:00 2001 From: arc53-machine <232052973+arc53-machine@users.noreply.github.com> Date: Tue, 29 Sep 2026 09:36:25 +0100 Subject: [PATCH] Check a preset's switch for a key-based connection to its server A custom MCP connection at a preset's address is reported and run as that preset, but saving it checked the custom MCP switch. Turning custom servers off now leaves a preset on a key alone, and turning the preset off stops it. --- docsgpt/connectors/service.py | 7 +++-- .../test_policies_and_attribution.py | 28 +++++++++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/docsgpt/connectors/service.py b/docsgpt/connectors/service.py index e9364f0c..1a18c0f2 100644 --- a/docsgpt/connectors/service.py +++ b/docsgpt/connectors/service.py @@ -718,11 +718,14 @@ def create_api_key_connection( Raises: ConnectorDisabled: An admin turned the connector off. Checked first, so callers that fall back to a legacy path on the other errors - never do so for a disabled connector. + never do so for a disabled connector. A custom MCP server at a + preset's address is that preset (see + :func:`catalog.connector_key_for_row`), so its switch applies. ValueError: A required credential field is missing. EncryptionKeyNotConfigured: See :func:`ensure_can_store_credentials`. """ - ensure_connector_allowed(conn, definition.key) + preset = catalog.preset_for_url(server_url) if definition.key == "custom_mcp" else None + ensure_connector_allowed(conn, preset.key if preset else definition.key) fields = {f.key: f for f in definition.credential_fields} if fields: missing = [f.label for f in fields.values() if f.required and not str(credentials.get(f.key) or "").strip()] diff --git a/tests/connectors/test_policies_and_attribution.py b/tests/connectors/test_policies_and_attribution.py index cd4cc716..8f8692a2 100644 --- a/tests/connectors/test_policies_and_attribution.py +++ b/tests/connectors/test_policies_and_attribution.py @@ -130,6 +130,34 @@ class TestAdminPolicies: pg_conn, "bob", catalog.get_definition("telegram"), {"token": "long-enough-token"}, ) + def test_a_preset_server_on_a_key_follows_the_preset_switch(self, app, pg_conn): + """A key-based connection to a preset's server is that preset, not a + custom server: turning custom servers off leaves it alone, and turning + the preset off stops it.""" + from docsgpt.api.admin.connectors import AdminConnectorsResource + + linear = catalog.get_definition("mcp:linear") + custom = catalog.get_definition("custom_mcp") + with _db(pg_conn): + _call(app, AdminConnectorsResource, "put", "/api/admin/connectors", roles=["admin"], body={ + "policies": {}, "allow_custom_mcp": False, + }) + row, _ = service.create_api_key_connection( + pg_conn, "bob", custom, {"api_key": "lin-key-123456"}, server_url=linear.mcp_base_url, + ) + assert catalog.connector_key_for_row(row) == "mcp:linear" + with pytest.raises(service.ConnectorDisabled): + service.create_api_key_connection( + pg_conn, "bob", custom, {"api_key": "other-key-123456"}, server_url="https://mcp.example.com", + ) + _call(app, AdminConnectorsResource, "put", "/api/admin/connectors", roles=["admin"], body={ + "policies": {"mcp:linear": {"enabled": False}}, "allow_custom_mcp": True, + }) + with pytest.raises(service.ConnectorDisabled): + service.create_api_key_connection( + pg_conn, "bob", custom, {"api_key": "lin-key-654321"}, server_url=linear.mcp_base_url, + ) + def test_unconfigured_connectors_are_off_until_an_admin_turns_them_on(self, app, pg_conn, monkeypatch): from docsgpt.api.admin.connectors import AdminConnectorsResource from docsgpt.core.settings import settings