mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-06-08 20:25:19 +02:00
use native connector types for MCP OAuth, restore original UI
This commit is contained in:
parent
940889c291
commit
ea3508cb25
5 changed files with 34 additions and 89 deletions
|
|
@ -530,11 +530,12 @@ async def load_mcp_tools(
|
|||
return list(cached_tools)
|
||||
|
||||
try:
|
||||
# Find all connectors with MCP server config: generic MCP_CONNECTOR type
|
||||
# and service-specific types (LINEAR_CONNECTOR, etc.) created via MCP OAuth.
|
||||
result = await session.execute(
|
||||
select(SearchSourceConnector).filter(
|
||||
SearchSourceConnector.connector_type
|
||||
== SearchSourceConnectorType.MCP_CONNECTOR,
|
||||
SearchSourceConnector.search_space_id == search_space_id,
|
||||
SearchSourceConnector.config.has_key("server_config"), # noqa: W601
|
||||
),
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -56,9 +56,7 @@ def _get_token_encryption() -> TokenEncryption:
|
|||
|
||||
|
||||
def _build_redirect_uri(service: str) -> str:
|
||||
base = config.BACKEND_URL
|
||||
if not base:
|
||||
raise HTTPException(status_code=500, detail="BACKEND_URL not configured.")
|
||||
base = config.BACKEND_URL or "http://localhost:8000"
|
||||
return f"{base.rstrip('/')}/api/v1/auth/mcp/{service}/connector/callback"
|
||||
|
||||
|
||||
|
|
@ -288,6 +286,7 @@ async def mcp_oauth_callback(
|
|||
}
|
||||
|
||||
# ---- Re-auth path ----
|
||||
db_connector_type = SearchSourceConnectorType(svc.connector_type)
|
||||
reauth_connector_id = data.get("connector_id")
|
||||
if reauth_connector_id:
|
||||
result = await session.execute(
|
||||
|
|
@ -295,8 +294,7 @@ async def mcp_oauth_callback(
|
|||
SearchSourceConnector.id == reauth_connector_id,
|
||||
SearchSourceConnector.user_id == user_id,
|
||||
SearchSourceConnector.search_space_id == space_id,
|
||||
SearchSourceConnector.connector_type
|
||||
== SearchSourceConnectorType.MCP_CONNECTOR,
|
||||
SearchSourceConnector.connector_type == db_connector_type,
|
||||
)
|
||||
)
|
||||
db_connector = result.scalars().first()
|
||||
|
|
@ -329,15 +327,15 @@ async def mcp_oauth_callback(
|
|||
# ---- New connector path ----
|
||||
connector_name = await generate_unique_connector_name(
|
||||
session,
|
||||
SearchSourceConnectorType.MCP_CONNECTOR,
|
||||
db_connector_type,
|
||||
space_id,
|
||||
user_id,
|
||||
f"{svc.name} MCP",
|
||||
svc.name,
|
||||
)
|
||||
|
||||
new_connector = SearchSourceConnector(
|
||||
name=connector_name,
|
||||
connector_type=SearchSourceConnectorType.MCP_CONNECTOR,
|
||||
connector_type=db_connector_type,
|
||||
is_indexable=False,
|
||||
config=connector_config,
|
||||
search_space_id=space_id,
|
||||
|
|
@ -388,26 +386,26 @@ async def reauth_mcp_service(
|
|||
user: User = Depends(current_active_user),
|
||||
session: AsyncSession = Depends(get_async_session),
|
||||
):
|
||||
result = await session.execute(
|
||||
select(SearchSourceConnector).filter(
|
||||
SearchSourceConnector.id == connector_id,
|
||||
SearchSourceConnector.user_id == user.id,
|
||||
SearchSourceConnector.search_space_id == space_id,
|
||||
SearchSourceConnector.connector_type
|
||||
== SearchSourceConnectorType.MCP_CONNECTOR,
|
||||
)
|
||||
)
|
||||
if not result.scalars().first():
|
||||
raise HTTPException(
|
||||
status_code=404, detail="MCP connector not found or access denied",
|
||||
)
|
||||
|
||||
from app.services.mcp_oauth.registry import get_service
|
||||
|
||||
svc = get_service(service)
|
||||
if not svc:
|
||||
raise HTTPException(status_code=404, detail=f"Unknown MCP service: {service}")
|
||||
|
||||
db_connector_type = SearchSourceConnectorType(svc.connector_type)
|
||||
result = await session.execute(
|
||||
select(SearchSourceConnector).filter(
|
||||
SearchSourceConnector.id == connector_id,
|
||||
SearchSourceConnector.user_id == user.id,
|
||||
SearchSourceConnector.search_space_id == space_id,
|
||||
SearchSourceConnector.connector_type == db_connector_type,
|
||||
)
|
||||
)
|
||||
if not result.scalars().first():
|
||||
raise HTTPException(
|
||||
status_code=404, detail="Connector not found or access denied",
|
||||
)
|
||||
|
||||
try:
|
||||
from app.services.mcp_oauth.discovery import (
|
||||
discover_oauth_metadata,
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ from dataclasses import dataclass, field
|
|||
class MCPServiceConfig:
|
||||
name: str
|
||||
mcp_url: str
|
||||
connector_type: str
|
||||
supports_dcr: bool = True
|
||||
oauth_discovery_origin: str | None = None
|
||||
client_id_env: str | None = None
|
||||
|
|
@ -26,18 +27,22 @@ MCP_SERVICES: dict[str, MCPServiceConfig] = {
|
|||
"linear": MCPServiceConfig(
|
||||
name="Linear",
|
||||
mcp_url="https://mcp.linear.app/mcp",
|
||||
connector_type="LINEAR_CONNECTOR",
|
||||
),
|
||||
"jira": MCPServiceConfig(
|
||||
name="Jira",
|
||||
mcp_url="https://mcp.atlassian.com/v1/mcp",
|
||||
connector_type="JIRA_CONNECTOR",
|
||||
),
|
||||
"clickup": MCPServiceConfig(
|
||||
name="ClickUp",
|
||||
mcp_url="https://mcp.clickup.com/mcp",
|
||||
connector_type="CLICKUP_CONNECTOR",
|
||||
),
|
||||
"slack": MCPServiceConfig(
|
||||
name="Slack",
|
||||
mcp_url="https://mcp.slack.com/mcp",
|
||||
connector_type="SLACK_CONNECTOR",
|
||||
supports_dcr=False,
|
||||
client_id_env="SLACK_CLIENT_ID",
|
||||
client_secret_env="SLACK_CLIENT_SECRET",
|
||||
|
|
@ -45,6 +50,7 @@ MCP_SERVICES: dict[str, MCPServiceConfig] = {
|
|||
"airtable": MCPServiceConfig(
|
||||
name="Airtable",
|
||||
mcp_url="https://mcp.airtable.com/mcp",
|
||||
connector_type="AIRTABLE_CONNECTOR",
|
||||
oauth_discovery_origin="https://airtable.com",
|
||||
),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ export const OAUTH_CONNECTORS = [
|
|||
title: "Airtable",
|
||||
description: "Search your Airtable bases",
|
||||
connectorType: EnumConnectorName.AIRTABLE_CONNECTOR,
|
||||
authEndpoint: "/api/v1/auth/airtable/connector/add/",
|
||||
authEndpoint: "/api/v1/auth/mcp/airtable/connector/add/",
|
||||
},
|
||||
{
|
||||
id: "notion-connector",
|
||||
|
|
@ -45,14 +45,14 @@ export const OAUTH_CONNECTORS = [
|
|||
title: "Linear",
|
||||
description: "Search issues & projects",
|
||||
connectorType: EnumConnectorName.LINEAR_CONNECTOR,
|
||||
authEndpoint: "/api/v1/auth/linear/connector/add/",
|
||||
authEndpoint: "/api/v1/auth/mcp/linear/connector/add/",
|
||||
},
|
||||
{
|
||||
id: "slack-connector",
|
||||
title: "Slack",
|
||||
description: "Search Slack messages",
|
||||
connectorType: EnumConnectorName.SLACK_CONNECTOR,
|
||||
authEndpoint: "/api/v1/auth/slack/connector/add/",
|
||||
authEndpoint: "/api/v1/auth/mcp/slack/connector/add/",
|
||||
},
|
||||
{
|
||||
id: "teams-connector",
|
||||
|
|
@ -87,7 +87,7 @@ export const OAUTH_CONNECTORS = [
|
|||
title: "Jira",
|
||||
description: "Search Jira issues",
|
||||
connectorType: EnumConnectorName.JIRA_CONNECTOR,
|
||||
authEndpoint: "/api/v1/auth/jira/connector/add/",
|
||||
authEndpoint: "/api/v1/auth/mcp/jira/connector/add/",
|
||||
},
|
||||
{
|
||||
id: "confluence-connector",
|
||||
|
|
@ -101,47 +101,8 @@ export const OAUTH_CONNECTORS = [
|
|||
title: "ClickUp",
|
||||
description: "Search ClickUp tasks",
|
||||
connectorType: EnumConnectorName.CLICKUP_CONNECTOR,
|
||||
authEndpoint: "/api/v1/auth/clickup/connector/add/",
|
||||
},
|
||||
] as const;
|
||||
|
||||
// MCP OAuth Connectors (one-click connect via official MCP servers)
|
||||
export const MCP_OAUTH_CONNECTORS = [
|
||||
{
|
||||
id: "linear-mcp-connector",
|
||||
title: "Linear (MCP)",
|
||||
description: "Interact with Linear issues via MCP",
|
||||
connectorType: EnumConnectorName.MCP_CONNECTOR,
|
||||
authEndpoint: "/api/v1/auth/mcp/linear/connector/add/",
|
||||
},
|
||||
{
|
||||
id: "jira-mcp-connector",
|
||||
title: "Jira (MCP)",
|
||||
description: "Interact with Jira issues via MCP",
|
||||
connectorType: EnumConnectorName.MCP_CONNECTOR,
|
||||
authEndpoint: "/api/v1/auth/mcp/jira/connector/add/",
|
||||
},
|
||||
{
|
||||
id: "clickup-mcp-connector",
|
||||
title: "ClickUp (MCP)",
|
||||
description: "Interact with ClickUp tasks via MCP",
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -10,14 +10,12 @@ import { ConnectorCard } from "../components/connector-card";
|
|||
import {
|
||||
COMPOSIO_CONNECTORS,
|
||||
CRAWLERS,
|
||||
MCP_OAUTH_CONNECTORS,
|
||||
OAUTH_CONNECTORS,
|
||||
OTHER_CONNECTORS,
|
||||
} from "../constants/connector-constants";
|
||||
import { getDocumentCountForConnector } from "../utils/connector-document-mapping";
|
||||
|
||||
type OAuthConnector = (typeof OAUTH_CONNECTORS)[number];
|
||||
type MCPOAuthConnector = (typeof MCP_OAUTH_CONNECTORS)[number];
|
||||
type ComposioConnector = (typeof COMPOSIO_CONNECTORS)[number];
|
||||
type OtherConnector = (typeof OTHER_CONNECTORS)[number];
|
||||
type CrawlerConnector = (typeof CRAWLERS)[number];
|
||||
|
|
@ -130,10 +128,6 @@ export const AllConnectorsTab: FC<AllConnectorsTabProps> = ({
|
|||
(c) => c.connectorType === EnumConnectorName.AIRTABLE_CONNECTOR
|
||||
);
|
||||
|
||||
const filteredMCPOAuth = MCP_OAUTH_CONNECTORS.filter(
|
||||
(c) => matchesSearch(c.title, c.description),
|
||||
);
|
||||
|
||||
const moreIntegrationsComposio = filteredComposio.filter(
|
||||
(c) =>
|
||||
!DOCUMENT_FILE_CONNECTOR_TYPES.has(c.connectorType) &&
|
||||
|
|
@ -285,7 +279,6 @@ export const AllConnectorsTab: FC<AllConnectorsTabProps> = ({
|
|||
nativeGoogleDriveConnectors.length > 0 ||
|
||||
composioGoogleDriveConnectors.length > 0 ||
|
||||
fileStorageConnectors.length > 0;
|
||||
const hasMCPOAuth = filteredMCPOAuth.length > 0;
|
||||
const hasMoreIntegrations =
|
||||
otherDocumentYouTubeConnectors.length > 0 ||
|
||||
otherDocumentNotionConnectors.length > 0 ||
|
||||
|
|
@ -295,7 +288,7 @@ export const AllConnectorsTab: FC<AllConnectorsTabProps> = ({
|
|||
moreIntegrationsOther.length > 0 ||
|
||||
moreIntegrationsCrawlers.length > 0;
|
||||
|
||||
const hasAnyResults = hasDocumentFileConnectors || hasMCPOAuth || hasMoreIntegrations;
|
||||
const hasAnyResults = hasDocumentFileConnectors || hasMoreIntegrations;
|
||||
|
||||
if (!hasAnyResults && searchQuery) {
|
||||
return (
|
||||
|
|
@ -325,20 +318,6 @@ export const AllConnectorsTab: FC<AllConnectorsTabProps> = ({
|
|||
</section>
|
||||
)}
|
||||
|
||||
{/* Live MCP Integrations */}
|
||||
{hasMCPOAuth && (
|
||||
<section>
|
||||
<div className="flex items-center gap-2 mb-4">
|
||||
<h3 className="text-sm font-semibold text-muted-foreground">
|
||||
Live MCP Integrations
|
||||
</h3>
|
||||
</div>
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
|
||||
{filteredMCPOAuth.map((connector) => renderOAuthCard(connector as OAuthConnector | ComposioConnector))}
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
|
||||
{/* More Integrations */}
|
||||
{hasMoreIntegrations && (
|
||||
<section>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue