Skip to content

Commit 2158fa2

Browse files
feat(credential-groups): add managed MCP connector presets
1 parent 66c191f commit 2158fa2

42 files changed

Lines changed: 1842 additions & 385 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import {
2+
deleteCredentialGroupMcpConnectorContract,
3+
updateCredentialGroupMcpConnectorContract,
4+
} from '@/lib/api/contracts/credential-groups'
5+
import {
6+
defineInternalJsonRoute,
7+
internalRateLimits,
8+
internalSessionAuth,
9+
} from '@/lib/api/server/routes'
10+
import {
11+
deleteCredentialGroupMcpConnector,
12+
updateCredentialGroupMcpConnector,
13+
} from '@/lib/credential-groups/application/manage-mcp-connectors'
14+
import { credentialGroupOperations } from '@/lib/credential-groups/application/operations'
15+
import { createCredentialGroupInternalErrorPolicy } from '@/app/api/workspaces/[id]/credential-groups/error-policy'
16+
17+
const rateLimit = internalRateLimits.none({
18+
reason: 'Preserve existing internal Credential Group settings behavior',
19+
})
20+
21+
export const PATCH = defineInternalJsonRoute({
22+
contract: updateCredentialGroupMcpConnectorContract,
23+
auth: internalSessionAuth,
24+
operation: credentialGroupOperations.updateMcpConnector,
25+
rateLimit,
26+
errorPolicy: createCredentialGroupInternalErrorPolicy('Failed to update managed MCP connector'),
27+
mapInput: ({ params, body }) => ({
28+
assertedWorkspaceId: params.id,
29+
credentialGroupId: params.groupId,
30+
connectorId: params.connectorId,
31+
update: body,
32+
}),
33+
useCase: updateCredentialGroupMcpConnector,
34+
present: ({ mcpServer }) => ({ mcpServer }),
35+
})
36+
37+
export const DELETE = defineInternalJsonRoute({
38+
contract: deleteCredentialGroupMcpConnectorContract,
39+
auth: internalSessionAuth,
40+
operation: credentialGroupOperations.deleteMcpConnector,
41+
rateLimit,
42+
errorPolicy: createCredentialGroupInternalErrorPolicy('Failed to remove managed MCP connector'),
43+
mapInput: ({ params }) => ({
44+
assertedWorkspaceId: params.id,
45+
credentialGroupId: params.groupId,
46+
connectorId: params.connectorId,
47+
}),
48+
useCase: deleteCredentialGroupMcpConnector,
49+
present: () => ({ success: true as const }),
50+
})
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { createCredentialGroupMcpConnectorContract } from '@/lib/api/contracts/credential-groups'
2+
import {
3+
defineInternalJsonRoute,
4+
internalRateLimits,
5+
internalSessionAuth,
6+
} from '@/lib/api/server/routes'
7+
import { createCredentialGroupMcpConnector } from '@/lib/credential-groups/application/manage-mcp-connectors'
8+
import { credentialGroupOperations } from '@/lib/credential-groups/application/operations'
9+
import { createCredentialGroupInternalErrorPolicy } from '@/app/api/workspaces/[id]/credential-groups/error-policy'
10+
11+
export const POST = defineInternalJsonRoute({
12+
contract: createCredentialGroupMcpConnectorContract,
13+
auth: internalSessionAuth,
14+
operation: credentialGroupOperations.createMcpConnector,
15+
rateLimit: internalRateLimits.none({
16+
reason: 'Preserve existing internal Credential Group settings behavior',
17+
}),
18+
errorPolicy: createCredentialGroupInternalErrorPolicy('Failed to add managed MCP connector'),
19+
mapInput: ({ params, body }) => ({
20+
assertedWorkspaceId: params.id,
21+
credentialGroupId: params.groupId,
22+
connector: body,
23+
}),
24+
useCase: createCredentialGroupMcpConnector,
25+
present: ({ mcpServer }) => ({ mcpServer }),
26+
})

apps/sim/app/credential-groups/enroll/[token]/page.tsx

