figma-console-mcp 1.17.2 → 1.17.4

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.
package/README.md CHANGED
@@ -51,9 +51,9 @@ Figma Console MCP connects AI assistants (like Claude) to Figma, enabling:
51
51
  | Real-time monitoring (console, selection) | ✅ | ❌ | ❌ |
52
52
  | Desktop Bridge plugin | ✅ | ✅ | ❌ |
53
53
  | Requires Node.js | Yes | **No** | No |
54
- | **Total tools available** | **78+** | **43** | **22** |
54
+ | **Total tools available** | **84+** | **43** | **22** |
55
55
 
56
- > **Bottom line:** Remote SSE is **read-only** with ~38% of the tools. **Cloud Mode** unlocks write access from web AI clients without Node.js. NPX/Local Git gives the full 78+ tools with real-time monitoring.
56
+ > **Bottom line:** Remote SSE is **read-only** with ~38% of the tools. **Cloud Mode** unlocks write access from web AI clients without Node.js. NPX/Local Git gives the full 84+ tools with real-time monitoring.
57
57
 
58
58
  ---
59
59
 
@@ -61,7 +61,7 @@ Figma Console MCP connects AI assistants (like Claude) to Figma, enabling:
61
61
 
62
62
  **Best for:** Designers who want full AI-assisted design capabilities.
63
63
 
64
- **What you get:** All 78+ tools including design creation, variable management, and component instantiation.
64
+ **What you get:** All 84+ tools including design creation, variable management, and component instantiation.
65
65
 
66
66
  #### Prerequisites
67
67
 
@@ -74,7 +74,8 @@ Figma Console MCP connects AI assistants (like Claude) to Figma, enabling:
74
74
  1. Go to [Manage personal access tokens](https://help.figma.com/hc/en-us/articles/8085703771159-Manage-personal-access-tokens) in Figma Help
75
75
  2. Follow the steps to **create a new personal access token**
76
76
  3. Enter description: `Figma Console MCP`
77
- 4. **Copy the token** you won't see it again! (starts with `figd_`)
77
+ 4. Set scopes: **File content** (Read), **Variables** (Read), **Comments** (Read and write)
78
+ 5. **Copy the token** — you won't see it again! (starts with `figd_`)
78
79
 
79
80
  #### Step 2: Configure Your MCP Client
80
81
 
@@ -155,7 +156,7 @@ Create a simple frame with a blue background
155
156
 
156
157
  **Best for:** Developers who want to modify source code or contribute to the project.
157
158
 
158
- **What you get:** Same 78+ tools as NPX, plus full source code access.
159
+ **What you get:** Same 84+ tools as NPX, plus full source code access.
159
160
 
160
161
  #### Quick Setup
161
162
 
@@ -244,7 +245,7 @@ Ready for design creation? Follow the [NPX Setup](#-npx-setup-recommended) guide
244
245
 
245
246
  **Best for:** Using Claude.ai, v0, Replit, or Lovable to create and modify Figma designs — no Node.js required.
246
247
 
247
- **What you get:** 52 tools including full write access — design creation, variable management, component instantiation, and all REST API tools. Only real-time monitoring (console logs, selection tracking, document changes) requires Local Mode.
248
+ **What you get:** 76 tools including full write access — design creation, variable management, component instantiation, and all REST API tools. Only real-time monitoring (console logs, selection tracking, document changes) requires Local Mode.
248
249
 
249
250
  #### Prerequisites
250
251
 
@@ -301,7 +302,7 @@ AI Client → Cloud MCP Server → Durable Object Relay → Desktop Bridge Plugi
301
302
  | Feature | NPX (Recommended) | Cloud Mode | Local Git | Remote SSE |
302
303
  |---------|-------------------|------------|-----------|------------|
303
304
  | **Setup time** | ~10 minutes | ~5 minutes | ~15 minutes | ~2 minutes |
304
- | **Total tools** | **78+** | **43** | **78+** | **22** (read-only) |
305
+ | **Total tools** | **84+** | **43** | **84+** | **22** (read-only) |
305
306
  | **Design creation** | ✅ | ✅ | ✅ | ❌ |
306
307
  | **Variable management** | ✅ | ✅ | ✅ | ❌ |
307
308
  | **Component instantiation** | ✅ | ✅ | ✅ | ❌ |
@@ -316,7 +317,7 @@ AI Client → Cloud MCP Server → Durable Object Relay → Desktop Bridge Plugi
316
317
  | **Automatic updates** | ✅ (`@latest`) | ✅ | Manual (`git pull`) | ✅ |
317
318
  | **Source code access** | ❌ | ❌ | ✅ | ❌ |
318
319
 
319
- > **Key insight:** Remote SSE is read-only. Cloud Mode adds write access for web AI clients without Node.js. NPX/Local Git give the full 78+ tools.
320
+ > **Key insight:** Remote SSE is read-only. Cloud Mode adds write access for web AI clients without Node.js. NPX/Local Git give the full 84+ tools.
320
321
 
321
322
  **📖 [Complete Feature Comparison](docs/mode-comparison.md)**
322
323
 
@@ -362,7 +363,7 @@ When you first use design system tools:
362
363
  ### Local Mode - Personal Access Token (Manual)
363
364
 
364
365
  1. Visit https://help.figma.com/hc/en-us/articles/8085703771159-Manage-personal-access-tokens
365
- 2. Generate token
366
+ 2. Generate token with scopes: **File content** (Read), **Variables** (Read), **Comments** (Read and write)
366
367
  3. Add to MCP config as `FIGMA_ACCESS_TOKEN` environment variable
367
368
 
368
369
  ---
@@ -648,7 +649,7 @@ The **Figma Desktop Bridge** plugin is the recommended way to connect Figma to t
648
649
  - The MCP server communicates via **WebSocket** through the Desktop Bridge plugin
649
650
  - The server tries port 9223 first, then automatically falls back through ports 9224–9232 if needed
650
651
  - The plugin scans all ports in the range and connects to every active server it finds
651
- - All 78+ tools work through the WebSocket transport
652
+ - All 84+ tools work through the WebSocket transport
652
653
 
653
654
  **Multiple files:** The WebSocket server supports multiple simultaneous plugin connections — one per open Figma file. Each connection is tracked by file key with independent state (selection, document changes, console logs).
654
655
 
@@ -785,7 +786,7 @@ The architecture supports adding new apps with minimal boilerplate — each app
785
786
 
786
787
  ## 🛤️ Roadmap
787
788
 
788
- **Current Status:** v1.17.0 (Stable) - Production-ready with FigJam + Slides support, Cloud Write Relay, Design System Kit, WebSocket-only connectivity, smart multi-file tracking, 78+ tools, Comments API, and MCP Apps
789
+ **Current Status:** v1.17.0 (Stable) - Production-ready with FigJam + Slides support, Cloud Write Relay, Design System Kit, WebSocket-only connectivity, smart multi-file tracking, 84+ tools, Comments API, and MCP Apps
789
790
 
790
791
  **Recent Releases:**
791
792
  - [x] **v1.17.0** - Figma Slides Support: 15 new tools for managing presentations — slides, transitions, content, reordering, and navigation. Inspired by Toni Haidamous (PR #11).
@@ -1198,15 +1198,23 @@ export function registerFigmaAPITools(server, getFigmaAPI, getCurrentUrl, getCon
1198
1198
  // =====================================================================
1199
1199
  // FETCH LOGIC: No cache or cache invalid/refresh requested
1200
1200
  // =====================================================================
1201
- // Check if REST API token is available (determines priority)
1201
+ // Check if REST API token is available
1202
1202
  const hasToken = !!process.env.FIGMA_ACCESS_TOKEN;
1203
1203
  let restApiSucceeded = false;
1204
+ // Detect Desktop Bridge availability early (needed for priority decision)
1205
+ if (ensureInitialized && !getDesktopConnector && !parseFromConsole) {
1206
+ logger.info("Calling ensureInitialized to initialize browser manager (legacy path)");
1207
+ await ensureInitialized();
1208
+ }
1209
+ const browserManager = getBrowserManager?.();
1210
+ const hasDesktopConnection = !!getDesktopConnector || !!browserManager;
1204
1211
  // PRIORITY LOGIC:
1205
- // 1. If token exists → Try REST API FIRST (enterprise users)
1206
- // 2. If no token OR REST API fails → Try Desktop Bridge as fallback
1207
- logger.info({ hasToken }, "Authentication method detection");
1208
- // Try REST API first if token is available
1209
- if (hasToken && !parseFromConsole) {
1212
+ // 1. If Desktop Bridge connected → Try Desktop Bridge FIRST (instant, all plans, full Plugin API data)
1213
+ // 2. If no Desktop Bridge OR it fails → Try REST API as fallback (Enterprise users)
1214
+ // 3. If both fail Console snippet fallback (manual user step)
1215
+ logger.info({ hasToken, hasDesktopConnection }, "Authentication method detection");
1216
+ // Try REST API only when Desktop Bridge is NOT available
1217
+ if (hasToken && !parseFromConsole && !hasDesktopConnection) {
1210
1218
  try {
1211
1219
  logger.info({ fileKey, includePublished, verbosity, enrich }, "Fetching variables via REST API (priority: token detected)");
1212
1220
  const api = await getFigmaAPI();
@@ -1449,26 +1457,9 @@ export function registerFigmaAPITools(server, getFigmaAPI, getCurrentUrl, getCon
1449
1457
  // Don't throw - fall through to Desktop Bridge
1450
1458
  }
1451
1459
  }
1452
- // FALLBACK: Try Desktop connection (when no token available OR as secondary fallback)
1453
- // Only call ensureInitialized for legacy path skip when transport-agnostic connector exists
1454
- if (ensureInitialized && !getDesktopConnector && !parseFromConsole && (!hasToken || !restApiSucceeded)) {
1455
- logger.info("Calling ensureInitialized to initialize browser manager (legacy path)");
1456
- await ensureInitialized();
1457
- }
1458
- const browserManager = getBrowserManager?.();
1459
- const hasDesktopConnection = !!getDesktopConnector || !!browserManager;
1460
- logger.info({ hasBrowserManager: !!browserManager, hasDesktopConnector: !!getDesktopConnector, parseFromConsole, hasToken, restApiSucceeded }, "Desktop connection check");
1461
- // Debug: Log why Desktop connection might be skipped
1462
- if (!hasDesktopConnection) {
1463
- logger.error("Desktop connection skipped: neither connector nor browserManager available");
1464
- }
1465
- else if (parseFromConsole) {
1466
- logger.info("Desktop connection skipped: parseFromConsole is true");
1467
- }
1468
- else if (restApiSucceeded) {
1469
- logger.info("Desktop connection skipped: REST API already succeeded");
1470
- }
1471
- if (hasDesktopConnection && !parseFromConsole && (!hasToken || !restApiSucceeded)) {
1460
+ // PRIMARY: Try Desktop Bridge (instant, all plans, full Plugin API data including aliases)
1461
+ // Also used as fallback when REST API fails (403, timeout, rate limit)
1462
+ if (hasDesktopConnection && !parseFromConsole && !restApiSucceeded) {
1472
1463
  try {
1473
1464
  logger.info({ fileKey }, "Attempting to get variables via Desktop connection");
1474
1465
  let connector;
@@ -1683,10 +1674,77 @@ export function registerFigmaAPITools(server, getFigmaAPI, getCurrentUrl, getCon
1683
1674
  catch (logError) {
1684
1675
  // Ignore logging errors
1685
1676
  }
1686
- // Continue to try other methods
1677
+ // Continue to try REST API fallback
1678
+ }
1679
+ }
1680
+ // SECONDARY FALLBACK: Try REST API if Desktop Bridge failed/unavailable and token exists
1681
+ if (hasToken && !parseFromConsole && !restApiSucceeded) {
1682
+ try {
1683
+ logger.info({ fileKey }, "Attempting REST API fallback for variables");
1684
+ const api = await getFigmaAPI();
1685
+ const { local, published, localError } = await withTimeout(api.getAllVariables(fileKey), 30000, 'Figma Variables API');
1686
+ if (!localError && local) {
1687
+ let localFormatted = formatVariables(local);
1688
+ let publishedFormatted = includePublished ? formatVariables(published) : null;
1689
+ // Apply filters
1690
+ if (format === 'filtered') {
1691
+ const filteredLocal = applyFilters({ variables: localFormatted.variables, variableCollections: localFormatted.collections }, { collection, namePattern, mode }, verbosity || "standard");
1692
+ localFormatted = { summary: localFormatted.summary, collections: filteredLocal.variableCollections, variables: filteredLocal.variables };
1693
+ }
1694
+ // Apply verbosity
1695
+ if (verbosity && verbosity !== 'full') {
1696
+ const verbosityFiltered = applyFilters({ variables: localFormatted.variables, variableCollections: localFormatted.collections }, {}, verbosity);
1697
+ localFormatted = { ...localFormatted, collections: verbosityFiltered.variableCollections, variables: verbosityFiltered.variables };
1698
+ }
1699
+ // Cache
1700
+ const dataForCache = {
1701
+ fileKey,
1702
+ local: { summary: localFormatted.summary, collections: localFormatted.collections, variables: localFormatted.variables },
1703
+ ...(includePublished && publishedFormatted && { published: { summary: publishedFormatted.summary, collections: publishedFormatted.collections, variables: publishedFormatted.variables } }),
1704
+ verbosity: verbosity || "standard",
1705
+ enriched: enrich || false,
1706
+ timestamp: Date.now(),
1707
+ source: "rest_api",
1708
+ };
1709
+ if (variablesCache) {
1710
+ variablesCache.set(fileKey, { data: dataForCache, timestamp: Date.now() });
1711
+ }
1712
+ // Apply alias resolution
1713
+ if (resolveAliases && localFormatted.variables?.length > 0) {
1714
+ const allVariablesMap = new Map();
1715
+ const collectionsMap = new Map();
1716
+ for (const v of localFormatted.variables || [])
1717
+ allVariablesMap.set(v.id, v);
1718
+ for (const c of localFormatted.collections || [])
1719
+ collectionsMap.set(c.id, c);
1720
+ localFormatted.variables = resolveVariableAliases(localFormatted.variables, allVariablesMap, collectionsMap);
1721
+ }
1722
+ restApiSucceeded = true;
1723
+ logger.info({ fileKey }, "REST API fallback succeeded");
1724
+ const responseData = {
1725
+ fileKey,
1726
+ local: { summary: localFormatted.summary, collections: localFormatted.collections, variables: localFormatted.variables },
1727
+ verbosity: verbosity || "standard",
1728
+ enriched: enrich || false,
1729
+ };
1730
+ return adaptiveResponse(responseData, {
1731
+ toolName: "figma_get_variables",
1732
+ suggestedActions: [
1733
+ "Use verbosity='inventory' or 'summary' for large variable sets",
1734
+ "Apply filters: collection, namePattern, or mode parameters",
1735
+ ],
1736
+ });
1737
+ }
1738
+ else {
1739
+ logger.warn({ error: localError, fileKey }, "REST API fallback also failed (likely non-Enterprise plan)");
1740
+ }
1741
+ }
1742
+ catch (restFallbackError) {
1743
+ const msg = restFallbackError instanceof Error ? restFallbackError.message : String(restFallbackError);
1744
+ logger.warn({ error: msg, fileKey }, "REST API fallback failed");
1687
1745
  }
1688
1746
  }
1689
- // FALLBACK: Parse from console logs if requested
1747
+ // LAST RESORT: Parse from console logs if requested
1690
1748
  if (parseFromConsole) {
1691
1749
  const consoleMonitor = getConsoleMonitor?.();
1692
1750
  if (!consoleMonitor) {
@@ -41,6 +41,8 @@ const PORT_FILE_DIR = tmpdir();
41
41
  export const MAX_PORT_FILE_AGE_MS = 4 * 60 * 60 * 1000;
42
42
  /** Maximum time since last heartbeat before a process is considered stale (5 minutes) */
43
43
  export const HEARTBEAT_STALE_MS = 5 * 60 * 1000;
44
+ /** Minimum age before an instance can be evicted as last resort (2 minutes) */
45
+ export const EVICTION_MIN_AGE_MS = 2 * 60 * 1000;
44
46
  /** Interval between heartbeat refreshes (30 seconds) */
45
47
  export const HEARTBEAT_INTERVAL_MS = 30 * 1000;
46
48
  /**
@@ -336,6 +338,92 @@ export function cleanupOrphanedProcesses(preferredPort = DEFAULT_WS_PORT) {
336
338
  }
337
339
  return cleaned;
338
340
  }
341
+ /**
342
+ * Last-resort eviction: terminate the oldest MCP server instance to free a port.
343
+ *
344
+ * Called ONLY when all ports in the range are exhausted after both cleanup phases
345
+ * (cleanupStalePortFiles + cleanupOrphanedProcesses) have already run. This handles
346
+ * the case where old instances are still alive and heartbeating but no longer needed
347
+ * (e.g., from yesterday's Claude Desktop session that was closed without terminating
348
+ * the MCP server process).
349
+ *
350
+ * Safety guards:
351
+ * - Only evicts instances older than EVICTION_MIN_AGE_MS (2 min) to prevent cascade
352
+ * - Never evicts our own PID
353
+ * - Re-reads port file before kill to avoid TOCTOU race
354
+ * - Uses SIGTERM for graceful shutdown
355
+ * - Waits briefly for port release before returning
356
+ *
357
+ * @returns true if an instance was evicted (caller should retry port binding), false otherwise
358
+ */
359
+ export function evictOldestInstance(preferredPort = DEFAULT_WS_PORT) {
360
+ const myPid = process.pid;
361
+ const ports = getPortRange(preferredPort);
362
+ const candidates = [];
363
+ // Collect all valid port file entries that aren't us
364
+ for (const port of ports) {
365
+ const filePath = getPortFilePath(port);
366
+ try {
367
+ if (!existsSync(filePath))
368
+ continue;
369
+ const raw = readFileSync(filePath, 'utf-8');
370
+ const data = JSON.parse(raw);
371
+ if (data.pid === myPid)
372
+ continue; // Never evict ourselves
373
+ if (!isProcessAlive(data.pid)) {
374
+ // Dead process — just clean up the file (port should already be free)
375
+ try {
376
+ unlinkSync(filePath);
377
+ }
378
+ catch { /* best-effort */ }
379
+ continue;
380
+ }
381
+ candidates.push({ ...data, filePath });
382
+ }
383
+ catch {
384
+ // Corrupt or unreadable — skip
385
+ }
386
+ }
387
+ if (candidates.length === 0) {
388
+ logger.debug('No eviction candidates — ports may be held by non-MCP processes');
389
+ return false;
390
+ }
391
+ // Sort by startedAt ascending — oldest first
392
+ candidates.sort((a, b) => new Date(a.startedAt).getTime() - new Date(b.startedAt).getTime());
393
+ const oldest = candidates[0];
394
+ const ageMs = Date.now() - new Date(oldest.startedAt).getTime();
395
+ // Safety: don't evict instances that started recently (prevents cascade)
396
+ if (ageMs < EVICTION_MIN_AGE_MS) {
397
+ logger.info({ port: oldest.port, pid: oldest.pid, ageSeconds: Math.round(ageMs / 1000) }, 'Oldest instance is too recent to evict — skipping');
398
+ return false;
399
+ }
400
+ // Re-read the port file to avoid TOCTOU race
401
+ try {
402
+ const raw = readFileSync(oldest.filePath, 'utf-8');
403
+ const freshData = JSON.parse(raw);
404
+ if (freshData.pid !== oldest.pid) {
405
+ // PID changed between reads — another process took over, skip
406
+ return false;
407
+ }
408
+ }
409
+ catch {
410
+ // File disappeared — port may already be free
411
+ return true;
412
+ }
413
+ logger.info({ port: oldest.port, pid: oldest.pid, startedAt: oldest.startedAt, ageHours: Math.round(ageMs / 3600000 * 10) / 10 }, 'Evicting oldest MCP server instance to free port (all ports exhausted)');
414
+ terminateProcess(oldest.pid);
415
+ try {
416
+ unlinkSync(oldest.filePath);
417
+ }
418
+ catch { /* best-effort */ }
419
+ // Brief wait for the port to be released by the OS
420
+ try {
421
+ const { execSync } = require('child_process');
422
+ execSync('sleep 0.5', { timeout: 2000 });
423
+ }
424
+ catch { /* non-critical */ }
425
+ return true;
426
+ }
339
427
  /**
340
428
  * Register process exit handlers to clean up port advertisement file.
341
429
  * Should be called once after the port is successfully bound.
@@ -40,7 +40,7 @@ export class FigmaConsoleMCPv3 extends McpAgent {
40
40
  super(...arguments);
41
41
  this.server = new McpServer({
42
42
  name: "Figma Console MCP",
43
- version: "1.17.1",
43
+ version: "1.17.4",
44
44
  });
45
45
  this.browserManager = null;
46
46
  this.consoleMonitor = null;
@@ -956,7 +956,7 @@ export default {
956
956
  });
957
957
  const statelessServer = new McpServer({
958
958
  name: "Figma Console MCP",
959
- version: "1.17.1",
959
+ version: "1.17.4",
960
960
  });
961
961
  // ================================================================
962
962
  // Cloud Write Relay — Pairing Tool (stateless /mcp path)
@@ -1589,7 +1589,7 @@ export default {
1589
1589
  return new Response(JSON.stringify({
1590
1590
  status: "healthy",
1591
1591
  service: "Figma Console MCP",
1592
- version: "1.17.1",
1592
+ version: "1.17.4",
1593
1593
  endpoints: {
1594
1594
  mcp: ["/sse", "/mcp"],
1595
1595
  oauth_mcp_spec: ["/.well-known/oauth-authorization-server", "/authorize", "/token", "/oauth/register"],
@@ -1635,13 +1635,13 @@ export default {
1635
1635
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
1636
1636
  <title>Figma Console MCP - The Most Comprehensive MCP Server for Figma</title>
1637
1637
  <link rel="icon" type="image/svg+xml" href="https://docs.figma-console-mcp.southleft.com/favicon.svg">
1638
- <meta name="description" content="Turn your Figma design system into a living API. 78+ tools give AI assistants deep access to design tokens, component specs, variables, and programmatic design creation.">
1638
+ <meta name="description" content="Turn your Figma design system into a living API. 84+ tools give AI assistants deep access to design tokens, component specs, variables, and programmatic design creation.">
1639
1639
 
1640
1640
  <!-- Open Graph -->
1641
1641
  <meta property="og:type" content="website">
1642
1642
  <meta property="og:url" content="https://figma-console-mcp.southleft.com">
1643
1643
  <meta property="og:title" content="Figma Console MCP - Turn Your Design System Into a Living API">
1644
- <meta property="og:description" content="The most comprehensive MCP server for Figma. 78+ tools give AI assistants deep access to design tokens, components, variables, and programmatic design creation.">
1644
+ <meta property="og:description" content="The most comprehensive MCP server for Figma. 84+ tools give AI assistants deep access to design tokens, components, variables, and programmatic design creation.">
1645
1645
  <meta property="og:image" content="https://docs.figma-console-mcp.southleft.com/images/og-image.jpg">
1646
1646
  <meta property="og:image:width" content="1200">
1647
1647
  <meta property="og:image:height" content="630">
@@ -1649,7 +1649,7 @@ export default {
1649
1649
  <!-- Twitter -->
1650
1650
  <meta name="twitter:card" content="summary_large_image">
1651
1651
  <meta name="twitter:title" content="Figma Console MCP - Turn Your Design System Into a Living API">
1652
- <meta name="twitter:description" content="The most comprehensive MCP server for Figma. 78+ tools give AI assistants deep access to design tokens, components, variables, and programmatic design creation.">
1652
+ <meta name="twitter:description" content="The most comprehensive MCP server for Figma. 84+ tools give AI assistants deep access to design tokens, components, variables, and programmatic design creation.">
1653
1653
  <meta name="twitter:image" content="https://docs.figma-console-mcp.southleft.com/images/og-image.jpg">
1654
1654
 
1655
1655
  <meta name="theme-color" content="#0D9488">
@@ -32,6 +32,8 @@ export declare const PORT_RANGE_SIZE = 10;
32
32
  export declare const MAX_PORT_FILE_AGE_MS: number;
33
33
  /** Maximum time since last heartbeat before a process is considered stale (5 minutes) */
34
34
  export declare const HEARTBEAT_STALE_MS: number;
35
+ /** Minimum age before an instance can be evicted as last resort (2 minutes) */
36
+ export declare const EVICTION_MIN_AGE_MS: number;
35
37
  /** Interval between heartbeat refreshes (30 seconds) */
36
38
  export declare const HEARTBEAT_INTERVAL_MS: number;
37
39
  export interface PortFileData {
@@ -113,6 +115,25 @@ export declare function cleanupStalePortFiles(): number;
113
115
  * then this catches any remaining ghosts.
114
116
  */
115
117
  export declare function cleanupOrphanedProcesses(preferredPort?: number): number;
118
+ /**
119
+ * Last-resort eviction: terminate the oldest MCP server instance to free a port.
120
+ *
121
+ * Called ONLY when all ports in the range are exhausted after both cleanup phases
122
+ * (cleanupStalePortFiles + cleanupOrphanedProcesses) have already run. This handles
123
+ * the case where old instances are still alive and heartbeating but no longer needed
124
+ * (e.g., from yesterday's Claude Desktop session that was closed without terminating
125
+ * the MCP server process).
126
+ *
127
+ * Safety guards:
128
+ * - Only evicts instances older than EVICTION_MIN_AGE_MS (2 min) to prevent cascade
129
+ * - Never evicts our own PID
130
+ * - Re-reads port file before kill to avoid TOCTOU race
131
+ * - Uses SIGTERM for graceful shutdown
132
+ * - Waits briefly for port release before returning
133
+ *
134
+ * @returns true if an instance was evicted (caller should retry port binding), false otherwise
135
+ */
136
+ export declare function evictOldestInstance(preferredPort?: number): boolean;
116
137
  /**
117
138
  * Register process exit handlers to clean up port advertisement file.
118
139
  * Should be called once after the port is successfully bound.
@@ -1 +1 @@
1
- {"version":3,"file":"port-discovery.d.ts","sourceRoot":"","sources":["../../src/core/port-discovery.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AASH,uCAAuC;AACvC,eAAO,MAAM,eAAe,OAAO,CAAC;AAEpC,mEAAmE;AACnE,eAAO,MAAM,eAAe,KAAK,CAAC;AAQlC,qFAAqF;AACrF,eAAO,MAAM,oBAAoB,QAAqB,CAAC;AAEvD,yFAAyF;AACzF,eAAO,MAAM,kBAAkB,QAAgB,CAAC;AAEhD,wDAAwD;AACxD,eAAO,MAAM,qBAAqB,QAAY,CAAC;AAE/C,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,sFAAsF;IACtF,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,aAAa,GAAE,MAAwB,GAAG,MAAM,EAAE,CAM9E;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,MAAoB,GAAG,IAAI,CAiB5E;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAa3D;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAUlD;AAcD;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAY3D;AAkBD;;;GAGG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CAoB9D;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,aAAa,GAAE,MAAwB,GAAG,YAAY,EAAE,CAW/F;AAED;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,IAAI,MAAM,CAsC9C;AAED;;;;;;;;;GASG;AACH,wBAAgB,wBAAwB,CAAC,aAAa,GAAE,MAAwB,GAAG,MAAM,CAkExF;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAatD"}
1
+ {"version":3,"file":"port-discovery.d.ts","sourceRoot":"","sources":["../../src/core/port-discovery.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AASH,uCAAuC;AACvC,eAAO,MAAM,eAAe,OAAO,CAAC;AAEpC,mEAAmE;AACnE,eAAO,MAAM,eAAe,KAAK,CAAC;AAQlC,qFAAqF;AACrF,eAAO,MAAM,oBAAoB,QAAqB,CAAC;AAEvD,yFAAyF;AACzF,eAAO,MAAM,kBAAkB,QAAgB,CAAC;AAEhD,+EAA+E;AAC/E,eAAO,MAAM,mBAAmB,QAAgB,CAAC;AAEjD,wDAAwD;AACxD,eAAO,MAAM,qBAAqB,QAAY,CAAC;AAE/C,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,sFAAsF;IACtF,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,aAAa,GAAE,MAAwB,GAAG,MAAM,EAAE,CAM9E;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,MAAoB,GAAG,IAAI,CAiB5E;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAa3D;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAUlD;AAcD;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAY3D;AAkBD;;;GAGG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CAoB9D;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,aAAa,GAAE,MAAwB,GAAG,YAAY,EAAE,CAW/F;AAED;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,IAAI,MAAM,CAsC9C;AAED;;;;;;;;;GASG;AACH,wBAAgB,wBAAwB,CAAC,aAAa,GAAE,MAAwB,GAAG,MAAM,CAkExF;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,mBAAmB,CAAC,aAAa,GAAE,MAAwB,GAAG,OAAO,CA0EpF;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAatD"}
@@ -41,6 +41,8 @@ const PORT_FILE_DIR = tmpdir();
41
41
  export const MAX_PORT_FILE_AGE_MS = 4 * 60 * 60 * 1000;
42
42
  /** Maximum time since last heartbeat before a process is considered stale (5 minutes) */
43
43
  export const HEARTBEAT_STALE_MS = 5 * 60 * 1000;
44
+ /** Minimum age before an instance can be evicted as last resort (2 minutes) */
45
+ export const EVICTION_MIN_AGE_MS = 2 * 60 * 1000;
44
46
  /** Interval between heartbeat refreshes (30 seconds) */
45
47
  export const HEARTBEAT_INTERVAL_MS = 30 * 1000;
46
48
  /**
@@ -336,6 +338,92 @@ export function cleanupOrphanedProcesses(preferredPort = DEFAULT_WS_PORT) {
336
338
  }
337
339
  return cleaned;
338
340
  }
341
+ /**
342
+ * Last-resort eviction: terminate the oldest MCP server instance to free a port.
343
+ *
344
+ * Called ONLY when all ports in the range are exhausted after both cleanup phases
345
+ * (cleanupStalePortFiles + cleanupOrphanedProcesses) have already run. This handles
346
+ * the case where old instances are still alive and heartbeating but no longer needed
347
+ * (e.g., from yesterday's Claude Desktop session that was closed without terminating
348
+ * the MCP server process).
349
+ *
350
+ * Safety guards:
351
+ * - Only evicts instances older than EVICTION_MIN_AGE_MS (2 min) to prevent cascade
352
+ * - Never evicts our own PID
353
+ * - Re-reads port file before kill to avoid TOCTOU race
354
+ * - Uses SIGTERM for graceful shutdown
355
+ * - Waits briefly for port release before returning
356
+ *
357
+ * @returns true if an instance was evicted (caller should retry port binding), false otherwise
358
+ */
359
+ export function evictOldestInstance(preferredPort = DEFAULT_WS_PORT) {
360
+ const myPid = process.pid;
361
+ const ports = getPortRange(preferredPort);
362
+ const candidates = [];
363
+ // Collect all valid port file entries that aren't us
364
+ for (const port of ports) {
365
+ const filePath = getPortFilePath(port);
366
+ try {
367
+ if (!existsSync(filePath))
368
+ continue;
369
+ const raw = readFileSync(filePath, 'utf-8');
370
+ const data = JSON.parse(raw);
371
+ if (data.pid === myPid)
372
+ continue; // Never evict ourselves
373
+ if (!isProcessAlive(data.pid)) {
374
+ // Dead process — just clean up the file (port should already be free)
375
+ try {
376
+ unlinkSync(filePath);
377
+ }
378
+ catch { /* best-effort */ }
379
+ continue;
380
+ }
381
+ candidates.push({ ...data, filePath });
382
+ }
383
+ catch {
384
+ // Corrupt or unreadable — skip
385
+ }
386
+ }
387
+ if (candidates.length === 0) {
388
+ logger.debug('No eviction candidates — ports may be held by non-MCP processes');
389
+ return false;
390
+ }
391
+ // Sort by startedAt ascending — oldest first
392
+ candidates.sort((a, b) => new Date(a.startedAt).getTime() - new Date(b.startedAt).getTime());
393
+ const oldest = candidates[0];
394
+ const ageMs = Date.now() - new Date(oldest.startedAt).getTime();
395
+ // Safety: don't evict instances that started recently (prevents cascade)
396
+ if (ageMs < EVICTION_MIN_AGE_MS) {
397
+ logger.info({ port: oldest.port, pid: oldest.pid, ageSeconds: Math.round(ageMs / 1000) }, 'Oldest instance is too recent to evict — skipping');
398
+ return false;
399
+ }
400
+ // Re-read the port file to avoid TOCTOU race
401
+ try {
402
+ const raw = readFileSync(oldest.filePath, 'utf-8');
403
+ const freshData = JSON.parse(raw);
404
+ if (freshData.pid !== oldest.pid) {
405
+ // PID changed between reads — another process took over, skip
406
+ return false;
407
+ }
408
+ }
409
+ catch {
410
+ // File disappeared — port may already be free
411
+ return true;
412
+ }
413
+ logger.info({ port: oldest.port, pid: oldest.pid, startedAt: oldest.startedAt, ageHours: Math.round(ageMs / 3600000 * 10) / 10 }, 'Evicting oldest MCP server instance to free port (all ports exhausted)');
414
+ terminateProcess(oldest.pid);
415
+ try {
416
+ unlinkSync(oldest.filePath);
417
+ }
418
+ catch { /* best-effort */ }
419
+ // Brief wait for the port to be released by the OS
420
+ try {
421
+ const { execSync } = require('child_process');
422
+ execSync('sleep 0.5', { timeout: 2000 });
423
+ }
424
+ catch { /* non-critical */ }
425
+ return true;
426
+ }
339
427
  /**
340
428
  * Register process exit handlers to clean up port advertisement file.
341
429
  * Should be called once after the port is successfully bound.
@@ -1 +1 @@
1
- {"version":3,"file":"port-discovery.js","sourceRoot":"","sources":["../../src/core/port-discovery.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,IAAI,CAAC;AACtF,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,MAAM,EAAE,MAAM,IAAI,CAAC;AAC5B,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEhD,MAAM,MAAM,GAAG,iBAAiB,CAAC,EAAE,SAAS,EAAE,gBAAgB,EAAE,CAAC,CAAC;AAElE,uCAAuC;AACvC,MAAM,CAAC,MAAM,eAAe,GAAG,IAAI,CAAC;AAEpC,mEAAmE;AACnE,MAAM,CAAC,MAAM,eAAe,GAAG,EAAE,CAAC;AAElC,kDAAkD;AAClD,MAAM,gBAAgB,GAAG,oBAAoB,CAAC;AAE9C,6CAA6C;AAC7C,MAAM,aAAa,GAAG,MAAM,EAAE,CAAC;AAE/B,qFAAqF;AACrF,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAEvD,yFAAyF;AACzF,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;AAEhD,wDAAwD;AACxD,MAAM,CAAC,MAAM,qBAAqB,GAAG,EAAE,GAAG,IAAI,CAAC;AAW/C;;;;;;;;GAQG;AACH,MAAM,UAAU,YAAY,CAAC,gBAAwB,eAAe;IAClE,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,eAAe,EAAE,CAAC,EAAE,EAAE,CAAC;QACzC,KAAK,CAAC,IAAI,CAAC,aAAa,GAAG,CAAC,CAAC,CAAC;IAChC,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,IAAY;IAC1C,OAAO,IAAI,CAAC,aAAa,EAAE,GAAG,gBAAgB,GAAG,IAAI,OAAO,CAAC,CAAC;AAChE,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,IAAY,EAAE,OAAe,WAAW;IACpE,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IACrC,MAAM,IAAI,GAAiB;QACzB,IAAI;QACJ,GAAG,EAAE,OAAO,CAAC,GAAG;QAChB,IAAI;QACJ,SAAS,EAAE,GAAG;QACd,QAAQ,EAAE,GAAG;KACd,CAAC;IAEF,MAAM,QAAQ,GAAG,eAAe,CAAC,IAAI,CAAC,CAAC;IACvC,IAAI,CAAC;QACH,aAAa,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QACvD,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,iBAAiB,CAAC,CAAC;IACrD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,yCAAyC,CAAC,CAAC;IACpF,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,wBAAwB,CAAC,IAAY;IACnD,MAAM,QAAQ,GAAG,eAAe,CAAC,IAAI,CAAC,CAAC;IACvC,IAAI,CAAC;QACH,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC;YAAE,OAAO;QAClC,MAAM,GAAG,GAAG,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAC5C,MAAM,IAAI,GAAiB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC3C,iCAAiC;QACjC,IAAI,IAAI,CAAC,GAAG,KAAK,OAAO,CAAC,GAAG;YAAE,OAAO;QACrC,IAAI,CAAC,QAAQ,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QACzC,aAAa,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IACzD,CAAC;IAAC,MAAM,CAAC;QACP,iDAAiD;IACnD,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,IAAY;IAC1C,MAAM,QAAQ,GAAG,eAAe,CAAC,IAAI,CAAC,CAAC;IACvC,IAAI,CAAC;QACH,IAAI,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YACzB,UAAU,CAAC,QAAQ,CAAC,CAAC;YACrB,MAAM,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,4BAA4B,CAAC,CAAC;QACjE,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,iDAAiD;IACnD,CAAC;AACH,CAAC;AAED;;GAEG;AACH,SAAS,cAAc,CAAC,GAAW;IACjC,IAAI,CAAC;QACH,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,oDAAoD;QAC1E,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,eAAe,CAAC,IAAkB;IAChD,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAEvB,iEAAiE;IACjE,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;QAClB,MAAM,WAAW,GAAG,GAAG,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,OAAO,EAAE,CAAC;QAC5D,OAAO,WAAW,GAAG,kBAAkB,CAAC;IAC1C,CAAC;IAED,+DAA+D;IAC/D,MAAM,UAAU,GAAG,GAAG,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,CAAC;IAC5D,OAAO,UAAU,GAAG,oBAAoB,CAAC;AAC3C,CAAC;AAED;;;;;;GAMG;AACH,SAAS,gBAAgB,CAAC,GAAW;IACnC,IAAI,CAAC;QACH,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;QAC7B,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC,CAAC,kCAAkC;IAClD,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,MAAM,QAAQ,GAAG,eAAe,CAAC,IAAI,CAAC,CAAC;IAEvC,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC;QAAE,OAAO,IAAI,CAAC;IAEvC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAC5C,MAAM,IAAI,GAAiB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAE3C,6CAA6C;QAC7C,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;YAC9B,MAAM,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,sDAAsD,CAAC,CAAC;YAC9F,IAAI,CAAC;gBAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;YAAC,CAAC;YAAC,MAAM,CAAC,CAAC,iBAAiB,CAAC,CAAC;YACzD,OAAO,IAAI,CAAC;QACd,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,uBAAuB,CAAC,gBAAwB,eAAe;IAC7E,MAAM,SAAS,GAAmB,EAAE,CAAC;IAErC,KAAK,MAAM,IAAI,IAAI,YAAY,CAAC,aAAa,CAAC,EAAE,CAAC;QAC/C,MAAM,IAAI,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;QAChC,IAAI,IAAI,EAAE,CAAC;YACT,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACvB,CAAC;IACH,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,qBAAqB;IACnC,IAAI,OAAO,GAAG,CAAC,CAAC;IAEhB,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,WAAW,CAAC,aAAa,CAAC,CAAC;QACzC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,IAAI,IAAI,CAAC,UAAU,CAAC,gBAAgB,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;gBAChE,MAAM,QAAQ,GAAG,IAAI,CAAC,aAAa,EAAE,IAAI,CAAC,CAAC;gBAC3C,IAAI,CAAC;oBACH,MAAM,GAAG,GAAG,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;oBAC5C,MAAM,IAAI,GAAiB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;oBAE3C,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;wBAC9B,oCAAoC;wBACpC,UAAU,CAAC,QAAQ,CAAC,CAAC;wBACrB,OAAO,EAAE,CAAC;wBACV,MAAM,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,2CAA2C,CAAC,CAAC;oBAChG,CAAC;yBAAM,IAAI,IAAI,CAAC,GAAG,KAAK,OAAO,CAAC,GAAG,IAAI,eAAe,CAAC,IAAI,CAAC,EAAE,CAAC;wBAC7D,qEAAqE;wBACrE,MAAM,CAAC,IAAI,CACT,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,EACtF,4DAA4D,CAC7D,CAAC;wBACF,gBAAgB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;wBAC3B,IAAI,CAAC;4BAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;wBAAC,CAAC;wBAAC,MAAM,CAAC,CAAC,iBAAiB,CAAC,CAAC;wBACzD,OAAO,EAAE,CAAC;oBACZ,CAAC;gBACH,CAAC;gBAAC,MAAM,CAAC;oBACP,2BAA2B;oBAC3B,IAAI,CAAC;wBAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;wBAAC,OAAO,EAAE,CAAC;oBAAC,CAAC;oBAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC;gBACjE,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,0CAA0C;IAC5C,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,wBAAwB,CAAC,gBAAwB,eAAe;IAC9E,uCAAuC;IACvC,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO;QAAE,OAAO,CAAC,CAAC;IAE3C,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC;IAC1B,MAAM,KAAK,GAAG,YAAY,CAAC,aAAa,CAAC,CAAC;IAE1C,+DAA+D;IAC/D,MAAM,SAAS,GAAG,IAAI,GAAG,EAAU,CAAC;IACpC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,IAAI,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;QAChC,IAAI,IAAI;YAAE,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACpC,CAAC;IACD,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,uBAAuB;IAE7C,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,CAAC;YACH,4CAA4C;YAC5C,MAAM,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;YAC9C,MAAM,MAAM,GAAG,QAAQ,CAAC,YAAY,IAAI,8BAA8B,EAAE;gBACtE,QAAQ,EAAE,OAAO;gBACjB,OAAO,EAAE,IAAI;aACd,CAAC,CAAC,IAAI,EAAE,CAAC;YAEV,IAAI,CAAC,MAAM;gBAAE,SAAS;YAEtB,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YAE5D,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;gBACvB,IAAI,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC;oBAAE,SAAS,CAAC,0BAA0B;gBAE5D,qEAAqE;gBACrE,IAAI,CAAC;oBACH,MAAM,OAAO,GAAG,QAAQ,CAAC,SAAS,GAAG,0BAA0B,EAAE;wBAC/D,QAAQ,EAAE,OAAO;wBACjB,OAAO,EAAE,IAAI;qBACd,CAAC,CAAC,IAAI,EAAE,CAAC;oBAEV,IAAI,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;wBACnH,MAAM,CAAC,IAAI,CACT,EAAE,IAAI,EAAE,GAAG,EAAE,OAAO,EAAE,OAAO,CAAC,SAAS,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,EACjD,8DAA8D,CAC/D,CAAC;wBACF,gBAAgB,CAAC,GAAG,CAAC,CAAC;wBACtB,OAAO,EAAE,CAAC;oBACZ,CAAC;gBACH,CAAC;gBAAC,MAAM,CAAC;oBACP,4CAA4C;gBAC9C,CAAC;YACH,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,mCAAmC;QACrC,CAAC;IACH,CAAC;IAED,IAAI,OAAO,GAAG,CAAC,EAAE,CAAC;QAChB,4DAA4D;QAC5D,IAAI,CAAC;YACH,MAAM,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;YAC9C,QAAQ,CAAC,WAAW,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;QAC3C,CAAC;QAAC,MAAM,CAAC,CAAC,kBAAkB,CAAC,CAAC;QAC9B,MAAM,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,EAAE,cAAc,OAAO,kCAAkC,CAAC,CAAC;IACpF,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAAY;IAC9C,MAAM,OAAO,GAAG,GAAG,EAAE,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;IAE5C,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAE5B,+DAA+D;IAC/D,2DAA2D;IAC3D,MAAM,uBAAuB,GAAG,OAAO,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;IAC5D,MAAM,wBAAwB,GAAG,OAAO,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;IAE9D,wEAAwE;IACxE,OAAO,CAAC,eAAe,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAC3C,OAAO,CAAC,eAAe,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;AAC9C,CAAC"}
1
+ {"version":3,"file":"port-discovery.js","sourceRoot":"","sources":["../../src/core/port-discovery.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"local.d.ts","sourceRoot":"","sources":["../src/local.ts"],"names":[],"mappings":";AAEA;;;;;;;;;;;;GAYG;AAmFH;;;GAGG;AACH,cAAM,oBAAoB;IACzB,OAAO,CAAC,MAAM,CAAY;IAC1B,OAAO,CAAC,cAAc,CAAoC;IAC1D,OAAO,CAAC,cAAc,CAA+B;IACrD,OAAO,CAAC,QAAQ,CAAyB;IACzC,OAAO,CAAC,gBAAgB,CAAgC;IACxD,OAAO,CAAC,QAAQ,CAAqC;IACrD,OAAO,CAAC,cAAc,CAA+C;IACrE,uGAAuG;IACvG,OAAO,CAAC,YAAY,CAAuB;IAC3C,6DAA6D;IAC7D,OAAO,CAAC,eAAe,CAA2B;IAClD,8EAA8E;IAC9E,OAAO,CAAC,gBAAgB,CAA+C;IACvE,OAAO,CAAC,MAAM,CAAe;IAI7B,OAAO,CAAC,cAAc,CAMR;;IA2Ed;;OAEG;YACW,WAAW;IA0BzB;;;OAGG;YACW,mBAAmB;IA6CjC;;;;OAIG;IACH,OAAO,CAAC,iBAAiB;IAiBzB;;OAEG;YACW,iBAAiB;IAqB/B,wDAAwD;IACxD,OAAO,CAAC,gBAAgB,CAAuB;IAE/C;;;OAGG;IACH,OAAO,CAAC,aAAa;IAgBrB;;;;OAIG;IACH,OAAO,CAAC,kBAAkB;IAuB1B;;OAEG;YACW,iBAAiB;IA+I/B;;OAEG;IACH,OAAO,CAAC,aAAa;IAggLrB;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAgK5B;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;CAgC/B;AA4ED,OAAO,EAAE,oBAAoB,EAAE,CAAC"}
1
+ {"version":3,"file":"local.d.ts","sourceRoot":"","sources":["../src/local.ts"],"names":[],"mappings":";AAEA;;;;;;;;;;;;GAYG;AAoFH;;;GAGG;AACH,cAAM,oBAAoB;IACzB,OAAO,CAAC,MAAM,CAAY;IAC1B,OAAO,CAAC,cAAc,CAAoC;IAC1D,OAAO,CAAC,cAAc,CAA+B;IACrD,OAAO,CAAC,QAAQ,CAAyB;IACzC,OAAO,CAAC,gBAAgB,CAAgC;IACxD,OAAO,CAAC,QAAQ,CAAqC;IACrD,OAAO,CAAC,cAAc,CAA+C;IACrE,uGAAuG;IACvG,OAAO,CAAC,YAAY,CAAuB;IAC3C,6DAA6D;IAC7D,OAAO,CAAC,eAAe,CAA2B;IAClD,8EAA8E;IAC9E,OAAO,CAAC,gBAAgB,CAA+C;IACvE,OAAO,CAAC,MAAM,CAAe;IAI7B,OAAO,CAAC,cAAc,CAMR;;IA2Ed;;OAEG;YACW,WAAW;IA0BzB;;;OAGG;YACW,mBAAmB;IA6CjC;;;;OAIG;IACH,OAAO,CAAC,iBAAiB;IAiBzB;;OAEG;YACW,iBAAiB;IAqB/B,wDAAwD;IACxD,OAAO,CAAC,gBAAgB,CAAuB;IAE/C;;;OAGG;IACH,OAAO,CAAC,aAAa;IAgBrB;;;;OAIG;IACH,OAAO,CAAC,kBAAkB;IAuB1B;;OAEG;YACW,iBAAiB;IA+I/B;;OAEG;IACH,OAAO,CAAC,aAAa;IAggLrB;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IA+L5B;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;CAgC/B;AA4ED,OAAO,EAAE,oBAAoB,EAAE,CAAC"}
package/dist/local.js CHANGED
@@ -31,7 +31,7 @@ import { registerDesignSystemTools } from "./core/design-system-tools.js";
31
31
  import { FigmaDesktopConnector } from "./core/figma-desktop-connector.js";
32
32
  import { FigmaWebSocketServer } from "./core/websocket-server.js";
33
33
  import { WebSocketConnector } from "./core/websocket-connector.js";
34
- import { DEFAULT_WS_PORT, getPortRange, advertisePort, unadvertisePort, registerPortCleanup, discoverActiveInstances, cleanupStalePortFiles, cleanupOrphanedProcesses, refreshPortAdvertisement, HEARTBEAT_INTERVAL_MS, } from "./core/port-discovery.js";
34
+ import { DEFAULT_WS_PORT, getPortRange, advertisePort, unadvertisePort, registerPortCleanup, discoverActiveInstances, cleanupStalePortFiles, cleanupOrphanedProcesses, evictOldestInstance, refreshPortAdvertisement, HEARTBEAT_INTERVAL_MS, } from "./core/port-discovery.js";
35
35
  import { registerTokenBrowserApp } from "./apps/token-browser/server.js";
36
36
  import { registerDesignSystemDashboardApp } from "./apps/design-system-dashboard/server.js";
37
37
  import { registerFigJamTools } from "./core/figjam-tools.js";
@@ -5056,6 +5056,34 @@ return {
5056
5056
  break;
5057
5057
  }
5058
5058
  }
5059
+ // Phase 3: If all ports exhausted, try evicting the oldest instance and retry ONCE
5060
+ if (!boundPort && evictOldestInstance(this.wsPreferredPort)) {
5061
+ for (const port of portsToTry) {
5062
+ try {
5063
+ this.wsServer = new FigmaWebSocketServer({ port, host: wsHost });
5064
+ await this.wsServer.start();
5065
+ const addr = this.wsServer.address();
5066
+ boundPort = addr?.port ?? port;
5067
+ this.wsActualPort = boundPort;
5068
+ logger.info({ wsPort: boundPort, eviction: true }, "WebSocket bridge server started after evicting stale instance");
5069
+ advertisePort(boundPort, wsHost);
5070
+ registerPortCleanup(boundPort);
5071
+ const heartbeatPort = boundPort;
5072
+ this.wsHeartbeatTimer = setInterval(() => refreshPortAdvertisement(heartbeatPort), HEARTBEAT_INTERVAL_MS);
5073
+ this.wsHeartbeatTimer.unref();
5074
+ break;
5075
+ }
5076
+ catch (wsError) {
5077
+ const errorCode = wsError instanceof Error ? wsError.code : undefined;
5078
+ if (errorCode === "EADDRINUSE") {
5079
+ this.wsServer = null;
5080
+ continue;
5081
+ }
5082
+ this.wsServer = null;
5083
+ break;
5084
+ }
5085
+ }
5086
+ }
5059
5087
  if (!boundPort) {
5060
5088
  this.wsStartupError = {
5061
5089
  code: "EADDRINUSE",