@geometra/mcp 1.61.3 → 1.62.2

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.
@@ -40,7 +40,9 @@ export declare function startEmbeddedGeometraProxy(opts: SpawnProxyParams): Prom
40
40
  runtime: EmbeddedProxyRuntime;
41
41
  wsUrl: string;
42
42
  }>;
43
- export declare function parseProxyReadySignalLine(line: string): string | undefined;
43
+ export declare function parseProxyReadySignalLine(line: string, options?: {
44
+ allowLegacy?: boolean;
45
+ }): string | undefined;
44
46
  export declare function formatProxyStartupFailure(message: string, opts: SpawnProxyParams): string;
45
47
  /**
46
48
  * Spawn geometra-proxy as a child process and resolve when it emits a structured ready signal.
@@ -7,6 +7,7 @@ const require = createRequire(import.meta.url);
7
7
  const READY_SIGNAL_TYPE = 'geometra-proxy-ready';
8
8
  const READY_TIMEOUT_MS = 45_000;
9
9
  const MODULE_DIR = path.dirname(fileURLToPath(import.meta.url));
10
+ const PLAYWRIGHT_INSTALL_HINT = 'Install Chromium with the Playwright version bundled in this package: npm run browsers:install -w @geometra/proxy (repo checkout) or npx --no-install playwright install chromium.';
10
11
  /** Resolve bundled @geometra/proxy CLI entry (dist/index.js). */
11
12
  export function resolveProxyScriptPath() {
12
13
  return resolveProxyScriptPathWith(require);
@@ -125,17 +126,8 @@ function buildLocalProxyDistIfPossible(packageDir, entryFile, errors) {
125
126
  }
126
127
  return undefined;
127
128
  }
128
- function envRequestsStealth() {
129
- const explicit = process.env.GEOMETRA_STEALTH;
130
- if (explicit !== undefined) {
131
- const v = explicit.toLowerCase();
132
- return v === '1' || v === 'true' || v === 'yes' || v === 'stealth' || v === 'cloak';
133
- }
134
- const browser = (process.env.GEOMETRA_BROWSER ?? '').toLowerCase();
135
- return browser === 'stealth' || browser === 'cloak' || browser === 'cloakbrowser';
136
- }
137
129
  export function resolveStealthMode(stealth) {
138
- return stealth ?? envRequestsStealth();
130
+ return stealth ?? true;
139
131
  }
140
132
  export async function startEmbeddedGeometraProxy(opts) {
141
133
  const runtimePath = resolveProxyRuntimePath();
@@ -148,7 +140,7 @@ export async function startEmbeddedGeometraProxy(opts) {
148
140
  port: opts.port,
149
141
  width: opts.width,
150
142
  height: opts.height,
151
- headed: opts.headless !== true,
143
+ headed: opts.headless === false,
152
144
  slowMo: opts.slowMo,
153
145
  ...(opts.stealth !== undefined && { stealth: opts.stealth }),
154
146
  eagerInitialExtract: opts.eagerInitialExtract,
@@ -156,7 +148,7 @@ export async function startEmbeddedGeometraProxy(opts) {
156
148
  });
157
149
  return { runtime, wsUrl: runtime.wsUrl };
158
150
  }
159
- export function parseProxyReadySignalLine(line) {
151
+ export function parseProxyReadySignalLine(line, options) {
160
152
  const trimmed = line.trim();
161
153
  if (!trimmed)
162
154
  return undefined;
@@ -173,13 +165,15 @@ export function parseProxyReadySignalLine(line) {
173
165
  /* ignore non-JSON lines */
174
166
  }
175
167
  }
168
+ if (options?.allowLegacy === false)
169
+ return undefined;
176
170
  const fallback = trimmed.match(/WebSocket listening on (ws:\/\/127\.0\.0\.1:\d+)/);
177
171
  return fallback?.[1];
178
172
  }
