@@ -65,6 +65,8 @@ function mergeDefinitionsWithStates(
6565 last_error : null , // Runtime-only, will be set by ServerManager events
6666 created_at : state ?. created_at , // Include for sorting
6767 installation_source : state ?. source , // Track how server was installed
68+ env_overrides : state ?. env_overrides ?? { } ,
69+ args_append : state ?. args_append ?? [ ] ,
6870 } as ServerViewModel ;
6971 } ) ;
7072}
@@ -96,6 +98,8 @@ function createOfflineServerViewModel(state: InstalledServerState): ServerViewMo
9698 last_error : null ,
9799 created_at : state . created_at ,
98100 installation_source : state . source ,
101+ env_overrides : state . env_overrides ?? { } ,
102+ args_append : state . args_append ?? [ ] ,
99103 } as ServerViewModel ;
100104 } catch ( e ) {
101105 console . warn ( '[ServersPage] Failed to parse cached_definition, using minimal fallback:' , e ) ;
@@ -129,6 +133,8 @@ function createOfflineServerViewModel(state: InstalledServerState): ServerViewMo
129133 last_error : null ,
130134 created_at : state . created_at ,
131135 installation_source : state . source ,
136+ env_overrides : state . env_overrides ?? { } ,
137+ args_append : state . args_append ?? [ ] ,
132138 } as ServerViewModel ;
133139}
134140
@@ -139,6 +145,10 @@ interface ConfigModalState {
139145 inputValues : Record < string , string > ;
140146 /** If true, saving will also enable the server (from Enable flow) */
141147 enableOnSave ?: boolean ;
148+ /** Additional environment variable overrides */
149+ envOverrides : Record < string , string > ;
150+ /** Additional arguments to append (stdio only) */
151+ argsAppend : string [ ] ;
142152}
143153
144154export function ServersPage ( ) {
@@ -153,6 +163,8 @@ export function ServersPage() {
153163 open : false ,
154164 server : null ,
155165 inputValues : { } ,
166+ envOverrides : { } ,
167+ argsAppend : [ ] ,
156168 } ) ;
157169
158170 // Features state
@@ -488,6 +500,8 @@ export function ServersPage() {
488500 server,
489501 inputValues : initialValues ,
490502 enableOnSave : true , // This is from Enable flow
503+ envOverrides : { ...( server . env_overrides ?? { } ) } ,
504+ argsAppend : [ ...( server . args_append ?? [ ] ) ] ,
491505 } ) ;
492506 return ;
493507 }
@@ -549,6 +563,8 @@ export function ServersPage() {
549563 server,
550564 inputValues : initialValues ,
551565 enableOnSave : false , // Just configure, don't enable
566+ envOverrides : { ...( server . env_overrides ?? { } ) } ,
567+ argsAppend : [ ...( server . args_append ?? [ ] ) ] ,
552568 } ) ;
553569 } ;
554570
@@ -562,11 +578,17 @@ export function ServersPage() {
562578 setActionLoading ( `config-${ serverId } ` ) ;
563579 try {
564580 const { saveServerInputs } = await import ( '@/lib/api/registry' ) ;
565-
566- // Save input values
567- await saveServerInputs ( serverId , configModal . inputValues , viewSpace ?. id ?? '' ) ;
568-
569- setConfigModal ( { open : false , server : null , inputValues : { } } ) ;
581+
582+ // Save input values with env overrides and args
583+ await saveServerInputs (
584+ serverId ,
585+ configModal . inputValues ,
586+ viewSpace ?. id ?? '' ,
587+ Object . keys ( configModal . envOverrides ) . length > 0 ? configModal . envOverrides : undefined ,
588+ configModal . argsAppend . length > 0 ? configModal . argsAppend : undefined ,
589+ ) ;
590+
591+ setConfigModal ( { open : false , server : null , inputValues : { } , envOverrides : { } , argsAppend : [ ] } ) ;
570592
571593 // Only enable if requested (from Enable flow)
572594 if ( shouldEnable && ! server . enabled ) {
@@ -604,7 +626,7 @@ export function ServersPage() {
604626 // Set the server to pending_config state by enabling but not connecting
605627 // Actually, we just close the modal - the UI already shows Configure button for missing inputs
606628 }
607- setConfigModal ( { open : false , server : null , inputValues : { } } ) ;
629+ setConfigModal ( { open : false , server : null , inputValues : { } , envOverrides : { } , argsAppend : [ ] } ) ;
608630 } ;
609631
610632 // Cancel OAuth flow - uses new ServerManager v2
@@ -1311,7 +1333,106 @@ export function ServersPage() {
13111333 </ div >
13121334 ) ;
13131335 } ) }
1314-
1336+
1337+ { /* Additional Arguments (stdio only) */ }
1338+ { configModal . server . transport . type === 'stdio' && (
1339+ < div >
1340+ < label className = "block text-sm font-medium text-[rgb(var(--foreground))] mb-1" >
1341+ Additional Arguments
1342+ </ label >
1343+ < p className = "text-xs text-[rgb(var(--muted))] mb-2" >
1344+ Extra command-line arguments (one per line)
1345+ </ p >
1346+ < textarea
1347+ value = { configModal . argsAppend . join ( '\n' ) }
1348+ onChange = { ( e ) => {
1349+ const lines = e . target . value . split ( '\n' ) ;
1350+ setConfigModal ( {
1351+ ...configModal ,
1352+ argsAppend : lines . filter ( ( l ) => l . length > 0 || e . target . value . endsWith ( '\n' ) ) ,
1353+ } ) ;
1354+ } }
1355+ onBlur = { ( e ) => {
1356+ // Clean up empty lines on blur
1357+ setConfigModal ( {
1358+ ...configModal ,
1359+ argsAppend : e . target . value . split ( '\n' ) . filter ( ( l ) => l . trim ( ) . length > 0 ) ,
1360+ } ) ;
1361+ } }
1362+ placeholder = "--flag value"
1363+ rows = { 3 }
1364+ className = "input w-full font-mono text-sm resize-y"
1365+ data-testid = "config-args-append"
1366+ />
1367+ </ div >
1368+ ) }
1369+
1370+ { /* Environment Variable Overrides */ }
1371+ < div >
1372+ < label className = "block text-sm font-medium text-[rgb(var(--foreground))] mb-1" >
1373+ Environment Variables
1374+ </ label >
1375+ < p className = "text-xs text-[rgb(var(--muted))] mb-2" >
1376+ { configModal . server . transport . type === 'stdio'
1377+ ? 'Additional environment variables for the server process'
1378+ : 'Additional environment variables' }
1379+ </ p >
1380+ < div className = "space-y-2" >
1381+ { Object . entries ( configModal . envOverrides ) . map ( ( [ key , value ] , idx ) => (
1382+ < div key = { idx } className = "flex gap-2" >
1383+ < input
1384+ type = "text"
1385+ value = { key }
1386+ onChange = { ( e ) => {
1387+ const entries = Object . entries ( configModal . envOverrides ) ;
1388+ entries [ idx ] = [ e . target . value , value ] ;
1389+ setConfigModal ( {
1390+ ...configModal ,
1391+ envOverrides : Object . fromEntries ( entries ) ,
1392+ } ) ;
1393+ } }
1394+ placeholder = "KEY"
1395+ className = "input flex-1 font-mono text-sm"
1396+ />
1397+ < input
1398+ type = "text"
1399+ value = { value }
1400+ onChange = { ( e ) => {
1401+ setConfigModal ( {
1402+ ...configModal ,
1403+ envOverrides : { ...configModal . envOverrides , [ key ] : e . target . value } ,
1404+ } ) ;
1405+ } }
1406+ placeholder = "value"
1407+ className = "input flex-1 font-mono text-sm"
1408+ />
1409+ < button
1410+ onClick = { ( ) => {
1411+ const { [ key ] : _ , ...rest } = configModal . envOverrides ;
1412+ setConfigModal ( { ...configModal , envOverrides : rest } ) ;
1413+ } }
1414+ className = "px-2 py-1 text-sm text-[rgb(var(--muted))] hover:text-[rgb(var(--error))] transition-colors"
1415+ title = "Remove"
1416+ >
1417+ ✕
1418+ </ button >
1419+ </ div >
1420+ ) ) }
1421+ < button
1422+ onClick = { ( ) => {
1423+ setConfigModal ( {
1424+ ...configModal ,
1425+ envOverrides : { ...configModal . envOverrides , '' : '' } ,
1426+ } ) ;
1427+ } }
1428+ className = "text-xs text-[rgb(var(--primary))] hover:underline"
1429+ data-testid = "config-add-env"
1430+ >
1431+ + Add variable
1432+ </ button >
1433+ </ div >
1434+ </ div >
1435+
13151436 < div className = "flex justify-end gap-2 pt-2" >
13161437 < button
13171438 onClick = { handleCancelConfig }
0 commit comments