Skip to content

Commit d3ae6dc

Browse files
committed
refactor: extract shared classifyBridgeError into core utility
De-duplicates nearly identical error classification logic from StandaloneXcodeToolsBridge and XcodeToolsBridgeManager into a single exported function in core.ts with an optional connected parameter.
1 parent 898819b commit d3ae6dc

3 files changed

Lines changed: 52 additions & 59 deletions

File tree

src/integrations/xcode-tools-bridge/core.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,3 +68,35 @@ export async function isXcodeRunning(): Promise<boolean | null> {
6868
return null;
6969
}
7070
}
71+
72+
export function classifyBridgeError(
73+
error: unknown,
74+
operation: 'list' | 'call',
75+
opts?: { connected?: boolean },
76+
): string {
77+
const message = (error instanceof Error ? error.message : String(error)).toLowerCase();
78+
79+
if (message.includes('mcpbridge not available')) {
80+
return 'MCPBRIDGE_NOT_FOUND';
81+
}
82+
if (message.includes('workflow is not enabled')) {
83+
return 'XCODE_MCP_UNAVAILABLE';
84+
}
85+
if (message.includes('timed out') || message.includes('timeout')) {
86+
if (opts?.connected === false) {
87+
return 'BRIDGE_CONNECT_TIMEOUT';
88+
}
89+
return operation === 'list' ? 'BRIDGE_LIST_TIMEOUT' : 'BRIDGE_CALL_TIMEOUT';
90+
}
91+
if (message.includes('permission') || message.includes('not allowed')) {
92+
return 'XCODE_APPROVAL_REQUIRED';
93+
}
94+
if (
95+
message.includes('connection closed') ||
96+
message.includes('closed') ||
97+
message.includes('disconnected')
98+
) {
99+
return 'XCODE_SESSION_NOT_READY';
100+
}
101+
return 'XCODE_MCP_UNAVAILABLE';
102+
}

src/integrations/xcode-tools-bridge/manager.ts

Lines changed: 13 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
import { XcodeToolsProxyRegistry, type ProxySyncResult } from './registry.ts';
1010
import {
1111
buildXcodeToolsBridgeStatus,
12+
classifyBridgeError,
1213
getMcpBridgeAvailability,
1314
type XcodeToolsBridgeStatus,
1415
} from './core.ts';
@@ -163,7 +164,12 @@ export class XcodeToolsBridgeManager {
163164
};
164165
return createTextResponse(JSON.stringify(payload, null, 2));
165166
} catch (error) {
166-
return this.createBridgeFailureResponse(this.classifyBridgeError(error, 'list'), error);
167+
return this.createBridgeFailureResponse(
168+
classifyBridgeError(error, 'list', {
169+
connected: this.service.getClientStatus().connected,
170+
}),
171+
error,
172+
);
167173
}
168174
}
169175

@@ -185,7 +191,12 @@ export class XcodeToolsBridgeManager {
185191
});
186192
return response as ToolResponse;
187193
} catch (error) {
188-
return this.createBridgeFailureResponse(this.classifyBridgeError(error, 'call'), error);
194+
return this.createBridgeFailureResponse(
195+
classifyBridgeError(error, 'call', {
196+
connected: this.service.getClientStatus().connected,
197+
}),
198+
error,
199+
);
189200
}
190201
}
191202

@@ -204,33 +215,4 @@ export class XcodeToolsBridgeManager {
204215
const message = error instanceof Error ? error.message : String(error);
205216
return createErrorResponse(code, message);
206217
}
207-
208-
private classifyBridgeError(error: unknown, operation: 'list' | 'call'): string {
209-
const message = (error instanceof Error ? error.message : String(error)).toLowerCase();
210-
const clientStatus = this.service.getClientStatus();
211-
212-
if (message.includes('mcpbridge not available')) {
213-
return 'MCPBRIDGE_NOT_FOUND';
214-
}
215-
if (message.includes('workflow is not enabled')) {
216-
return 'XCODE_MCP_UNAVAILABLE';
217-
}
218-
if (message.includes('timed out') || message.includes('timeout')) {
219-
if (!clientStatus.connected) {
220-
return 'BRIDGE_CONNECT_TIMEOUT';
221-
}
222-
return operation === 'list' ? 'BRIDGE_LIST_TIMEOUT' : 'BRIDGE_CALL_TIMEOUT';
223-
}
224-
if (message.includes('permission') || message.includes('not allowed')) {
225-
return 'XCODE_APPROVAL_REQUIRED';
226-
}
227-
if (
228-
message.includes('connection closed') ||
229-
message.includes('closed') ||
230-
message.includes('disconnected')
231-
) {
232-
return 'XCODE_SESSION_NOT_READY';
233-
}
234-
return 'XCODE_MCP_UNAVAILABLE';
235-
}
236218
}

src/integrations/xcode-tools-bridge/standalone.ts

Lines changed: 7 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,11 @@ import {
33
createTextResponse,
44
type ToolResponse,
55
} from '../../utils/responses/index.ts';
6-
import { buildXcodeToolsBridgeStatus, type XcodeToolsBridgeStatus } from './core.ts';
6+
import {
7+
buildXcodeToolsBridgeStatus,
8+
classifyBridgeError,
9+
type XcodeToolsBridgeStatus,
10+
} from './core.ts';
711
import { XcodeIdeToolService } from './tool-service.ts';
812

913
export class StandaloneXcodeToolsBridge {
@@ -78,7 +82,7 @@ export class StandaloneXcodeToolsBridge {
7882
);
7983
} catch (error) {
8084
const message = error instanceof Error ? error.message : String(error);
81-
return createErrorResponse(this.classifyBridgeError(error, 'list'), message);
85+
return createErrorResponse(classifyBridgeError(error, 'list'), message);
8286
}
8387
}
8488

@@ -94,32 +98,7 @@ export class StandaloneXcodeToolsBridge {
9498
return response as ToolResponse;
9599
} catch (error) {
96100
const message = error instanceof Error ? error.message : String(error);
97-
return createErrorResponse(this.classifyBridgeError(error, 'call'), message);
98-
}
99-
}
100-
101-
private classifyBridgeError(error: unknown, operation: 'list' | 'call'): string {
102-
const message = (error instanceof Error ? error.message : String(error)).toLowerCase();
103-
104-
if (message.includes('mcpbridge not available')) {
105-
return 'MCPBRIDGE_NOT_FOUND';
106-
}
107-
if (message.includes('workflow is not enabled')) {
108-
return 'XCODE_MCP_UNAVAILABLE';
109-
}
110-
if (message.includes('timed out') || message.includes('timeout')) {
111-
return operation === 'list' ? 'BRIDGE_LIST_TIMEOUT' : 'BRIDGE_CALL_TIMEOUT';
112-
}
113-
if (message.includes('permission') || message.includes('not allowed')) {
114-
return 'XCODE_APPROVAL_REQUIRED';
115-
}
116-
if (
117-
message.includes('connection closed') ||
118-
message.includes('closed') ||
119-
message.includes('disconnected')
120-
) {
121-
return 'XCODE_SESSION_NOT_READY';
101+
return createErrorResponse(classifyBridgeError(error, 'call'), message);
122102
}
123-
return 'XCODE_MCP_UNAVAILABLE';
124103
}
125104
}

0 commit comments

Comments
 (0)