Lines changed: 24 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@ import { type ReactNode, Suspense } from 'react'
22
import { Chip } from '@sim/emcn'
33
import type { Metadata } from 'next'
44
import { headers } from 'next/headers'
5-
import { McpIcon } from '@/components/icons'
65
import { asOrchestrationError } from '@/lib/core/orchestration/types'
76
import { authenticateCredentialGroupEnrollment } from '@/lib/credential-groups/application/enrollment-auth'
87
import { readPublicCredentialGroupEnrollment } from '@/lib/credential-groups/application/public-enrollment'
8+
import { getManagedMcpConnectorIcon } from '@/lib/credential-groups/managed-mcp-connector-icons'
99
import { getCredentialGroupProviderService } from '@/lib/credential-groups/providers'
1010
import { enforcePublicCredentialGroupIpRateLimit } from '@/lib/credential-groups/rate-limit'
1111
import { SupportFooter } from '@/app/(auth)/components'
@@ -181,26 +181,29 @@ export default async function CredentialGroupEnrollmentPage({
181181
/>
182182
)
183183
})}
184-
{enrollment.mcpServers.map((server) => (
185-
<SettingsResourceRow
186-
key={server.id}
187-
icon={<McpIcon />}
188-
title={server.name}
189-
description={
190-
server.connection?.status === 'connected'
191-
? 'Connected'
192-
: server.connection
193-
? 'Reconnect required'
194-
: server.description || 'Not connected'
195-
}
196-
trailing={
197-
<OAuthConnectLink
198-
href={`/api/credential-groups/enroll/${token}/mcp/${server.id}`}
199-
reconnect={Boolean(server.connection)}
200-
/>
201-
}
202-
/>
203-
))}
184+
{enrollment.mcpServers.map((server) => {
185+
const ConnectorIcon = getManagedMcpConnectorIcon(server.managedConnectorId)
186+
return (
187+
<SettingsResourceRow
188+
key={server.id}
189+
icon={<ConnectorIcon />}
190+
title={server.name}
191+
description={
192+
server.connection?.status === 'connected'
193+
? 'Connected'
194+
: server.connection
195+
? 'Reconnect required'
196+
: server.description || 'Not connected'
197+
}
198+
trailing={
199+
<OAuthConnectLink
200+
href={`/api/credential-groups/enroll/${token}/mcp/${server.id}`}
201+
reconnect={Boolean(server.connection)}
202+
/>
203+
}
204+
/>
205+
)
206+
})}
204207
</div>
205208
</SettingsSection>
206209
<form

apps/sim/app/workspace/[workspaceId]/home/components/chat-context-kind-registry/chat-context-kind-registry.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
} from '@sim/emcn/icons'
1212
import { AgentSkillsIcon, McpIcon } from '@/components/icons'
1313
import { getDocumentIcon } from '@/components/icons/document-icons'
14+
import { getManagedMcpConnectorIcon } from '@/lib/credential-groups/managed-mcp-connector-icons'
1415
import type { ChatContextKind, ChatMessageContext } from '@/app/workspace/[workspaceId]/home/types'
1516
import { BrandIcon } from '@/blocks/brand-icon'
1617
import { getBlockRegistry } from '@/blocks/registry'
@@ -118,6 +119,12 @@ export const CHAT_CONTEXT_KIND_REGISTRY: Record<ChatContextKind, ChatContextKind
118119
},
119120
mcp: {
120121
label: 'MCP server',
121-
renderIcon: ({ className }) => <McpIcon className={className} />,
122+
renderIcon: ({ context, className }) => {
123+
const McpServerIcon =
124+
context.kind === 'mcp' && context.managedConnectorId
125+
? getManagedMcpConnectorIcon(context.managedConnectorId)
126+
: McpIcon
127+
return <McpServerIcon className={className} />
128+
},
122129
},
123130
}

apps/sim/app/workspace/[workspaceId]/home/components/user-input/components/prompt-editor/use-prompt-editor.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ import {
3333
restoreSkillTriggerText,
3434
SKILL_CHIP_TRIGGER,
3535
} from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/copilot/components/user-input/utils'
36-
import { type McpServer, useMcpServers } from '@/hooks/queries/mcp'
36+
import { type McpServer, useMcpToolServers } from '@/hooks/queries/mcp'
3737
import { type SkillDefinition, useSkills } from '@/hooks/queries/skills'
3838
import type { ChatContext } from '@/stores/panel'
3939

