Skip to content

Commit 8ceedf8

Browse files
committed
feat: add status to show integrations
1 parent 009478a commit 8ceedf8

7 files changed

Lines changed: 1046 additions & 45 deletions

File tree

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

Lines changed: 156 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
'use client'
22

3+
import type { ReactNode } from 'react'
34
import { GitHubConnectionCard } from '@/components/github-connection-card'
45
import { GoogleDriveConnectionCard } from '@/components/google-drive-connection-card'
56
import { GitLabConnectionCard } from '@/components/gitlab-connection-card'
67
import { JiraConnectionCard } from '@/components/jira-connection-card'
78
import { MCPCredentialCard } from '@/components/mcp-credential-card'
89
import { PageHeader } from '@/components/page-header'
910
import { useIntegrationsStatus } from '@/services/queries/use-integrations'
11+
import { Card } from '@/components/ui/card'
1012
import { Loader2 } from 'lucide-react'
1113

1214
type 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+
60158
type Props = { appSlug?: string }
61159

62160
export 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>

components/frontend/src/components/mcp-credential-card.tsx

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
'use client'
22

3-
import React, { useState } from 'react'
3+
import type { ReactNode } from 'react'
4+
import { useState } from 'react'
45
import { Button } from '@/components/ui/button'
56
import { Card } from '@/components/ui/card'
67
import { Input } from '@/components/ui/input'
@@ -34,6 +35,8 @@ type Props = {
3435
fields: FieldDefinition[]
3536
status?: MCPServerStatusData
3637
onRefresh?: () => void
38+
icon?: ReactNode
39+
iconBg?: string
3740
}
3841

3942
export function MCPCredentialCard({
@@ -43,6 +46,8 @@ export function MCPCredentialCard({
4346
fields,
4447
status,
4548
onRefresh,
49+
icon,
50+
iconBg,
4651
}: Props) {
4752
const connectMutation = useConnectMCPServer(serverName)
4853
const disconnectMutation = useDisconnectMCPServer(serverName)
@@ -100,8 +105,8 @@ export function MCPCredentialCard({
100105
<div className="p-6 flex flex-col flex-1">
101106
{/* Header */}
102107
<div className="flex items-start gap-4 mb-6">
103-
<div className="flex-shrink-0 w-16 h-16 bg-primary rounded-lg flex items-center justify-center">
104-
<Plug className="w-8 h-8 text-white" />
108+
<div className={`flex-shrink-0 w-16 h-16 ${iconBg ?? 'bg-primary'} rounded-lg flex items-center justify-center`}>
109+
{icon ?? <Plug className="w-8 h-8 text-white" />}
105110
</div>
106111
<div className="flex-1">
107112
<h3 className="text-xl font-semibold text-foreground mb-1">{displayName}</h3>
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# Specification Quality Checklist: Add Context7/DeepWiki + Generic MCP Credential Framework
2+
3+
**Purpose**: Validate specification completeness and quality before proceeding to planning
4+
**Created**: 2026-03-06
5+
**Feature**: [spec.md](../spec.md)
6+
7+
## Content Quality
8+
9+
- [x] No implementation details (languages, frameworks, APIs)
10+
- [x] Focused on user value and business needs
11+
- [x] Written for non-technical stakeholders
12+
- [x] All mandatory sections completed
13+
14+
## Requirement Completeness
15+
16+
- [x] No [NEEDS CLARIFICATION] markers remain
17+
- [x] Requirements are testable and unambiguous
18+
- [x] Success criteria are measurable
19+
- [x] Success criteria are technology-agnostic (no implementation details)
20+
- [x] All acceptance scenarios are defined
21+
- [x] Edge cases are identified (missing credentials graceful degradation)
22+
- [x] Scope is clearly bounded (no migration of existing integrations)
23+
- [x] Dependencies and assumptions identified
24+
25+
## Feature Readiness
26+
27+
- [x] All functional requirements have clear acceptance criteria
28+
- [x] User scenarios cover primary flows (5 scenarios covering both parts)
29+
- [x] Feature meets measurable outcomes defined in Success Criteria
30+
- [x] No implementation details leak into specification
31+
32+
## Notes
33+
34+
- All items pass. Spec covers both parts: immediate (Context7/DeepWiki) and foundational (credential framework).
35+
- Key design decisions documented in research.md (12 decisions).
36+
- MVP is shippable independently (T001–T002: just the `.mcp.json` change).
37+
- Ready for `/speckit.implement`.

0 commit comments

Comments
 (0)