179
173
  export function formatProxyStartupFailure(message, opts) {
180
174
  const hints = [];
181
175
  if (/Executable doesn't exist|playwright install chromium|browserType\.launch/i.test(message)) {
182
- hints.push('Install Chromium with: npx playwright install chromium');
176
+ hints.push(PLAYWRIGHT_INSTALL_HINT);
183
177
  }
184
178
  if (/cloakbrowser|CLOAKBROWSER|ERR_MODULE_NOT_FOUND|Cannot find package/i.test(message)) {
185
179
  hints.push('Stealth mode uses CloakBrowser. Install dependencies with npm install, or disable stealth with stealth=false / GEOMETRA_STEALTH=0. To prefetch the patched Chromium binary, run: npx cloakbrowser install');
@@ -203,10 +197,10 @@ export function spawnGeometraProxy(opts) {
203
197
  args.push('--height', String(opts.height));
204
198
  if (opts.slowMo != null && opts.slowMo > 0)
205
199
  args.push('--slow-mo', String(opts.slowMo));
206
- if (opts.headless === true)
207
- args.push('--headless');
208
- else if (opts.headless === false)
200
+ if (opts.headless === false)
209
201
  args.push('--headed');
202
+ else
203
+ args.push('--headless');
210
204
  if (opts.stealth === true)
211
205
  args.push('--stealth');
212
206
  else if (opts.stealth === false)
@@ -236,7 +230,7 @@ export function spawnGeometraProxy(opts) {
236
230
  child.stderr?.removeAllListeners('data');
237
231
  };
238
232
  const tryResolveReady = (line) => {
239
- const wsUrl = parseProxyReadySignalLine(line);
233
+ const wsUrl = parseProxyReadySignalLine(line, { allowLegacy: false });
240
234
  if (!wsUrl || settled)
241
235
  return false;
242
236
  settled = true;
package/dist/session.js CHANGED
@@ -147,7 +147,7 @@ function setReusableProxy(proxy, wsUrl, opts) {
147
147
  const existing = reusableProxies.find(entry => sameReusableProxyEntry(entry, proxy));
148
148
  if (existing) {
149
149
  existing.wsUrl = wsUrl;
150
- existing.headless = opts.headless === true;
150
+ existing.headless = opts.headless !== false;
151
151
  existing.stealth = stealth;
152
152
  existing.slowMo = opts.slowMo ?? 0;
153
153
  existing.width = opts.width ?? 1280;
@@ -163,7 +163,7 @@ function setReusableProxy(proxy, wsUrl, opts) {
163
163
  const entry = {
164
164
  child,
165
165
  wsUrl,
166
- headless: opts.headless === true,
166
+ headless: opts.headless !== false,
167
167
  stealth,
168
168
  slowMo: opts.slowMo ?? 0,
169
169
  width: opts.width ?? 1280,
@@ -190,7 +190,7 @@ function setReusableProxy(proxy, wsUrl, opts) {
190
190
  reusableProxies.push({
191
191
  runtime: proxy.runtime,
192
192
  wsUrl,
193
- headless: opts.headless === true,
193
+ headless: opts.headless !== false,
194
194
  stealth,
195
195
  slowMo: opts.slowMo ?? 0,
196
196
  width: opts.width ?? 1280,
@@ -324,6 +324,11 @@ function evictOldestSession() {
324
324
  function formatUnknownError(err) {
325
325
  return err instanceof Error ? err.message : String(err);
326
326
  }
327
+ function rejectOnRuntimeReadyFailure(runtime) {
328
+ return new Promise((_, reject) => {
329
+ runtime.ready.catch(reject);
330
+ });
331
+ }
327
332
  function warnSessionLifecycleError(action, session, err) {
328
333
  console.warn(`geometra-mcp: failed to ${action} for session ${session.id}: ${formatUnknownError(err)}`);
329
334
  }
@@ -408,7 +413,7 @@ function stopSessionHeartbeat(session) {
408
413
  }
409
414
  function reusableProxyMatchesOptions(entry, options) {
410
415
  return (entry.pageUrl === options.pageUrl &&
411
- entry.headless === (options.headless === true) &&
416
+ entry.headless === (options.headless !== false) &&
412
417
  entry.stealth === resolveStealthMode(options.stealth) &&
413
418
  entry.slowMo === (options.slowMo ?? 0) &&
414
419
  entry.width === (options.width ?? 1280) &&
@@ -426,7 +431,7 @@ function findExactReusableProxy(options) {
426
431
  }
427
432
  function findReusableProxy(options) {
428
433
  clearReusableProxiesIfExited();
429
- const desiredHeadless = options.headless === true;
434
+ const desiredHeadless = options.headless !== false;
430
435
  const desiredStealth = resolveStealthMode(options.stealth);
431
436
  const desiredSlowMo = options.slowMo ?? 0;
432
437
  const desiredWidth = options.width ?? 1280;
@@ -465,7 +470,7 @@ export async function prewarmProxy(options) {
465
470
  transport: existing.runtime ? 'embedded' : 'child',
466
471
  pageUrl: options.pageUrl,
467
472
  wsUrl: existing.wsUrl,
468
- headless: options.headless === true,
473
+ headless: options.headless !== false,
469
474
  stealth: resolveStealthMode(options.stealth),
470
475
  width: options.width ?? 1280,
471
476
  height: options.height ?? 720,
@@ -506,7 +511,7 @@ export async function prewarmProxy(options) {
506
511
  transport: 'embedded',
507
512
  pageUrl: options.pageUrl,
508
513
  wsUrl,
509
- headless: options.headless === true,
514
+ headless: options.headless !== false,
510
515
  stealth: resolveStealthMode(options.stealth),
511
516
  width: options.width ?? 1280,
512
517
  height: options.height ?? 720,
@@ -541,7 +546,7 @@ export async function prewarmProxy(options) {
541
546
  transport: 'child',
542
547
  pageUrl: options.pageUrl,
543
548
  wsUrl,
544
- headless: options.headless === true,
549
+ headless: options.headless !== false,
545
550
  stealth: resolveStealthMode(options.stealth),
546
551
  width: options.width ?? 1280,
547
552
  height: options.height ?? 720,
@@ -641,11 +646,14 @@ async function startFreshProxySession(options) {
641
646
  });
642
647
  pendingEmbeddedRuntime = runtime;
643
648
  const proxyStartMs = performance.now() - proxyStartStartedAt;
644
- const session = await connect(wsUrl, {
645
- skipInitialResize: true,
646
- closePreviousProxy: false,
647
- awaitInitialFrame: options.awaitInitialFrame,
648
- });
649
+ const session = await Promise.race([
650
+ connect(wsUrl, {
651
+ skipInitialResize: true,
652
+ closePreviousProxy: false,
653
+ awaitInitialFrame: options.awaitInitialFrame,
654
+ }),
655
+ rejectOnRuntimeReadyFailure(runtime),
656
+ ]);
649
657
  // Connect succeeded — the session now owns the runtime, so the
650
658
  // catch-block cleanup below must not also close it.
651
659
  pendingEmbeddedRuntime = undefined;
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@geometra/mcp",
3
- "version": "1.61.3",
3
+ "version": "1.62.2",
4
4
  "description": "MCP server for Geometra — interact with running Geometra apps via the geometry protocol, no browser needed",
5
5
  "license": "MIT",
6
6
  "type": "module",
7
7
  "repository": {
8
8
  "type": "git",
9
- "url": "https://github.com/razroo/geometra",
9
+ "url": "https://github.com/Agent-Pattern-Labs/geometra",
10
10
  "directory": "mcp"
11
11
  },
12
12
  "bin": {
@@ -32,7 +32,7 @@
32
32
  "ui-testing"
33
33
  ],
34
34
  "dependencies": {
35
- "@geometra/proxy": "^1.61.3",
35
+ "@geometra/proxy": "^1.62.2",
36
36
  "@modelcontextprotocol/sdk": "^1.12.1",
37
37
  "@razroo/parallel-mcp": "^0.1.0",
38
38
  "ws": "^8.18.0",