@@ -165,7 +165,7 @@ export function usePromptEditor({
165165
onPasteFiles,
166166
}: UsePromptEditorProps) {
167167
const { data: skills = [] } = useSkills(workspaceId)
168-
const { data: allMcpServers = [] } = useMcpServers(workspaceId)
168+
const { data: allMcpServers = [] } = useMcpToolServers(workspaceId)
169169
const mcpServers = useMemo(
170170
() => allMcpServers.filter((server) => server.enabled && server.workspaceId === workspaceId),
171171
[allMcpServers, workspaceId]
@@ -527,7 +527,12 @@ export function usePromptEditor({
527527
setValueState(newValue)
528528
}
529529

530-
addContextNotified({ kind: 'mcp', serverId: server.id, label: server.name })
530+
addContextNotified({
531+
kind: 'mcp',
532+
serverId: server.id,
533+
label: server.name,
534+
...(server.managedConnectorId ? { managedConnectorId: server.managedConnectorId } : {}),
535+
})
531536
},
532537
[textareaRef, addContextNotified]
533538
)

apps/sim/app/workspace/[workspaceId]/home/components/user-input/components/skills-menu-dropdown/skills-menu-dropdown.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
dropdownMenuRowClass,
1010
} from '@sim/emcn'
1111
import { AgentSkillsIcon, McpIcon } from '@/components/icons'
12+
import { getManagedMcpConnectorIcon } from '@/lib/credential-groups/managed-mcp-connector-icons'
1213
import type { McpServer } from '@/hooks/queries/mcp'
1314
import type { SkillDefinition } from '@/hooks/queries/skills'
1415

@@ -201,6 +202,10 @@ export const SkillsMenuDropdown = React.memo(
201202
{filteredItems.length > 0 ? (
202203
filteredItems.map((target, index) => {
203204
const isActive = index === activeIndex
205+
const McpServerIcon =
206+
target.kind === 'mcp' && target.item.managedConnectorId
207+
? getManagedMcpConnectorIcon(target.item.managedConnectorId)
208+
: McpIcon
204209
return (
205210
<button
206211
key={`${target.kind}:${target.item.id}`}
@@ -216,7 +221,7 @@ export const SkillsMenuDropdown = React.memo(
216221
isActive && 'bg-[var(--surface-hover)]'
217222
)}
218223
>
219-
{target.kind === 'skill' ? <AgentSkillsIcon /> : <McpIcon />}
224+
{target.kind === 'skill' ? <AgentSkillsIcon /> : <McpServerIcon />}
220225
<span>{target.item.name}</span>
221226
</button>
222227
)

apps/sim/app/workspace/[workspaceId]/home/components/user-input/hooks/use-skill-auto-mention.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,12 @@ export function useSkillAutoMention({
8989
for (const server of mcpServers) {
9090
const key = server.name.toLowerCase()
9191
if (!byName.has(key)) {
92-
byName.set(key, { kind: 'mcp', serverId: server.id, label: server.name })
92+
byName.set(key, {
93+
kind: 'mcp',
94+
serverId: server.id,
95+
label: server.name,
96+
...(server.managedConnectorId ? { managedConnectorId: server.managedConnectorId } : {}),
97+
})
9398
}
9499
}
95100
const names = [...byName.values()]

apps/sim/app/workspace/[workspaceId]/home/hooks/use-chat.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3876,7 +3876,12 @@ export function useChat(
38763876
...('folderId' in c && c.folderId ? { folderId: c.folderId } : {}),
38773877
...(c.kind === 'skill' && 'skillId' in c ? { skillId: c.skillId } : {}),
38783878
...(c.kind === 'integration' && 'blockType' in c ? { blockType: c.blockType } : {}),
3879-
...(c.kind === 'mcp' && 'serverId' in c ? { serverId: c.serverId } : {}),
3879+
...(c.kind === 'mcp' && 'serverId' in c
3880+
? {
3881+
serverId: c.serverId,
3882+
...(c.managedConnectorId ? { managedConnectorId: c.managedConnectorId } : {}),
3883+
}
3884+
: {}),
38803885
...(c.kind === 'file_selection'
38813886
? {
38823887
fileName: c.fileName,

apps/sim/app/workspace/[workspaceId]/home/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import type { ManagedMcpConnectorId } from '@/lib/credential-groups/managed-mcp-connectors'
12
import type { ChatContext } from '@/stores/panel'
23
import type { BrowserTextSelection, TerminalTextSelection } from '@/stores/panel/types'
34

@@ -152,6 +153,7 @@ export interface ChatMessageContext {
152153
blockType?: string
153154
skillId?: string
154155
serverId?: string
156+
managedConnectorId?: ManagedMcpConnectorId
155157
/** Selected passage for a `file_selection` context. */
156158
text?: string
157159
/** Source file name for a `file_selection` context. */

0 commit comments

Comments
 (0)