Validate tool edits before touching the connection; scope kept MCP connections

- Editing a connection-backed tool validates the config before writing
  new credentials, so a rejected edit no longer rotates the connection's
  key and resumes its sources.
- An MCP edit keeps its previous connection only when the caller owns it
  and it signs in the way the tool now does.
This commit is contained in:
arc53-machine committed 2026-09-28 19:31:02 +01:00
1 parent 4d533d24d6
commit 473e8ec15c
4 files changed
+41 -20

No files matched your search

+8 -4
View File
@@ -115,12 +115,13 @@ def _mcp_connection(user, config, auth_type, auth_credentials, display_name):
return str(row["id"]) if row else None
def _previous_connection(existing_doc, config) -> str | None:
def _previous_connection(existing_doc, config, user) -> str | None:
"""The saved tool's connection, kept only while the server is unchanged.
An edit that cannot resolve a connection of its own (the default
encryption key blocks storing a new secret) must not carry the old
one over to a different server: its key would be sent there.
one over to a different server (its key would be sent there), to a
connection the caller does not own, or to one that signs in another way.
"""
from docsgpt.connectors import catalog
@@ -129,9 +130,12 @@ def _previous_connection(existing_doc, config) -> str | None:
if not connection_id or not base:
return None
with db_readonly() as conn:
row = ConnectorSessionsRepository(conn).get(str(connection_id))
row = ConnectorSessionsRepository(conn).get_for_user(str(connection_id), user)
if row is None or catalog.base_url(row.get("server_url")) != base:
return None
wanted = {"oauth": "mcp_oauth", "none": "none"}.get(config.get("auth_type") or "none", "api_key")
if (row.get("auth_kind") or "") != wanted:
return None
return str(connection_id)
@@ -383,7 +387,7 @@ class MCPServerSave(Resource):
display_name = data["displayName"]
connection_id = _mcp_connection(
user, storage_config, auth_type, auth_credentials, display_name,
) or _previous_connection(existing_doc, storage_config)
) or _previous_connection(existing_doc, storage_config, user)
if connection_id and auth_type != "oauth":
# The secret lives on the connection only.
storage_config.pop("encrypted_credentials", None)