mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-06-02 19:55:18 +02:00
add account metadata discovery and connected_accounts tool
This commit is contained in:
parent
a4bc621c2a
commit
9eb54bc4af
3 changed files with 261 additions and 11 deletions
|
|
@ -4,6 +4,12 @@ Each entry maps a URL-safe service key to its MCP server endpoint and
|
|||
authentication configuration. Services with ``supports_dcr=True`` use
|
||||
RFC 7591 Dynamic Client Registration (the MCP server issues its own
|
||||
credentials); the rest use pre-configured credentials via env vars.
|
||||
|
||||
``allowed_tools`` whitelists which MCP tools to expose to the agent.
|
||||
An empty list means "load every tool the server advertises" (used for
|
||||
user-managed generic MCP servers). Service-specific entries should
|
||||
curate this list to keep the agent's tool count low and selection
|
||||
accuracy high.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
|
@ -24,6 +30,17 @@ class MCPServiceConfig:
|
|||
scope_param: str = "scope"
|
||||
auth_endpoint_override: str | None = None
|
||||
token_endpoint_override: str | None = None
|
||||
allowed_tools: list[str] = field(default_factory=list)
|
||||
readonly_tools: frozenset[str] = field(default_factory=frozenset)
|
||||
account_metadata_keys: list[str] = field(default_factory=list)
|
||||
"""``connector.config`` keys exposed by ``get_connected_accounts``.
|
||||
|
||||
Only listed keys are returned to the LLM — tokens and secrets are
|
||||
never included. Every service should at least have its
|
||||
``display_name`` populated during OAuth; additional service-specific
|
||||
fields (e.g. Jira ``cloud_id``) are listed here so the LLM can pass
|
||||
them to action tools.
|
||||
"""
|
||||
|
||||
|
||||
MCP_SERVICES: dict[str, MCPServiceConfig] = {
|
||||
|
|
@ -31,16 +48,44 @@ MCP_SERVICES: dict[str, MCPServiceConfig] = {
|
|||
name="Linear",
|
||||
mcp_url="https://mcp.linear.app/mcp",
|
||||
connector_type="LINEAR_CONNECTOR",
|
||||
allowed_tools=[
|
||||
"list_issues",
|
||||
"get_issue",
|
||||
"save_issue",
|
||||
],
|
||||
readonly_tools=frozenset({"list_issues", "get_issue"}),
|
||||
account_metadata_keys=["organization_name", "organization_url_key"],
|
||||
),
|
||||
"jira": MCPServiceConfig(
|
||||
name="Jira",
|
||||
mcp_url="https://mcp.atlassian.com/v1/mcp",
|
||||
connector_type="JIRA_CONNECTOR",
|
||||
allowed_tools=[
|
||||
"getAccessibleAtlassianResources",
|
||||
"searchJiraIssuesUsingJql",
|
||||
"getVisibleJiraProjects",
|
||||
"getJiraProjectIssueTypesMetadata",
|
||||
"createJiraIssue",
|
||||
"editJiraIssue",
|
||||
],
|
||||
readonly_tools=frozenset({
|
||||
"getAccessibleAtlassianResources",
|
||||
"searchJiraIssuesUsingJql",
|
||||
"getVisibleJiraProjects",
|
||||
"getJiraProjectIssueTypesMetadata",
|
||||
}),
|
||||
account_metadata_keys=["cloud_id", "site_name", "base_url"],
|
||||
),
|
||||
"clickup": MCPServiceConfig(
|
||||
name="ClickUp",
|
||||
mcp_url="https://mcp.clickup.com/mcp",
|
||||
connector_type="CLICKUP_CONNECTOR",
|
||||
allowed_tools=[
|
||||
"clickup_search",
|
||||
"clickup_get_task",
|
||||
],
|
||||
readonly_tools=frozenset({"clickup_search", "clickup_get_task"}),
|
||||
account_metadata_keys=["workspace_id", "workspace_name"],
|
||||
),
|
||||
"slack": MCPServiceConfig(
|
||||
name="Slack",
|
||||
|
|
@ -49,17 +94,22 @@ MCP_SERVICES: dict[str, MCPServiceConfig] = {
|
|||
supports_dcr=False,
|
||||
client_id_env="SLACK_CLIENT_ID",
|
||||
client_secret_env="SLACK_CLIENT_SECRET",
|
||||
scope_param="user_scope",
|
||||
auth_endpoint_override="https://slack.com/oauth/v2/authorize",
|
||||
token_endpoint_override="https://slack.com/api/oauth.v2.access",
|
||||
auth_endpoint_override="https://slack.com/oauth/v2_user/authorize",
|
||||
token_endpoint_override="https://slack.com/api/oauth.v2.user.access",
|
||||
scopes=[
|
||||
"search:read.public", "search:read.private", "search:read.mpim",
|
||||
"search:read.im", "search:read.files", "search:read.users",
|
||||
"chat:write",
|
||||
"search:read.public", "search:read.private", "search:read.mpim", "search:read.im",
|
||||
"channels:history", "groups:history", "mpim:history", "im:history",
|
||||
"canvases:read", "canvases:write",
|
||||
"users:read", "users:read.email",
|
||||
],
|
||||
allowed_tools=[
|
||||
"slack_search_channels",
|
||||
"slack_read_channel",
|
||||
"slack_read_thread",
|
||||
],
|
||||
readonly_tools=frozenset({"slack_search_channels", "slack_read_channel", "slack_read_thread"}),
|
||||
# TODO: oauth.v2.user.access only returns team.id, not team.name.
|
||||
# To populate team_name, either add "team:read" scope and call
|
||||
# GET /api/team.info during OAuth callback, or switch to oauth.v2.access.
|
||||
account_metadata_keys=["team_id", "team_name"],
|
||||
),
|
||||
"airtable": MCPServiceConfig(
|
||||
name="Airtable",
|
||||
|
|
@ -69,10 +119,26 @@ MCP_SERVICES: dict[str, MCPServiceConfig] = {
|
|||
oauth_discovery_origin="https://airtable.com",
|
||||
client_id_env="AIRTABLE_CLIENT_ID",
|
||||
client_secret_env="AIRTABLE_CLIENT_SECRET",
|
||||
scopes=["data.records:read", "data.records:write", "schema.bases:read", "schema.bases:write"],
|
||||
scopes=["data.records:read", "schema.bases:read"],
|
||||
allowed_tools=[
|
||||
"list_bases",
|
||||
"list_tables_for_base",
|
||||
"list_records_for_table",
|
||||
],
|
||||
readonly_tools=frozenset({"list_bases", "list_tables_for_base", "list_records_for_table"}),
|
||||
account_metadata_keys=["user_id", "user_email"],
|
||||
),
|
||||
}
|
||||
|
||||
_CONNECTOR_TYPE_TO_SERVICE: dict[str, MCPServiceConfig] = {
|
||||
svc.connector_type: svc for svc in MCP_SERVICES.values()
|
||||
}
|
||||
|
||||
|
||||
def get_service(key: str) -> MCPServiceConfig | None:
|
||||
return MCP_SERVICES.get(key)
|
||||
|
||||
|
||||
def get_service_by_connector_type(connector_type: str) -> MCPServiceConfig | None:
|
||||
"""Look up an MCP service config by its ``connector_type`` enum value."""
|
||||
return _CONNECTOR_TYPE_TO_SERVICE.get(connector_type)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue