Skip to content

Commit 80045c6

Browse files
fix: avoid synthetic connecting state for enabled servers (#196)
Signed-off-by: Mohammod Al Amin Ashik <maa.ashik00@gmail.com> Co-authored-by: its-mash <maa.ashik00@gmail.com>
1 parent 187b57c commit 80045c6

2 files changed

Lines changed: 35 additions & 21 deletions

File tree

apps/desktop/src/features/servers/ServersPage.tsx

Lines changed: 32 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,10 @@ function mergeDefinitionsWithStates(
6262
(input: InputDefinition) => input.required && !inputValues[input.id]
6363
);
6464

65-
// Calculate initial connection_status based on enabled state
66-
// Calculate initial connection_status based on enabled state
67-
// Actual runtime status comes from ServerManager events via useServerManager hook
68-
const connection_status = state?.enabled ? 'connecting' : 'disconnected';
65+
// Runtime status is in-memory only and comes from ServerManager.
66+
// Do not infer `connecting` from persisted `enabled`; custom/offline servers can
67+
// otherwise stay stuck in a synthetic Connecting state forever.
68+
const connection_status = 'disconnected';
6969

7070
return {
7171
...def,
@@ -95,11 +95,7 @@ function createOfflineServerViewModel(state: InstalledServerState): ServerViewMo
9595
const inputValues = state.input_values;
9696
const requiredInputs = definition.transport.metadata?.inputs?.filter((i) => i.required) || [];
9797
const missing_required_inputs = requiredInputs.some((input) => !inputValues[input.id]);
98-
const connection_status = state.enabled
99-
? missing_required_inputs
100-
? 'error'
101-
: 'connecting'
102-
: 'disconnected';
98+
const connection_status = 'disconnected';
10399

104100
return {
105101
...definition,
@@ -143,7 +139,7 @@ function createOfflineServerViewModel(state: InstalledServerState): ServerViewMo
143139
enabled: state.enabled,
144140
oauth_connected: state.oauth_connected,
145141
input_values: state.input_values,
146-
connection_status: state.enabled ? 'connecting' : 'disconnected',
142+
connection_status: 'disconnected',
147143
missing_required_inputs: false,
148144
last_error: null,
149145
created_at: state.created_at,
@@ -371,7 +367,8 @@ export function ServersPage() {
371367
}
372368

373369
// Apply runtime statuses from ServerManager to fix initial connection_status
374-
// (mergeDefinitionsWithStates hardcodes 'connecting' for enabled servers)
370+
// (the view-model builders seed 'disconnected'; ServerManager owns the real
371+
// runtime status, which arrives via events)
375372
const mapStatus = (s: ConnectionStatus): ServerViewModel['connection_status'] => {
376373
if (s === 'refreshing' || s === 'authenticating') return 'connecting';
377374
return s;
@@ -450,6 +447,7 @@ export function ServersPage() {
450447
* - 'running': Server is connected and running
451448
* - 'error': Server has an error
452449
* - 'connected_auto': Non-OAuth server that's connected (no action buttons needed)
450+
* - 'disconnected': Server is enabled but has no active runtime connection
453451
*/
454452
const getServerAction = (
455453
server: ServerViewModel
@@ -461,7 +459,8 @@ export function ServersPage() {
461459
| 'auth_required'
462460
| 'running'
463461
| 'error'
464-
| 'connected_auto' => {
462+
| 'connected_auto'
463+
| 'disconnected' => {
465464
if (!server.enabled) {
466465
return 'enable';
467466
}
@@ -489,8 +488,7 @@ export function ServersPage() {
489488
case 'error':
490489
return 'error';
491490
case 'disconnected':
492-
// Enabled but disconnected - try to connect
493-
return server.auth?.type === 'oauth' ? 'auth_required' : 'connected_auto';
491+
return server.auth?.type === 'oauth' ? 'auth_required' : 'disconnected';
494492
}
495493
}
496494

@@ -515,8 +513,8 @@ export function ServersPage() {
515513
return 'auth_required';
516514
}
517515

518-
// Non-OAuth server that's enabled but not yet connected
519-
return 'connected_auto';
516+
// Enabled but no runtime connection exists yet. Let the user start/retry it.
517+
return 'disconnected';
520518
};
521519

522520
// Get display status for UI
@@ -544,6 +542,8 @@ export function ServersPage() {
544542
return 'Connected';
545543
case 'connected_auto':
546544
return 'Connected';
545+
case 'disconnected':
546+
return 'Disconnected';
547547
case 'error':
548548
return 'Error';
549549
}
@@ -1228,6 +1228,16 @@ export function ServersPage() {
12281228
</button>
12291229
)}
12301230

1231+
{serverAction === 'disconnected' && gatewayRunning && (
1232+
<button
1233+
onClick={() => handleRetry(server)}
1234+
disabled={retryLoading}
1235+
className="rounded-lg bg-[rgb(var(--success))] px-4 py-2 text-sm font-medium text-white shadow-sm transition-colors hover:bg-[rgb(var(--success))]/80 disabled:opacity-50"
1236+
>
1237+
{retryLoading ? 'Connecting...' : 'Connect'}
1238+
</button>
1239+
)}
1240+
12311241
{serverAction === 'error' && gatewayRunning && (
12321242
<button
12331243
onClick={() => handleRetry(server)}
@@ -1242,9 +1252,13 @@ export function ServersPage() {
12421252
</button>
12431253
)}
12441254

1245-
{/* Disable button - shown when enabled and connected/running */}
1255+
{/* Disable button - shown when an enabled server is connected,
1256+
running, or sitting disconnected (so it can still be turned off
1257+
without first reconnecting) */}
12461258
{server.enabled &&
1247-
(serverAction === 'running' || serverAction === 'connected_auto') && (
1259+
(serverAction === 'running' ||
1260+
serverAction === 'connected_auto' ||
1261+
serverAction === 'disconnected') && (
12481262
<button
12491263
onClick={() => handleDisableClick(server)}
12501264
disabled={disableLoading}
@@ -1756,7 +1770,6 @@ export function ServersPage() {
17561770
</div>
17571771
</div>
17581772
)}
1759-
17601773
</div>
17611774

17621775
{/* Pinned footer — always visible regardless of form length (#163) */}

apps/desktop/src/stores/registryStore.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -363,8 +363,9 @@ function mergeServers(defs: ServerDefinition[], states: InstalledServerState[]):
363363
input.required && !inputValues[input.id]
364364
);
365365

366-
// Calculate initial connection_status based on enabled state
367-
const connection_status = state?.enabled ? 'connecting' : 'disconnected';
366+
// Runtime connection status is not persisted. Do not infer Connecting from
367+
// the enabled flag; the ServerManager event/status stream owns that state.
368+
const connection_status = 'disconnected';
368369

369370
return {
370371
...def,

0 commit comments

Comments
 (0)