mirror of
https://github.com/tiennm99/DocsGPT.git
synced 2026-10-17 06:14:25 +00:00
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:
1 parent
6e2ee43944
commit
b692eaf7ad
20 files changed
+844
-4
No files matched your search
@@ -112,6 +112,31 @@ def _mcp_connection(user, config, auth_type, auth_credentials, display_name):
|
||||
return str(row["id"]) if row else None
|
||||
|
||||
|
||||
def _mcp_policy_error(config: dict):
|
||||
"""A 403 when an admin turned this MCP server's connector off, else None.
|
||||
|
||||
A preset's own switch applies to its server; any other server is a
|
||||
custom connector and needs "Allow custom MCP servers".
|
||||
"""
|
||||
from docsgpt.connectors import catalog, service
|
||||
|
||||
preset = catalog.preset_for_url(config.get("server_url"))
|
||||
key = preset.key if preset else "custom_mcp"
|
||||
try:
|
||||
with db_readonly() as conn:
|
||||
service.ensure_connector_allowed(conn, key)
|
||||
except service.ConnectorDisabled:
|
||||
return make_response(
|
||||
jsonify({"success": False, "error": "This MCP server is turned off by an admin", "code": "disabled"}),
|
||||
403,
|
||||
)
|
||||
except Exception:
|
||||
# The switch is an admin preference; when the policy table cannot be
|
||||
# read, saving the tool (which needs the database) fails on its own.
|
||||
current_app.logger.warning("Could not read connector policies", exc_info=True)
|
||||
return None
|
||||
|
||||
|
||||
@tools_mcp_ns.route("/mcp_server/test")
|
||||
class TestMCPServerConfig(Resource):
|
||||
@api.expect(
|
||||
@@ -147,6 +172,9 @@ class TestMCPServerConfig(Resource):
|
||||
)
|
||||
|
||||
_validate_mcp_server_url(config)
|
||||
policy_error = _mcp_policy_error(config)
|
||||
if policy_error is not None:
|
||||
return policy_error
|
||||
|
||||
auth_credentials = _extract_auth_credentials(config)
|
||||
test_config = config.copy()
|
||||
@@ -243,6 +271,9 @@ class MCPServerSave(Resource):
|
||||
)
|
||||
|
||||
_validate_mcp_server_url(config)
|
||||
policy_error = _mcp_policy_error(config)
|
||||
if policy_error is not None:
|
||||
return policy_error
|
||||
|
||||
auth_credentials = _extract_auth_credentials(config)
|
||||
auth_type = config.get("auth_type", "none")
|
||||
|
||||
@@ -470,6 +470,8 @@ def _create_connected_tool(user, data, definition, tool_instance):
|
||||
)
|
||||
except connection_service.EncryptionKeyNotConfigured:
|
||||
return None
|
||||
except connection_service.ConnectorDisabled as err:
|
||||
return make_response(jsonify({"success": False, "message": str(err)}), 403)
|
||||
except ValueError as err:
|
||||
return make_response(jsonify({"success": False, "message": str(err)}), 400)
|
||||
return make_response(jsonify({"id": str(created["id"]), "connection_id": str(connection["id"])}), 200)
|
||||
|
||||
Reference in new issue
Block a user