add Slack and Airtable MCP OAuth support

This commit is contained in:
CREDO23 2026-04-21 21:38:24 +02:00
parent c414cc257f
commit 8b8c9b1f5d
4 changed files with 47 additions and 6 deletions

View file

@ -106,7 +106,9 @@ async def connect_mcp_service(
register_client,
)
metadata = await discover_oauth_metadata(svc.mcp_url)
metadata = await discover_oauth_metadata(
svc.mcp_url, origin_override=svc.oauth_discovery_origin,
)
auth_endpoint = metadata.get("authorization_endpoint")
token_endpoint = metadata.get("token_endpoint")
registration_endpoint = metadata.get("registration_endpoint")
@ -409,7 +411,9 @@ async def reauth_mcp_service(
register_client,
)
metadata = await discover_oauth_metadata(svc.mcp_url)
metadata = await discover_oauth_metadata(
svc.mcp_url, origin_override=svc.oauth_discovery_origin,
)
auth_endpoint = metadata.get("authorization_endpoint")
token_endpoint = metadata.get("token_endpoint")
registration_endpoint = metadata.get("registration_endpoint")

View file

@ -11,14 +11,24 @@ import httpx
logger = logging.getLogger(__name__)
async def discover_oauth_metadata(mcp_url: str, *, timeout: float = 15.0) -> dict:
async def discover_oauth_metadata(
mcp_url: str,
*,
origin_override: str | None = None,
timeout: float = 15.0,
) -> dict:
"""Fetch OAuth 2.1 metadata from the MCP server's well-known endpoint.
Per the MCP spec the discovery document lives at the *origin* of the
MCP server URL, not at the MCP endpoint path.
MCP server URL. ``origin_override`` can be used when the OAuth server
lives on a different domain (e.g. Airtable: MCP at ``mcp.airtable.com``,
OAuth at ``airtable.com``).
"""
parsed = urlparse(mcp_url)
origin = f"{parsed.scheme}://{parsed.netloc}"
if origin_override:
origin = origin_override.rstrip("/")
else:
parsed = urlparse(mcp_url)
origin = f"{parsed.scheme}://{parsed.netloc}"
discovery_url = f"{origin}/.well-known/oauth-authorization-server"
async with httpx.AsyncClient(follow_redirects=True) as client:

View file

@ -16,6 +16,7 @@ class MCPServiceConfig:
name: str
mcp_url: str
supports_dcr: bool = True
oauth_discovery_origin: str | None = None
client_id_env: str | None = None
client_secret_env: str | None = None
scopes: list[str] = field(default_factory=list)
@ -34,6 +35,18 @@ MCP_SERVICES: dict[str, MCPServiceConfig] = {
name="ClickUp",
mcp_url="https://mcp.clickup.com/mcp",
),
"slack": MCPServiceConfig(
name="Slack",
mcp_url="https://mcp.slack.com/mcp",
supports_dcr=False,
client_id_env="SLACK_CLIENT_ID",
client_secret_env="SLACK_CLIENT_SECRET",
),
"airtable": MCPServiceConfig(
name="Airtable",
mcp_url="https://mcp.airtable.com/mcp",
oauth_discovery_origin="https://airtable.com",
),
}

View file

@ -128,6 +128,20 @@ export const MCP_OAUTH_CONNECTORS = [
connectorType: EnumConnectorName.MCP_CONNECTOR,
authEndpoint: "/api/v1/auth/mcp/clickup/connector/add/",
},
{
id: "slack-mcp-connector",
title: "Slack (MCP)",
description: "Interact with Slack channels via MCP",
connectorType: EnumConnectorName.MCP_CONNECTOR,
authEndpoint: "/api/v1/auth/mcp/slack/connector/add/",
},
{
id: "airtable-mcp-connector",
title: "Airtable (MCP)",
description: "Interact with Airtable bases via MCP",
connectorType: EnumConnectorName.MCP_CONNECTOR,
authEndpoint: "/api/v1/auth/mcp/airtable/connector/add/",
},
] as const;
// Content Sources (tools that extract and import content from external sources)