Skip to content

Commit 55c38c0

Browse files
author
Lasim
committed
refactor(gateway): streamline device registration during OAuth2 flow
1 parent 74550da commit 55c38c0

File tree

5 files changed

+133
-9
lines changed

5 files changed

+133
-9
lines changed

services/gateway/src/commands/login.ts

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,11 +69,21 @@ export function registerLoginCommand(program: Command) {
6969
// Detect current device information
7070
const deviceInfo = await detectDeviceInfo();
7171

72-
// Register or update device with backend
73-
const device = await api.registerOrUpdateDevice(deviceInfo);
72+
// Device registration already happened during OAuth2 token exchange
73+
// No separate device API call needed - just use hardware_id directly
7474

75-
// Download merged configurations using the new gateway endpoint
76-
const gatewayConfig = await mcpService.downloadGatewayMCPConfig(device.id, api, false);
75+
// Download merged configurations using the new gateway endpoint with hardware_id
76+
const gatewayConfig = await mcpService.downloadGatewayMCPConfig(deviceInfo.hardware_id, api, false);
77+
78+
// Convert and store the gateway config in the format expected by local storage
79+
const teamMCPConfig = mcpService.convertGatewayConfigToTeamConfig(
80+
defaultTeam.id,
81+
defaultTeam.name,
82+
gatewayConfig
83+
);
84+
85+
// Store the converted configuration
86+
await mcpService.storeMCPConfig(teamMCPConfig);
7787

7888
// Auto-start the gateway server after successful MCP config download
7989
spinner.text = 'Starting gateway server...';

services/gateway/src/core/auth/api-client.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -302,11 +302,14 @@ export class DeployStackAPI {
302302
}
303303

304304
const contentType = response.headers.get('content-type');
305+
let responseData;
305306
if (contentType && contentType.includes('application/json')) {
306-
return await response.json();
307+
responseData = await response.json();
307308
} else {
308-
return await response.text();
309+
responseData = await response.text();
309310
}
311+
312+
return responseData;
310313
} catch (error) {
311314
if (error instanceof AuthenticationError) {
312315
throw error;

services/gateway/src/core/mcp/config-service.ts

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,4 +289,104 @@ export class MCPConfigService {
289289
lastUpdated: config.last_updated
290290
};
291291
}
292+
293+
/**
294+
* Convert gateway configuration to team configuration for local storage
295+
* @param teamId Team ID
296+
* @param teamName Team name
297+
* @param gatewayConfig Gateway configuration from new API
298+
* @returns Team MCP configuration for storage
299+
*/
300+
convertGatewayConfigToTeamConfig(
301+
teamId: string,
302+
teamName: string,
303+
gatewayConfig: {
304+
servers: Array<{
305+
id: string;
306+
name: string;
307+
command: string;
308+
args: string[];
309+
env: Record<string, string>;
310+
status: 'ready' | 'invalid';
311+
}>;
312+
deviceId: string;
313+
lastUpdated: string;
314+
}
315+
): TeamMCPConfig {
316+
// Convert gateway servers to TeamMCPConfig format
317+
const servers = gatewayConfig.servers.map(server => ({
318+
id: server.id,
319+
name: server.name,
320+
installation_name: server.name,
321+
command: server.command,
322+
args: server.args,
323+
env: server.env,
324+
runtime: this.detectRuntime(server.command, server.args),
325+
installation_type: 'local' as const,
326+
transport_type: 'stdio' as const,
327+
status: server.status
328+
}));
329+
330+
// Create mock installations for compatibility (since gateway endpoint doesn't return installations)
331+
const installations = gatewayConfig.servers.map(server => ({
332+
id: server.id,
333+
team_id: teamId,
334+
server_id: server.id,
335+
created_by: 'gateway-endpoint',
336+
installation_name: server.name,
337+
installation_type: 'local' as const,
338+
team_args: server.args,
339+
team_env: server.env,
340+
created_at: gatewayConfig.lastUpdated,
341+
updated_at: gatewayConfig.lastUpdated,
342+
last_used_at: null,
343+
server: {
344+
id: server.id,
345+
name: server.name,
346+
description: `MCP server: ${server.name}`,
347+
github_url: null,
348+
runtime: this.detectRuntime(server.command, server.args),
349+
installation_methods: [{
350+
type: 'command' as const,
351+
command: server.command,
352+
args: server.args
353+
}],
354+
environment_variables: [],
355+
transport_type: 'stdio' as const
356+
}
357+
}));
358+
359+
return {
360+
team_id: teamId,
361+
team_name: teamName,
362+
installations,
363+
user_configurations: [], // Empty since gateway endpoint handles user configs internally
364+
servers,
365+
last_updated: gatewayConfig.lastUpdated
366+
};
367+
}
368+
369+
/**
370+
* Store MCP configuration (wrapper around storage)
371+
* @param config Team MCP configuration to store
372+
*/
373+
async storeMCPConfig(config: TeamMCPConfig): Promise<void> {
374+
await this.storage.storeMCPConfig(config);
375+
}
376+
377+
/**
378+
* Detect runtime from command and args
379+
* @param command Command string
380+
* @param _args Command arguments
381+
* @returns Detected runtime
382+
*/
383+
private detectRuntime(command: string, _args: string[]): 'nodejs' | 'python' | 'binary' {
384+
if (command === 'npx' || command === 'node') {
385+
return 'nodejs';
386+
}
387+
if (command === 'python' || command === 'python3' || command === 'pip') {
388+
return 'python';
389+
}
390+
return 'binary';
391+
}
292392
}

services/gateway/src/services/refresh-service.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,16 @@ export class RefreshService {
6969
// Backend will automatically find device by hardware_id
7070
const gatewayConfig = await this.mcpService.downloadGatewayMCPConfig(deviceInfo.hardware_id, api, false);
7171

72+
// Convert and store the gateway config in the format expected by local storage
73+
const teamMCPConfig = this.mcpService.convertGatewayConfigToTeamConfig(
74+
credentials.selectedTeam.id,
75+
credentials.selectedTeam.name,
76+
gatewayConfig
77+
);
78+
79+
// Store the converted configuration
80+
await this.mcpService.storeMCPConfig(teamMCPConfig);
81+
7282
const readyServers = gatewayConfig.servers.filter(s => s.status === 'ready');
7383
const invalidServers = gatewayConfig.servers.filter(s => s.status === 'invalid');
7484

services/gateway/src/types/mcp.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,14 +63,15 @@ export interface MCPUserConfigurationsResponse {
6363
// Processed MCP Server Config for Gateway
6464
export interface MCPServerConfig {
6565
id: string;
66-
name: string;
66+
name?: string;
6767
installation_name: string;
6868
command: string;
6969
args: string[];
7070
env: Record<string, string>;
7171
runtime: string;
72-
installation_type: 'local' | 'cloud';
73-
transport_type: 'stdio' | 'http' | 'sse';
72+
installation_type?: 'local' | 'cloud';
73+
transport_type?: 'stdio' | 'http' | 'sse';
74+
status?: 'ready' | 'invalid';
7475
}
7576

7677
// Team MCP Configuration stored securely (Three-tier architecture)

0 commit comments

Comments
 (0)