McpConfigPanel.tsx146 lines · main
1'use client'
2
3import React, { useMemo, useState } from 'react'
4import { cn, Separator } from 'ui'
5import { CodeBlock } from 'ui-patterns/CodeBlock'
6
7import { InfoTooltip } from '../info-tooltip'
8import { ClientSelectDropdown } from './components/ClientSelectDropdown'
9import { McpConfigurationDisplay } from './components/McpConfigurationDisplay'
10import { McpConfigurationOptions } from './components/McpConfigurationOptions'
11import {
12 FEATURE_GROUPS_NON_PLATFORM,
13 FEATURE_GROUPS_PLATFORM,
14 MCP_CLIENT_GROUPS,
15 MCP_CLIENTS,
16} from './constants'
17import type { McpClient, McpOnCopyCallback } from './types'
18import { getMcpUrl } from './utils/getMcpUrl'
19
20const CLIENT_GROUPS = MCP_CLIENT_GROUPS.map((group) => ({
21 heading: group.heading,
22 clients: group.keys
23 .map((key) => MCP_CLIENTS.find((c) => c.key === key))
24 .filter(Boolean) as (typeof MCP_CLIENTS)[number][],
25}))
26
27export interface McpConfigPanelProps {
28 baseUrl?: string
29 projectRef?: string
30 initialSelectedClient?: McpClient
31 onClientSelect?: (client: McpClient) => void
32 onCopyCallback: (type?: McpOnCopyCallback) => void
33 onInstallCallback?: () => void
34 theme?: 'light' | 'dark'
35 className?: string
36 isPlatform: boolean // For docs this is controlled by state, for studio by environment variable
37 apiUrl?: string
38}
39
40export function McpConfigPanel({
41 projectRef,
42 initialSelectedClient,
43 onClientSelect,
44 onCopyCallback,
45 onInstallCallback,
46 className,
47 theme = 'dark',
48 isPlatform,
49 apiUrl,
50}: McpConfigPanelProps) {
51 const [readonly, setReadonly] = useState(false)
52 const [selectedFeatures, setSelectedFeatures] = useState<string[]>([])
53 const [selectedClient, setSelectedClient] = useState(initialSelectedClient ?? MCP_CLIENTS[0])
54
55 const supportedFeatures = isPlatform ? FEATURE_GROUPS_PLATFORM : FEATURE_GROUPS_NON_PLATFORM
56 const selectedFeaturesSupported = useMemo(() => {
57 return selectedFeatures.filter((feature) =>
58 supportedFeatures.some((group) => group.id === feature)
59 )
60 }, [selectedFeatures, supportedFeatures])
61
62 const { mcpUrl, clientConfig } = getMcpUrl({
63 projectRef,
64 isPlatform,
65 apiUrl,
66 readonly,
67 features: selectedFeaturesSupported,
68 selectedClient,
69 })
70
71 const handleClientChange = (clientKey: string) => {
72 const client = MCP_CLIENTS.find((c) => c.key === clientKey)
73 if (client) {
74 setSelectedClient(client)
75 }
76 }
77 React.useEffect(() => {
78 onClientSelect?.(selectedClient)
79 }, [selectedClient, onClientSelect])
80
81 const innerPanelSpacing = 'px-4 py-3'
82
83 return (
84 <div className={cn('space-y-6', className)}>
85 <div className={cn('border rounded-lg')}>
86 <h3 className={innerPanelSpacing}>Options</h3>
87 <Separator />
88 <McpConfigurationOptions
89 className={innerPanelSpacing}
90 readonly={readonly}
91 onReadonlyChange={setReadonly}
92 selectedFeatures={selectedFeaturesSupported}
93 onFeaturesChange={setSelectedFeatures}
94 featureGroups={isPlatform ? FEATURE_GROUPS_PLATFORM : FEATURE_GROUPS_NON_PLATFORM}
95 />
96 <div className={innerPanelSpacing}>
97 <CodeBlock
98 focusable={false}
99 title={
100 <div className="flex items-center gap-2">
101 Server URL
102 <InfoTooltip>
103 {`MCP clients should support the Streamable HTTP transport${isPlatform ? ' and OAuth 2.1 with dynamic client registration' : ''}`}
104 </InfoTooltip>
105 </div>
106 }
107 hideLineNumbers
108 language="http"
109 className="max-h-64 overflow-y-auto"
110 onCopyCallback={() => onCopyCallback?.('url')}
111 >
112 {mcpUrl}
113 </CodeBlock>
114 </div>
115 </div>
116 <div className="flex flex-col gap-y-3">
117 <ClientSelectDropdown
118 label="Client"
119 clients={MCP_CLIENTS}
120 groups={CLIENT_GROUPS}
121 selectedClient={selectedClient}
122 onClientChange={handleClientChange}
123 theme={theme}
124 />
125 <p className="text-xs text-foreground-lighter">
126 Configure your MCP client to connect with your Briven project
127 </p>
128 </div>
129 <div className={cn('border rounded-lg')}>
130 <div className={innerPanelSpacing}>
131 <h3>Installation</h3>
132 </div>
133 <Separator />
134 <McpConfigurationDisplay
135 className={innerPanelSpacing}
136 theme={theme}
137 selectedClient={selectedClient}
138 clientConfig={clientConfig}
139 onCopyCallback={onCopyCallback}
140 onInstallCallback={onInstallCallback}
141 isPlatform={isPlatform}
142 />
143 </div>
144 </div>
145 )
146}