Add the connector catalog and read-only connections API

connector_sessions already holds one row per signed-in account or MCP
server. Migration 0038 names each row (connector_key, auth_kind,
display_name, account_label) and lets sources and tools point at the
row they use through connection_id, backfilling the links it can prove.

docsgpt/connectors/catalog.py declares every service DocsGPT connects
to, with the server settings it needs and what a connection provides.
New endpoints: GET /api/connectors/catalog, GET /api/connections,
GET /api/connections/<id> and POST /api/connections/<id>/disconnect.
/api/available_tools now groups tools into built_in, service and
custom, and source listings carry connectionId.
This commit is contained in:
arc53-machine committed 2026-09-28 16:30:48 +01:00
1 parent 335f2af70c
commit 5321a94388
17 files changed
+1714 -2

No files matched your search

+8
View File
@@ -67,6 +67,12 @@ def _get_provider_from_remote_data(remote_data):
return None
def _connection_id(row: dict) -> str | None:
"""The connection a source syncs from, as a string id, or None."""
value = row.get("connection_id")
return str(value) if value else None
@sources_ns.route("/sources")
class CombinedJson(Resource):
@api.doc(description="Provide JSON file with combined available indexes")
@@ -117,6 +123,7 @@ class CombinedJson(Resource):
"config": SourceConfig.parse(index.get("config")).model_dump(),
"ownership": ownership,
"team_access": team_access,
"connectionId": _connection_id(index),
}
for index in indexes:
@@ -207,6 +214,7 @@ class PaginatedSources(Resource):
"team_access": (
None if owned else team_shared.get(str(doc["id"]))
),
"connectionId": _connection_id(doc),
}
)
response = {
+14
View File
@@ -19,6 +19,7 @@ from docsgpt.api import api
from docsgpt.api.pat.rules import filter_listing
from docsgpt.api.user.artifacts.authz import Principal, authorize_artifact
from docsgpt.api.user.team_sharing import effective_write_owner, visible_with_access
from docsgpt.connectors.catalog import definition_for_tool
from docsgpt.core.settings import settings
from docsgpt.core.url_validation import SSRFError, validate_url
from docsgpt.security.encryption import decrypt_credentials, encrypt_credentials
@@ -186,6 +187,10 @@ def transform_actions(actions_metadata):
tools_ns = Namespace("tools", description="Tool management operations", path="/api")
# Tools the Connectors page adds through "Add custom connector" rather than
# the Add Tool modal.
_CUSTOM_CONNECTOR_TOOLS = {"mcp_tool": "custom_mcp", "api_tool": "custom_openapi"}
@tools_ns.route("/available_tools")
class AvailableTools(Resource):
@@ -202,6 +207,13 @@ class AvailableTools(Resource):
description = lines[1].strip() if len(lines) > 1 else ""
config_req = tool_instance.get_config_requirements()
actions = tool_instance.get_actions_metadata()
definition = definition_for_tool(tool_name)
if definition is not None:
group, connector_key = "service", definition.key
elif tool_name in _CUSTOM_CONNECTOR_TOOLS:
group, connector_key = "custom", _CUSTOM_CONNECTOR_TOOLS[tool_name]
else:
group, connector_key = "built_in", None
tools_metadata.append(
{
"name": tool_name,
@@ -209,6 +221,8 @@ class AvailableTools(Resource):
"description": description,
"configRequirements": config_req,
"actions": actions,
"group": group,
"connector_key": connector_key,
}
)
except Exception as err: