@aiwerk/mcp-bridge 2.6.2 → 2.6.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -41,4 +41,4 @@ export interface DeviceCodeConfig {
41
41
  * 2. Display user_code and verification_uri to the user
42
42
  * 3. Poll tokenUrl until the user authorizes or the code expires
43
43
  */
44
- export declare function performDeviceCodeLogin(serverName: string, config: DeviceCodeConfig, logger: Logger): Promise<StoredToken>;
44
+ export declare function performDeviceCodeLogin(serverName: string, config: DeviceCodeConfig, logger: Logger, signal?: AbortSignal): Promise<StoredToken>;
@@ -174,7 +174,7 @@ const SLOW_DOWN_INCREMENT_S = 5;
174
174
  * 2. Display user_code and verification_uri to the user
175
175
  * 3. Poll tokenUrl until the user authorizes or the code expires
176
176
  */
177
- export async function performDeviceCodeLogin(serverName, config, logger) {
177
+ export async function performDeviceCodeLogin(serverName, config, logger, signal) {
178
178
  // Step 1: Request device code
179
179
  const formData = new URLSearchParams();
180
180
  formData.set("client_id", config.clientId);
@@ -221,7 +221,13 @@ export async function performDeviceCodeLogin(serverName, config, logger) {
221
221
  // Step 3: Poll for token
222
222
  const deadline = Date.now() + expiresInS * 1000;
223
223
  while (Date.now() < deadline) {
224
+ if (signal?.aborted) {
225
+ throw new Error("Device code login aborted");
226
+ }
224
227
  await sleep(intervalS * 1000);
228
+ if (signal?.aborted) {
229
+ throw new Error("Device code login aborted");
230
+ }
225
231
  const tokenForm = new URLSearchParams();
226
232
  tokenForm.set("grant_type", "urn:ietf:params:oauth:grant-type:device_code");
227
233
  tokenForm.set("device_code", deviceCode);
@@ -232,6 +238,7 @@ export async function performDeviceCodeLogin(serverName, config, logger) {
232
238
  method: "POST",
233
239
  headers: { "Content-Type": "application/x-www-form-urlencoded" },
234
240
  body: tokenForm.toString(),
241
+ signal,
235
242
  });
236
243
  // Guard against non-JSON responses (e.g. 500 HTML error pages)
237
244
  const contentType = tokenResponse.headers.get("content-type") || "";
@@ -41,9 +41,12 @@ export declare class OAuth2TokenManager {
41
41
  * Checks TokenStore, refreshes if expired, throws if unavailable.
42
42
  */
43
43
  getTokenForDeviceCode(serverName: string, config: DeviceCodeOAuth2Config): Promise<string>;
44
- private doDeviceCodeRefresh;
44
+ /**
45
+ * Shared refresh logic for both auth_code and device_code flows.
46
+ * Takes a refreshFn that performs the actual token exchange.
47
+ */
48
+ private doTokenRefresh;
45
49
  private refreshDeviceCodeToken;
46
- private doAuthCodeRefresh;
47
50
  private refreshAuthCodeToken;
48
51
  private makeKey;
49
52
  private fetchToken;
@@ -66,7 +66,7 @@ export class OAuth2TokenManager {
66
66
  if (existingInflight) {
67
67
  return existingInflight;
68
68
  }
69
- const refreshPromise = this.doAuthCodeRefresh(serverName, stored, config);
69
+ const refreshPromise = this.doTokenRefresh(serverName, stored, (s) => this.refreshAuthCodeToken(s, config), "Auth code");
70
70
  this.tokenRefreshInflight.set(serverName, refreshPromise);
71
71
  try {
72
72
  return await refreshPromise;
@@ -98,7 +98,7 @@ export class OAuth2TokenManager {
98
98
  if (existingInflight) {
99
99
  return existingInflight;
100
100
  }
101
- const refreshPromise = this.doDeviceCodeRefresh(serverName, stored, config);
101
+ const refreshPromise = this.doTokenRefresh(serverName, stored, (s) => this.refreshDeviceCodeToken(s, config), "Device code");
102
102
  this.tokenRefreshInflight.set(serverName, refreshPromise);
103
103
  try {
104
104
  return await refreshPromise;
@@ -107,15 +107,19 @@ export class OAuth2TokenManager {
107
107
  this.tokenRefreshInflight.delete(serverName);
108
108
  }
109
109
  }
110
- async doDeviceCodeRefresh(serverName, stored, config) {
110
+ /**
111
+ * Shared refresh logic for both auth_code and device_code flows.
112
+ * Takes a refreshFn that performs the actual token exchange.
113
+ */
114
+ async doTokenRefresh(serverName, stored, refreshFn, flowName) {
111
115
  if (stored.refreshToken) {
112
116
  try {
113
- const refreshed = await this.refreshDeviceCodeToken(stored, config);
117
+ const refreshed = await refreshFn(stored);
114
118
  this.tokenStore.save(serverName, refreshed);
115
119
  return refreshed.accessToken;
116
120
  }
117
121
  catch (err) {
118
- this.logger.warn("[mcp-bridge] Device code token refresh failed:", err);
122
+ this.logger.warn(`[mcp-bridge] ${flowName} token refresh failed:`, err);
119
123
  }
120
124
  }
121
125
  // Refresh failed or no refresh token
@@ -158,23 +162,6 @@ export class OAuth2TokenManager {
158
162
  scopes: config.scopes,
159
163
  };
160
164
  }
161
- async doAuthCodeRefresh(serverName, stored, config) {
162
- if (stored.refreshToken) {
163
- try {
164
- const refreshed = await this.refreshAuthCodeToken(stored, config);
165
- this.tokenStore.save(serverName, refreshed);
166
- return refreshed.accessToken;
167
- }
168
- catch (err) {
169
- this.logger.warn("[mcp-bridge] Auth code token refresh failed:", err);
170
- }
171
- }
172
- // Refresh failed or no refresh token
173
- this.tokenStore.remove(serverName);
174
- const error = new Error(`Authentication expired for server "${serverName}". Run: mcp-bridge auth login ${serverName}`);
175
- error.code = -32006;
176
- throw error;
177
- }
178
165
  async refreshAuthCodeToken(stored, config) {
179
166
  const formData = new URLSearchParams();
180
167
  formData.set("grant_type", "refresh_token");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiwerk/mcp-bridge",
3
- "version": "2.6.2",
3
+ "version": "2.6.3",
4
4
  "description": "Standalone MCP server that multiplexes multiple MCP servers into one interface",
5
5
  "type": "module",
6
6
  "main": "./dist/src/index.js",