1+ import { Icon } from '@iconify/react' ;
12import { SectionBox } from '@kinvolk/headlamp-plugin/lib/components/common' ;
23import {
34 Box ,
45 Button ,
56 FormControlLabel ,
67 Switch ,
7- TextField ,
88 Typography ,
9- Alert ,
109} from '@mui/material' ;
11- import { Icon } from '@iconify/react' ;
1210import React , { useEffect , useState } from 'react' ;
1311import { pluginStore } from '../../utils' ;
12+ import MCPConfigEditorDialog from './MCPConfigEditorDialog' ;
1413
1514// Helper function to check if running in Electron
1615const isElectron = ( ) : boolean => {
@@ -23,6 +22,7 @@ export interface MCPServer {
2322 name : string ;
2423 command : string ;
2524 args : string [ ] ;
25+ env ?: Record < string , string > ;
2626 enabled : boolean ;
2727}
2828
@@ -36,13 +36,6 @@ interface MCPSettingsProps {
3636 onConfigChange ?: ( config : MCPConfig ) => void ;
3737}
3838
39- const defaultMCPServer : MCPServer = {
40- name : '' ,
41- command : 'npx' ,
42- args : [ ] ,
43- enabled : true ,
44- } ;
45-
4639export function MCPSettings ( { config, onConfigChange } : MCPSettingsProps ) {
4740 const [ mcpConfig , setMCPConfig ] = useState < MCPConfig > (
4841 config || {
@@ -51,8 +44,7 @@ export function MCPSettings({ config, onConfigChange }: MCPSettingsProps) {
5144 }
5245 ) ;
5346
54- const [ jsonConfig , setJsonConfig ] = useState ( '' ) ;
55- const [ jsonError , setJsonError ] = useState ( '' ) ;
47+ const [ editorDialogOpen , setEditorDialogOpen ] = useState ( false ) ;
5648
5749 useEffect ( ( ) => {
5850 // Load MCP config from Electron if available
@@ -67,10 +59,6 @@ export function MCPSettings({ config, onConfigChange }: MCPSettingsProps) {
6759 }
6860 } , [ ] ) ;
6961
70- // Update JSON config when mcpConfig changes
71- useEffect ( ( ) => {
72- setJsonConfig ( JSON . stringify ( mcpConfig , null , 2 ) ) ;
73- } , [ mcpConfig ] ) ;
7462
7563 const loadMCPConfigFromElectron = async ( ) => {
7664 if ( ! isElectron ( ) ) return ;
@@ -140,25 +128,25 @@ export function MCPSettings({ config, onConfigChange }: MCPSettingsProps) {
140128 name : 'inspektor-gadget' ,
141129 command : 'docker' ,
142130 args : [
143- 'run' , '-i' , '--rm' ,
144- '--mount' , 'type=bind,src=%USERPROFILE%\\.kube\\config,dst=/root/.kube/config,readonly' ,
145- '--mount' , 'type=bind,src=%USERPROFILE%\\.minikube,dst=/root/.minikube,readonly' ,
146- 'ghcr.io/inspektor-gadget/ig-mcp-server:latest' ,
147- '-gadget-discoverer=artifacthub'
131+ 'mcp' ,
132+ 'gateway' ,
133+ 'run'
148134 ] ,
149- enabled : false , // Disabled by default to avoid errors
135+ enabled : true ,
150136 } ,
151137 {
152- name : 'filesystem ' ,
153- command : 'npx ' ,
138+ name : 'flux-mcp ' ,
139+ command : 'flux-operator-mcp ' ,
154140 args : [
155- '-y' , '@danielsuguimoto/readonly-server-filesystem' ,
156- 'C:\\Users\\' + ( process . env . USERNAME || 'username' ) + '\\Desktop'
141+ 'serve'
157142 ] ,
143+ env : {
144+ 'KUBECONFIG' : '/Users/ashughildiyal/.kube/config'
145+ } ,
158146 enabled : true ,
159147 }
160148 ] ;
161-
149+
162150 newConfig . servers = defaultServers ;
163151 }
164152
@@ -167,87 +155,16 @@ export function MCPSettings({ config, onConfigChange }: MCPSettingsProps) {
167155
168156
169157
170- const handleJsonConfigChange = ( value : string ) => {
171- setJsonConfig ( value ) ;
172- setJsonError ( '' ) ;
173- } ;
174-
175- const validateAndApplyJsonConfig = ( ) => {
176- try {
177- const parsedConfig = JSON . parse ( jsonConfig ) as MCPConfig ;
178-
179- // Validate the structure
180- if ( typeof parsedConfig . enabled !== 'boolean' ) {
181- throw new Error ( 'enabled field must be a boolean' ) ;
182- }
183-
184- if ( ! Array . isArray ( parsedConfig . servers ) ) {
185- throw new Error ( 'servers field must be an array' ) ;
186- }
187-
188- // Validate each server
189- parsedConfig . servers . forEach ( ( server , index ) => {
190- if ( typeof server . name !== 'string' ) {
191- throw new Error ( `Server ${ index } : name must be a string` ) ;
192- }
193- if ( typeof server . command !== 'string' ) {
194- throw new Error ( `Server ${ index } : command must be a string` ) ;
195- }
196- if ( ! Array . isArray ( server . args ) ) {
197- throw new Error ( `Server ${ index } : args must be an array` ) ;
198- }
199- if ( typeof server . enabled !== 'boolean' ) {
200- throw new Error ( `Server ${ index } : enabled must be a boolean` ) ;
201- }
202- } ) ;
203-
204- // Apply the config
205- handleConfigChange ( parsedConfig ) ;
206- setJsonError ( '' ) ;
207- } catch ( error ) {
208- setJsonError ( error instanceof Error ? error . message : 'Invalid JSON configuration' ) ;
209- }
210- } ;
211-
212- const resetJsonToCurrentConfig = ( ) => {
213- setJsonConfig ( JSON . stringify ( mcpConfig , null , 2 ) ) ;
214- setJsonError ( '' ) ;
158+ const handleOpenEditorDialog = ( ) => {
159+ setEditorDialogOpen ( true ) ;
215160 } ;
216161
217- const getExampleConfig = ( ) : MCPConfig => {
218- return {
219- enabled : true ,
220- servers : [
221- {
222- name : "inspektor-gadget" ,
223- command : "docker" ,
224- args : [
225- "run" , "-i" , "--rm" ,
226- "--mount" , "type=bind,src=%USERPROFILE%\\.kube\\config,dst=/root/.kube/config,readonly" ,
227- "--mount" , "type=bind,src=%USERPROFILE%\\.minikube,dst=/root/.minikube,readonly" ,
228- "ghcr.io/inspektor-gadget/ig-mcp-server:latest" ,
229- "-gadget-discoverer=artifacthub"
230- ] ,
231- enabled : false
232- } ,
233- {
234- name : "filesystem" ,
235- command : "npx" ,
236- args : [
237- "-y" , "@danielsuguimoto/readonly-server-filesystem" ,
238- "C:\\Users\\username\\Desktop" ,
239- "C:\\Users\\username\\Documents"
240- ] ,
241- enabled : true
242- }
243- ]
244- } ;
162+ const handleCloseEditorDialog = ( ) => {
163+ setEditorDialogOpen ( false ) ;
245164 } ;
246165
247- const loadExampleConfig = ( ) => {
248- const exampleConfig = getExampleConfig ( ) ;
249- setJsonConfig ( JSON . stringify ( exampleConfig , null , 2 ) ) ;
250- setJsonError ( '' ) ;
166+ const handleSaveConfig = ( newConfig : MCPConfig ) => {
167+ handleConfigChange ( newConfig ) ;
251168 } ;
252169
253170 // Only show MCP settings in Electron
@@ -282,86 +199,60 @@ export function MCPSettings({ config, onConfigChange }: MCPSettingsProps) {
282199
283200 { mcpConfig . enabled && (
284201 < >
285- { /* JSON Configuration */ }
202+ { /* Configuration Summary */ }
286203 < Box sx = { { mb : 3 } } >
287204 < Typography variant = "h6" sx = { { mb : 2 } } >
288- JSON Configuration Editor
205+ Server Configuration
289206 </ Typography >
290207 < Typography variant = "body2" color = "textSecondary" sx = { { mb : 2 } } >
291- Edit your MCP servers configuration as JSON. This allows you to easily add, remove,
292- and modify multiple servers at once .
208+ You have { mcpConfig . servers . length } server(s) configured.
209+ { mcpConfig . servers . filter ( s => s . enabled ) . length } server(s) are currently enabled .
293210 </ Typography >
294-
295- { jsonError && (
296- < Alert severity = "error" sx = { { mb : 2 } } >
297- { jsonError }
298- </ Alert >
299- ) }
300-
301- < TextField
302- label = "MCP Configuration JSON"
303- value = { jsonConfig }
304- onChange = { ( e ) => handleJsonConfigChange ( e . target . value ) }
305- multiline
306- rows = { 15 }
307- fullWidth
308- variant = "outlined"
309- sx = { {
310- mb : 2 ,
311- '& .MuiInputBase-input' : {
312- fontFamily : 'monospace' ,
313- fontSize : '0.875rem' ,
314- }
315- } }
316- helperText = "Edit the JSON configuration above. Make sure to keep the proper structure."
317- />
318-
319- < Box sx = { { display : 'flex' , gap : 2 , mb : 3 } } >
320- < Button
321- variant = "contained"
322- onClick = { validateAndApplyJsonConfig }
323- startIcon = { < Icon icon = "mdi:check" /> }
324- >
325- Apply Configuration
326- </ Button >
327- < Button
328- variant = "outlined"
329- onClick = { resetJsonToCurrentConfig }
330- startIcon = { < Icon icon = "mdi:refresh" /> }
331- >
332- Reset to Current
333- </ Button >
334- < Button
335- variant = "outlined"
336- onClick = { loadExampleConfig }
337- startIcon = { < Icon icon = "mdi:file-document" /> }
338- >
339- Load Example
340- </ Button >
341- </ Box >
342211
343- { /* Schema Documentation */ }
344- < Box sx = { { p : 2 , bgcolor : 'grey.50' , borderRadius : 1 , mb : 3 } } >
345- < Typography variant = "subtitle2" sx = { { mb : 2 } } >
346- 📝 Configuration Schema:
347- </ Typography >
348- < Typography variant = "body2" sx = { { fontFamily : 'monospace' , fontSize : '0.8rem' , mb : 1 } } >
349- { JSON . stringify ( {
350- "enabled" : "boolean - Enable/disable MCP servers" ,
351- "servers" : [
352- {
353- "name" : "string - Unique server name" ,
354- "command" : "string - Executable command" ,
355- "args" : [ "array of strings - Command arguments" ] ,
356- "enabled" : "boolean - Enable/disable this server"
357- }
358- ]
359- } , null , 2 ) }
212+ < Button
213+ variant = "contained"
214+ onClick = { handleOpenEditorDialog }
215+ startIcon = { < Icon icon = "mdi:pencil" /> }
216+ >
217+ Edit Configuration
218+ </ Button >
219+ </ Box >
220+
221+ { /* Server List Summary */ }
222+ { mcpConfig . servers . length > 0 && (
223+ < Box sx = { { mb : 3 } } >
224+ < Typography variant = "subtitle2" sx = { { mb : 1 } } >
225+ Configured Servers:
360226 </ Typography >
227+ < Box component = "ul" sx = { { pl : 2 } } >
228+ { mcpConfig . servers . map ( ( server , index ) => (
229+ < li key = { index } >
230+ < Typography variant = "body2" >
231+ < strong > { server . name } </ strong > ({ server . command } ) -
232+ < span style = { { color : server . enabled ? 'green' : 'red' , marginLeft : '8px' } } >
233+ { server . enabled ? 'Enabled' : 'Disabled' }
234+ </ span >
235+ { server . env && (
236+ < span style = { { marginLeft : '8px' , fontStyle : 'italic' } } >
237+ (with env variables)
238+ </ span >
239+ ) }
240+ </ Typography >
241+ </ li >
242+ ) ) }
243+ </ Box >
361244 </ Box >
245+ ) }
246+ </ >
247+ ) }
362248
363- </ Box >
364- </ > ) }
249+ { /* Editor Dialog */ }
250+ < MCPConfigEditorDialog
251+ open = { editorDialogOpen }
252+ onClose = { handleCloseEditorDialog }
253+ config = { mcpConfig }
254+ onSave = { handleSaveConfig }
255+ />
365256 </ SectionBox >
366257 ) ;
367258}
0 commit comments