11'use client'
22
3+ import type { ReactNode } from 'react'
34import { GitHubConnectionCard } from '@/components/github-connection-card'
45import { GoogleDriveConnectionCard } from '@/components/google-drive-connection-card'
56import { GitLabConnectionCard } from '@/components/gitlab-connection-card'
67import { JiraConnectionCard } from '@/components/jira-connection-card'
78import { MCPCredentialCard } from '@/components/mcp-credential-card'
89import { PageHeader } from '@/components/page-header'
910import { useIntegrationsStatus } from '@/services/queries/use-integrations'
11+ import { Card } from '@/components/ui/card'
1012import { Loader2 } from 'lucide-react'
1113
1214type FieldDefinition = {
@@ -17,22 +19,81 @@ type FieldDefinition = {
1719 helpText ?: string
1820}
1921
20- type MCPServerDefinition = {
22+ type MCPServerEntry = {
2123 displayName : string
2224 description : string
23- fields : FieldDefinition [ ]
24- }
25+ iconBg : string
26+ icon : ReactNode
27+ } & (
28+ | { kind : 'enabled' }
29+ | { kind : 'credentials' ; fields : FieldDefinition [ ] }
30+ )
31+
32+ /* Context7 — stylized "C7" mark */
33+ const Context7Icon = (
34+ < svg className = "w-8 h-8" viewBox = "0 0 24 24" fill = "none" aria-hidden = "true" >
35+ < text x = "3" y = "19" fontSize = "16" fontWeight = "700" fontFamily = "system-ui, sans-serif" fill = "white" > C7</ text >
36+ </ svg >
37+ )
38+
39+ /* DeepWiki — open book */
40+ const DeepWikiIcon = (
41+ < 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" >
42+ < path d = "M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z" />
43+ < path d = "M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z" />
44+ </ svg >
45+ )
46+
47+ /* Web Fetch — globe with arrow */
48+ const WebFetchIcon = (
49+ < 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" >
50+ < circle cx = "12" cy = "12" r = "10" />
51+ < path d = "M2 12h20" />
52+ < 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" />
53+ </ svg >
54+ )
55+
56+ /* Jira — the Jira logo (same SVG used in jira-connection-card) */
57+ const JiraMCPIcon = (
58+ < svg className = "w-8 h-8 text-white" fill = "currentColor" viewBox = "0 0 24 24" aria-hidden = "true" >
59+ < path d = "M11.571 11.513H0a5.218 5.218 0 0 0 5.232 5.215h2.13v2.057A5.215 5.215 0 0 0 12.575 24V12.518a1.005 1.005 0 0 0-1.005-1.005zm5.723-5.756H5.736a5.215 5.215 0 0 0 5.215 5.214h2.129v2.058a5.218 5.218 0 0 0 5.215 5.214V6.758a1.001 1.001 0 0 0-1.001-1.001zM23.013 0H11.455a5.215 5.215 0 0 0 5.215 5.215h2.129v2.057A5.215 5.215 0 0 0 24 12.483V1.005A1.001 1.001 0 0 0 23.013 0z" />
60+ </ svg >
61+ )
2562
2663/**
27- * Known MCP servers that require user-provided credentials .
28- * Servers like webfetch, context7, and deepwiki are excluded because
29- * they don't require authentication .
64+ * All MCP servers configured in the platform (.mcp.json) .
65+ * - "enabled" servers require no credentials and are always active.
66+ * - "credentials" servers need user-provided fields to function .
3067 */
31- const KNOWN_MCP_SERVERS : Record < string , MCPServerDefinition > = {
68+ const MCP_SERVERS : Record < string , MCPServerEntry > = {
69+ context7 : {
70+ kind : 'enabled' ,
71+ displayName : 'Context7' ,
72+ description : 'Up-to-date documentation and code examples for libraries and frameworks' ,
73+ iconBg : 'bg-violet-600' ,
74+ icon : Context7Icon ,
75+ } ,
76+ deepwiki : {
77+ kind : 'enabled' ,
78+ displayName : 'DeepWiki' ,
79+ description : 'AI-powered knowledge base for open-source repositories' ,
80+ iconBg : 'bg-sky-600' ,
81+ icon : DeepWikiIcon ,
82+ } ,
83+ webfetch : {
84+ kind : 'enabled' ,
85+ displayName : 'Web Fetch' ,
86+ description : 'Fetch and extract content from web pages' ,
87+ iconBg : 'bg-emerald-600' ,
88+ icon : WebFetchIcon ,
89+ } ,
3290 'mcp-atlassian' : {
91+ kind : 'credentials' ,
3392 displayName : 'Jira (MCP)' ,
3493 description :
3594 'Provide Jira credentials for the MCP Atlassian server used in agentic sessions' ,
95+ iconBg : 'bg-blue-600' ,
96+ icon : JiraMCPIcon ,
3697 fields : [
3798 {
3899 name : 'jira_url' ,
@@ -57,14 +118,50 @@ const KNOWN_MCP_SERVERS: Record<string, MCPServerDefinition> = {
57118 } ,
58119}
59120
121+ function MCPEnabledCard ( {
122+ displayName,
123+ description,
124+ iconBg,
125+ icon,
126+ } : {
127+ displayName : string
128+ description : string
129+ iconBg : string
130+ icon : ReactNode
131+ } ) {
132+ return (
133+ < Card className = "bg-card border border-border/60 shadow-sm shadow-black/[0.03] dark:shadow-black/[0.15] flex flex-col h-full" >
134+ < div className = "p-6 flex flex-col flex-1" >
135+ < div className = "flex items-start gap-4 mb-6" >
136+ < div className = { `flex-shrink-0 w-16 h-16 ${ iconBg } rounded-lg flex items-center justify-center` } >
137+ { icon }
138+ </ div >
139+ < div className = "flex-1" >
140+ < h3 className = "text-xl font-semibold text-foreground mb-1" > { displayName } </ h3 >
141+ < p className = "text-muted-foreground" > { description } </ p >
142+ </ div >
143+ </ div >
144+ < div className = "flex items-center gap-2 mt-auto" >
145+ < span className = "w-2 h-2 rounded-full bg-green-500" />
146+ < span className = "text-sm font-medium text-foreground/80" >
147+ Enabled
148+ </ span >
149+ < span className = "text-xs text-muted-foreground ml-1" >
150+ No configuration required
151+ </ span >
152+ </ div >
153+ </ div >
154+ </ Card >
155+ )
156+ }
157+
60158type Props = { appSlug ?: string }
61159
62160export default function IntegrationsClient ( { appSlug } : Props ) {
63- // Fetch all integration statuses in one call
64161 const { data : integrations , isLoading, refetch } = useIntegrationsStatus ( )
65162
66163 // Merge known servers with any additional servers that have stored credentials
67- const mcpServerNames = new Set < string > ( Object . keys ( KNOWN_MCP_SERVERS ) )
164+ const mcpServerNames = new Set < string > ( Object . keys ( MCP_SERVERS ) )
68165 if ( integrations ?. mcpServers ) {
69166 for ( const name of Object . keys ( integrations . mcpServers ) ) {
70167 mcpServerNames . add ( name )
@@ -114,44 +211,61 @@ export default function IntegrationsClient({ appSlug }: Props) {
114211 />
115212 </ div >
116213
117- { /* MCP Servers section — always shown for known servers */ }
118- { mcpServerNames . size > 0 && (
119- < div className = "mt-8" >
120- < h2 className = "text-lg font-semibold text-foreground mb-4" > MCP Servers</ h2 >
121- < div className = "grid grid-cols-1 md:grid-cols-2 gap-6" >
122- { [ ...mcpServerNames ] . map ( ( name ) => {
123- const definition = KNOWN_MCP_SERVERS [ name ]
124- const serverStatus = integrations ?. mcpServers ?. [ name ]
214+ { /* MCP Servers section */ }
215+ < div className = "mt-8" >
216+ < h2 className = "text-lg font-semibold text-foreground mb-4" > MCP Servers</ h2 >
217+ < div className = "grid grid-cols-1 md:grid-cols-2 gap-6" >
218+ { [ ...mcpServerNames ] . map ( ( name ) => {
219+ const entry = MCP_SERVERS [ name ]
220+ const serverStatus = integrations ?. mcpServers ?. [ name ]
221+
222+ if ( entry ?. kind === 'enabled' ) {
125223 return (
126- < MCPCredentialCard
224+ < MCPEnabledCard
127225 key = { name }
128- serverName = { name }
129- displayName = { definition ?. displayName ?? name }
130- description = {
131- definition ?. description ?? `Credentials for the ${ name } MCP server`
132- }
133- fields = {
134- definition ?. fields ?? [
135- {
136- name : 'api_key' ,
137- label : 'API Key' ,
138- type : 'password' as const ,
139- placeholder : 'Enter API key' ,
140- } ,
141- ]
142- }
143- status = {
144- serverStatus
145- ? { connected : serverStatus . connected , serverName : name }
146- : { connected : false , serverName : name }
147- }
148- onRefresh = { refetch }
226+ displayName = { entry . displayName }
227+ description = { entry . description }
228+ iconBg = { entry . iconBg }
229+ icon = { entry . icon }
149230 />
150231 )
151- } ) }
152- </ div >
232+ }
233+
234+ return (
235+ < MCPCredentialCard
236+ key = { name }
237+ serverName = { name }
238+ displayName = { entry ?. displayName ?? name }
239+ icon = { entry ?. icon }
240+ iconBg = { entry ?. iconBg }
241+ description = {
242+ entry ?. kind === 'credentials'
243+ ? entry . description
244+ : `Credentials for the ${ name } MCP server`
245+ }
246+ fields = {
247+ entry ?. kind === 'credentials'
248+ ? entry . fields
249+ : [
250+ {
251+ name : 'api_key' ,
252+ label : 'API Key' ,
253+ type : 'password' as const ,
254+ placeholder : 'Enter API key' ,
255+ } ,
256+ ]
257+ }
258+ status = {
259+ serverStatus
260+ ? { connected : serverStatus . connected , serverName : name }
261+ : { connected : false , serverName : name }
262+ }
263+ onRefresh = { refetch }
264+ />
265+ )
266+ } ) }
153267 </ div >
154- ) }
268+ </ div >
155269 </ >
156270 ) }
157271 </ div >
0 commit comments