Skip to content

Commit 05a0f69

Browse files
committed
feat: move integrations outside user scope
1 parent 9dba382 commit 05a0f69

2 files changed

Lines changed: 55 additions & 141 deletions

File tree

components/frontend/src/app/integrations/IntegrationsClient.tsx

Lines changed: 46 additions & 137 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import { JiraConnectionCard } from '@/components/jira-connection-card'
88
import { MCPCredentialCard } from '@/components/mcp-credential-card'
99
import { PageHeader } from '@/components/page-header'
1010
import { useIntegrationsStatus } from '@/services/queries/use-integrations'
11-
import { Card } from '@/components/ui/card'
1211
import { Loader2 } from 'lucide-react'
1312

1413
type FieldDefinition = {
@@ -19,53 +18,21 @@ type FieldDefinition = {
1918
helpText?: string
2019
}
2120

22-
type MCPServerEntry = {
21+
type MCPCredentialServer = {
2322
displayName: string
2423
description: string
25-
iconBg: string
26-
icon: ReactNode
27-
} & (
28-
| { kind: 'enabled' }
29-
| { kind: 'credentials'; fields: FieldDefinition[] }
30-
)
31-
32-
/* eslint-disable @next/next/no-img-element */
24+
iconBg?: string
25+
icon?: ReactNode
26+
fields: FieldDefinition[]
27+
}
3328

3429
/**
35-
* All MCP servers configured in the platform (.mcp.json).
36-
* - "enabled" servers require no credentials and are always active.
37-
* - "credentials" servers need user-provided fields to function.
30+
* MCP servers that require user-provided credentials.
31+
* Platform-wide no-auth servers (context7, deepwiki, webfetch) are not shown
32+
* here — they belong at the project scope, not user-scoped integrations.
3833
*/
39-
const MCP_SERVERS: Record<string, MCPServerEntry> = {
40-
context7: {
41-
kind: 'enabled',
42-
displayName: 'Context7',
43-
description: 'Up-to-date documentation and code examples for libraries and frameworks',
44-
iconBg: '',
45-
icon: <img src="/logos/context7.svg" alt="Context7" className="w-16 h-16 rounded-lg" />,
46-
},
47-
deepwiki: {
48-
kind: 'enabled',
49-
displayName: 'DeepWiki',
50-
description: 'AI-powered knowledge base for open-source repositories',
51-
iconBg: 'bg-white',
52-
icon: <img src="/logos/deepwiki.png" alt="DeepWiki" className="w-10 h-10" />,
53-
},
54-
webfetch: {
55-
kind: 'enabled',
56-
displayName: 'Web Fetch',
57-
description: 'Fetch and extract content from web pages',
58-
iconBg: 'bg-emerald-600',
59-
icon: (
60-
<svg className="w-8 h-8 text-white" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
61-
<circle cx="12" cy="12" r="10" />
62-
<path d="M2 12h20" />
63-
<path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z" />
64-
</svg>
65-
),
66-
},
34+
const MCP_CREDENTIAL_SERVERS: Record<string, MCPCredentialServer> = {
6735
'mcp-atlassian': {
68-
kind: 'credentials',
6936
displayName: 'Jira (MCP)',
7037
description:
7138
'Provide Jira credentials for the MCP Atlassian server used in agentic sessions',
@@ -99,56 +66,13 @@ const MCP_SERVERS: Record<string, MCPServerEntry> = {
9966
},
10067
}
10168

102-
function MCPEnabledCard({
103-
displayName,
104-
description,
105-
iconBg,
106-
icon,
107-
}: {
108-
displayName: string
109-
description: string
110-
iconBg: string
111-
icon: ReactNode
112-
}) {
113-
return (
114-
<Card className="bg-card border border-border/60 shadow-sm shadow-black/[0.03] dark:shadow-black/[0.15] flex flex-col h-full">
115-
<div className="p-6 flex flex-col flex-1">
116-
<div className="flex items-start gap-4 mb-6">
117-
{iconBg ? (
118-
<div className={`flex-shrink-0 w-16 h-16 ${iconBg} rounded-lg flex items-center justify-center`}>
119-
{icon}
120-
</div>
121-
) : (
122-
<div className="flex-shrink-0 w-16 h-16">
123-
{icon}
124-
</div>
125-
)}
126-
<div className="flex-1">
127-
<h3 className="text-xl font-semibold text-foreground mb-1">{displayName}</h3>
128-
<p className="text-muted-foreground">{description}</p>
129-
</div>
130-
</div>
131-
<div className="flex items-center gap-2 mt-auto">
132-
<span className="w-2 h-2 rounded-full bg-green-500" />
133-
<span className="text-sm font-medium text-foreground/80">
134-
Enabled
135-
</span>
136-
<span className="text-xs text-muted-foreground ml-1">
137-
No configuration required
138-
</span>
139-
</div>
140-
</div>
141-
</Card>
142-
)
143-
}
144-
14569
type Props = { appSlug?: string }
14670

14771
export default function IntegrationsClient({ appSlug }: Props) {
14872
const { data: integrations, isLoading, refetch } = useIntegrationsStatus()
14973

150-
// Merge known servers with any additional servers that have stored credentials
151-
const mcpServerNames = new Set<string>(Object.keys(MCP_SERVERS))
74+
// Merge known credential servers with any additional servers that have stored credentials
75+
const mcpServerNames = new Set<string>(Object.keys(MCP_CREDENTIAL_SERVERS))
15276
if (integrations?.mcpServers) {
15377
for (const name of Object.keys(integrations.mcpServers)) {
15478
mcpServerNames.add(name)
@@ -198,61 +122,46 @@ export default function IntegrationsClient({ appSlug }: Props) {
198122
/>
199123
</div>
200124

201-
{/* MCP Servers section */}
202-
<div className="mt-8">
203-
<h2 className="text-lg font-semibold text-foreground mb-4">MCP Servers</h2>
204-
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
205-
{[...mcpServerNames].map((name) => {
206-
const entry = MCP_SERVERS[name]
207-
const serverStatus = integrations?.mcpServers?.[name]
208-
209-
if (entry?.kind === 'enabled') {
125+
{/* MCP Servers section — only credential-based servers (user-scoped) */}
126+
{mcpServerNames.size > 0 && (
127+
<div className="mt-8">
128+
<h2 className="text-lg font-semibold text-foreground mb-4">MCP Servers</h2>
129+
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
130+
{[...mcpServerNames].map((name) => {
131+
const entry = MCP_CREDENTIAL_SERVERS[name]
132+
const serverStatus = integrations?.mcpServers?.[name]
210133
return (
211-
<MCPEnabledCard
134+
<MCPCredentialCard
212135
key={name}
213-
displayName={entry.displayName}
214-
description={entry.description}
215-
iconBg={entry.iconBg}
216-
icon={entry.icon}
136+
serverName={name}
137+
displayName={entry?.displayName ?? name}
138+
icon={entry?.icon}
139+
iconBg={entry?.iconBg}
140+
description={
141+
entry?.description ?? `Credentials for the ${name} MCP server`
142+
}
143+
fields={
144+
entry?.fields ?? [
145+
{
146+
name: 'api_key',
147+
label: 'API Key',
148+
type: 'password' as const,
149+
placeholder: 'Enter API key',
150+
},
151+
]
152+
}
153+
status={
154+
serverStatus
155+
? { connected: serverStatus.connected, serverName: name }
156+
: { connected: false, serverName: name }
157+
}
158+
onRefresh={refetch}
217159
/>
218160
)
219-
}
220-
221-
return (
222-
<MCPCredentialCard
223-
key={name}
224-
serverName={name}
225-
displayName={entry?.displayName ?? name}
226-
icon={entry?.icon}
227-
iconBg={entry?.iconBg}
228-
description={
229-
entry?.kind === 'credentials'
230-
? entry.description
231-
: `Credentials for the ${name} MCP server`
232-
}
233-
fields={
234-
entry?.kind === 'credentials'
235-
? entry.fields
236-
: [
237-
{
238-
name: 'api_key',
239-
label: 'API Key',
240-
type: 'password' as const,
241-
placeholder: 'Enter API key',
242-
},
243-
]
244-
}
245-
status={
246-
serverStatus
247-
? { connected: serverStatus.connected, serverName: name }
248-
: { connected: false, serverName: name }
249-
}
250-
onRefresh={refetch}
251-
/>
252-
)
253-
})}
161+
})}
162+
</div>
254163
</div>
255-
</div>
164+
)}
256165
</>
257166
)}
258167
</div>

components/frontend/src/components/navigation.tsx

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -134,15 +134,20 @@ export function Navigation({ feedbackUrl }: NavigationProps) {
134134
</a>
135135
)}
136136
<ThemeToggle />
137+
<Button
138+
variant="ghost"
139+
size="sm"
140+
onClick={() => router.push('/integrations')}
141+
className="text-muted-foreground hover:text-foreground"
142+
>
143+
<Plug className="w-4 h-4 mr-1" />
144+
Integrations
145+
</Button>
137146
<DropdownMenu>
138147
<DropdownMenuTrigger className="outline-none">
139148
<UserBubble />
140149
</DropdownMenuTrigger>
141150
<DropdownMenuContent align="end">
142-
<DropdownMenuItem onSelect={() => router.push('/integrations')}>
143-
<Plug className="w-4 h-4 mr-2" />
144-
Integrations
145-
</DropdownMenuItem>
146151
<DropdownMenuItem onSelect={handleLogout}>
147152
<LogOut className="w-4 h-4 mr-2" />
148153
Logout

0 commit comments

Comments
 (0)