Add MCP presets, admin connector policies and connector attribution

Six vendor-run MCP servers (Notion, Linear, Atlassian, Sentry, Asana,
Stripe) join the catalog as presets from docsgpt/connectors/presets/
mcp.yaml; existing connections to those servers show under them.

connector_policies lets an admin turn a connector off or force whose
account every share uses, and app_metadata's
connectors.allow_custom_mcp turns custom MCP servers off. Both are
enforced on the server: new connections, OAuth sign-ins, MCP test and
save, and the tools of a disabled connector stop resolving.
GET/PUT /api/admin/connectors reads and changes them.

A shared tool's owner picks owner or member credentials with
PUT /api/connections/tools/<id>/credential-mode, and a member running
the owner's account always confirms write actions.

Tool-call events and retrieved chunks name the connector they came
from (key and display name, never the account), and tool calls keep
those fields when the conversation is reloaded.
This commit is contained in:
arc53-machine committed 2026-09-28 17:30:53 +01:00
1 parent 6e2ee43944
commit b692eaf7ad
20 files changed
+844 -4

No files matched your search

+19
View File
@@ -932,6 +932,14 @@ class ToolExecutor:
or require_approval
)
# A member running someone else's account (a shared tool in owner
# mode) always confirms write actions, whatever the owner chose for
# themselves.
if not require_approval and resolved is not None and resolved.delegated:
from docsgpt.connectors.permissions import ACCESS_WRITE, action_access
require_approval = action_access(tool_data.get("name"), action_data) == ACCESS_WRITE
if require_approval:
if self.headless:
tool_row_id = str(tool_data.get("id") or tool_id)
@@ -1286,6 +1294,12 @@ class ToolExecutor:
"arguments": call_args,
}
tool_data = tools_dict[tool_id]
# Name the service a connection-backed tool used, so the chip can show
# its logo ("Searched Notion"). Never the account behind it.
resolved = self._resolve_connection(tool_data)
if resolved is not None and resolved.connector_key:
tool_call_data["connector_key"] = resolved.connector_key
tool_call_data["connector_name"] = resolved.connector_name
# Surface the device id on remote_device tool-call events so the
# approval UI can wire up the sticky "don't ask again" button.
if tool_data.get("name") == "remote_device":
@@ -1331,6 +1345,11 @@ class ToolExecutor:
else next(action for action in tool_data["actions"] if action["name"] == action_name)
)
if "connector_key" in tool_call_data:
from docsgpt.connectors.permissions import action_access
tool_call_data["access"] = action_access(tool_data.get("name"), action_data)
query_params, headers, body, parameters = {}, {}, {}, {}
param_types = {
"query_params": query_params,