@geometra/mcp 1.59.1 → 1.60.0

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/dist/server.js CHANGED
@@ -2027,7 +2027,7 @@ Pass \`fieldLabel\` to open a labeled dropdown semantically instead of relying o
2027
2027
  .describe('Optional action wait timeout for slow dropdowns / remote search results'),
2028
2028
  detail: detailInput(),
2029
2029
  sessionId: sessionIdInput,
2030
- }, async ({ label, exact, openX, openY, fieldLabel, contextText, sectionText, query, timeoutMs, detail, sessionId }) => {
2030
+ }, async ({ label, exact, openX, openY, fieldLabel, contextText: _contextText, sectionText: _sectionText, query, timeoutMs, detail, sessionId }) => {
2031
2031
  const sessionResult = resolveToolSession(sessionId);
2032
2032
  if ('error' in sessionResult)
2033
2033
  return sessionResult.error;
@@ -4279,80 +4279,6 @@ function sortA11yNodes(nodes) {
4279
4279
  function clamp(value, min, max) {
4280
4280
  return Math.min(Math.max(value, min), max);
4281
4281
  }
4282
- function pathStartsWith(path, prefix) {
4283
- if (prefix.length > path.length)
4284
- return false;
4285
- for (let index = 0; index < prefix.length; index++) {
4286
- if (path[index] !== prefix[index])
4287
- return false;
4288
- }
4289
- return true;
4290
- }
4291
- function namedAncestors(root, path) {
4292
- const out = [];
4293
- let current = root;
4294
- for (const index of path) {
4295
- out.push(current);
4296
- if (!current.children[index])
4297
- break;
4298
- current = current.children[index];
4299
- }
4300
- return out;
4301
- }
4302
- function collectDescendants(node, predicate) {
4303
- const out = [];
4304
- function walk(current) {
4305
- for (const child of current.children) {
4306
- if (predicate(child))
4307
- out.push(child);
4308
- walk(child);
4309
- }
4310
- }
4311
- walk(node);
4312
- return out;
4313
- }
4314
- function promptContext(root, node) {
4315
- const ancestors = namedAncestors(root, node.path);
4316
- const normalizedName = (node.name ?? '').replace(/\s+/g, ' ').trim().toLowerCase();
4317
- for (let index = ancestors.length - 1; index >= 0; index--) {
4318
- const ancestor = ancestors[index];
4319
- const grouped = collectDescendants(ancestor, candidate => candidate.role === 'button' || candidate.role === 'radio' || candidate.role === 'checkbox').length >= 2;
4320
- if (!grouped && ancestor.role !== 'group' && ancestor.role !== 'form' && ancestor.role !== 'dialog')
4321
- continue;
4322
- const best = collectDescendants(ancestor, candidate => (candidate.role === 'heading' || candidate.role === 'text') &&
4323
- !!truncateInlineText(candidate.name, 120) &&
4324
- !pathStartsWith(candidate.path, node.path))
4325
- .filter(candidate => candidate.bounds.y <= node.bounds.y + 8)
4326
- .map(candidate => {
4327
- const text = truncateInlineText(candidate.name, 120);
4328
- if (!text)
4329
- return null;
4330
- if (text.toLowerCase() === normalizedName)
4331
- return null;
4332
- const dy = Math.max(0, node.bounds.y - candidate.bounds.y);
4333
- const dx = Math.abs(node.bounds.x - candidate.bounds.x);
4334
- const headingBonus = candidate.role === 'heading' ? -32 : 0;
4335
- return { text, score: dy * 4 + dx + headingBonus };
4336
- })
4337
- .filter((candidate) => !!candidate)
4338
- .sort((a, b) => a.score - b.score)[0];
4339
- if (best?.text)
4340
- return best.text;
4341
- }
4342
- return undefined;
4343
- }
4344
- function sectionContext(root, node) {
4345
- const ancestors = namedAncestors(root, node.path);
4346
- for (let index = ancestors.length - 1; index >= 0; index--) {
4347
- const ancestor = ancestors[index];
4348
- if (ancestor.role === 'form' || ancestor.role === 'dialog' || ancestor.role === 'main' || ancestor.role === 'navigation' || ancestor.role === 'region') {
4349
- const name = truncateInlineText(ancestor.name, 80);
4350
- if (name)
4351
- return name;
4352
- }
4353
- }
4354
- return undefined;
4355
- }
4356
4282
  function nodeContextText(context) {
4357
4283
  return [context?.prompt, context?.section, context?.item].filter(Boolean).join(' | ') || undefined;
4358
4284
  }
@@ -1,6 +1,7 @@
1
1
  import { mkdirSync } from 'node:fs';
2
2
  import { homedir } from 'node:os';
3
3
  import path from 'node:path';
4
+ import { threadId } from 'node:worker_threads';
4
5
  import { ParallelMcpOrchestrator, SqliteParallelMcpStore, } from '@razroo/parallel-mcp';
5
6
  const SESSION_NAMESPACE = 'geometra-mcp-session';
6
7
  const SESSION_TASK_KEY = 'session.live';
@@ -15,7 +16,7 @@ function resolveSessionStateFile() {
15
16
  }
16
17
  const dir = path.join(homedir(), '.geometra-mcp');
17
18
  mkdirSync(dir, { recursive: true });
18
- return path.join(dir, `parallel-mcp-${process.pid}.sqlite`);
19
+ return path.join(dir, `parallel-mcp-${process.pid}-${threadId}.sqlite`);
19
20
  }
20
21
  const orchestrator = new ParallelMcpOrchestrator(new SqliteParallelMcpStore({ filename: resolveSessionStateFile() }), { defaultLeaseMs: SESSION_LEASE_MS });
21
22
  const leaseSweep = setInterval(() => {
package/dist/session.d.ts CHANGED
@@ -112,8 +112,7 @@ interface PageSectionSummaryBase {
112
112
  };
113
113
  }
114
114
  /** Higher-level webpage structures extracted from the a11y tree. */
115
- export interface PageLandmark extends PageSectionSummaryBase {
116
- }
115
+ export type PageLandmark = PageSectionSummaryBase;
117
116
  export interface PagePrimaryAction {
118
117
  id: string;
119
118
  role: string;
package/dist/session.js CHANGED
@@ -534,7 +534,7 @@ export async function prewarmProxy(options) {
534
534
  };
535
535
  }
536
536
  catch (spawnFailure) {
537
- throw new Error(`Failed to prewarm embedded browser session: ${formatUnknownError(embeddedFailure)}\nChild-process proxy prewarm also failed: ${formatUnknownError(spawnFailure)}`);
537
+ throw new Error(`Failed to prewarm embedded browser session: ${formatUnknownError(embeddedFailure)}\nChild-process proxy prewarm also failed: ${formatUnknownError(spawnFailure)}`, { cause: spawnFailure });
538
538
  }
539
539
  }
540
540
  async function attachToReusableProxy(proxy, options) {
@@ -685,7 +685,6 @@ async function startFreshProxySession(options) {
685
685
  // every time we fall through to the child-process fallback path.
686
686
  if (pendingEmbeddedRuntime) {
687
687
  const leaked = pendingEmbeddedRuntime;
688
- pendingEmbeddedRuntime = undefined;
689
688
  void leaked.close().catch(() => { });
690
689
  }
691
690
  const proxyStartStartedAt = performance.now();
@@ -971,7 +970,7 @@ export async function connectThroughProxy(options) {
971
970
  }
972
971
  catch (e) {
973
972
  if (reuseFailure) {
974
- throw new Error(`Failed to recover reusable browser session after it became stale: ${formatUnknownError(reuseFailure)}\nFresh proxy start also failed: ${formatUnknownError(e)}`);
973
+ throw new Error(`Failed to recover reusable browser session after it became stale: ${formatUnknownError(reuseFailure)}\nFresh proxy start also failed: ${formatUnknownError(e)}`, { cause: e });
975
974
  }
976
975
  throw e;
977
976
  }
@@ -1076,7 +1075,7 @@ function estimateFillBatchTimeout(fields) {
1076
1075
  export function waitForUiCondition(session, predicate, timeoutMs) {
1077
1076
  return new Promise((resolve) => {
1078
1077
  const check = () => {
1079
- let matched = false;
1078
+ let matched;
1080
1079
  try {
1081
1080
  matched = predicate();
1082
1081
  }
@@ -1240,7 +1239,7 @@ async function ensureSessionConnected(session) {
1240
1239
  }
1241
1240
  })();
1242
1241
  session.reconnectInFlight = reconnectPromise;
1243
- let recovered = false;
1242
+ let recovered;
1244
1243
  try {
1245
1244
  recovered = await reconnectPromise;
1246
1245
  }
@@ -1520,7 +1519,6 @@ const DIALOG_ROLES = new Set([
1520
1519
  'dialog',
1521
1520
  'alertdialog',
1522
1521
  ]);
1523
- const FIELD_LABEL_ROLES = new Set(['textbox', 'combobox', 'checkbox', 'radio']);
1524
1522
  const CONTENT_NAME_ROLES = new Set(['heading', 'text']);
1525
1523
  function encodePath(path) {
1526
1524
  return path.length === 0 ? 'root' : path.map(part => part.toString(36)).join('.');
@@ -1797,9 +1795,6 @@ function cloneValidation(validation) {
1797
1795
  next.error = validation.error;
1798
1796
  return Object.keys(next).length > 0 ? next : undefined;
1799
1797
  }
1800
- function clonePath(path) {
1801
- return [...path];
1802
- }
1803
1798
  function sortByBounds(items) {
1804
1799
  return items.sort((a, b) => {
1805
1800
  if (a.bounds.y !== b.bounds.y)
@@ -2388,9 +2383,6 @@ function buildFormSchemaForNode(root, formNode, options) {
2388
2383
  })(),
2389
2384
  };
2390
2385
  }
2391
- function trimSchemaFieldContexts(fields) {
2392
- return presentFormSchemaFields(fields, { includeOptions: true, includeContext: 'auto' });
2393
- }
2394
2386
  function presentFormSchemaFields(fields, options) {
2395
2387
  const includeOptions = options?.includeOptions ?? false;
2396
2388
  const includeContext = options?.includeContext ?? 'auto';
@@ -2704,16 +2696,6 @@ function headingModels(node, maxHeadings, includeBounds) {
2704
2696
  ...(includeBounds ? { bounds: cloneBounds(heading.bounds) } : {}),
2705
2697
  }));
2706
2698
  }
2707
- function nestedListSummaries(node, maxLists, selfPath) {
2708
- const nestedLists = sortByBounds(collectDescendants(node, candidate => candidate.role === 'list' && pathKey(candidate.path) !== pathKey(selfPath)));
2709
- return nestedLists.slice(0, maxLists).map(list => ({
2710
- id: sectionIdForPath('list', list.path),
2711
- role: list.role,
2712
- ...(sectionDisplayName(list, 'list') ? { name: sectionDisplayName(list, 'list') } : {}),
2713
- bounds: cloneBounds(list.bounds),
2714
- itemCount: collectDescendants(list, candidate => candidate.role === 'listitem').length,
2715
- }));
2716
- }
2717
2699
  function sectionKindForNode(node) {
2718
2700
  if (node.role === 'form')
2719
2701
  return 'form';
@@ -2901,9 +2883,6 @@ function diffCompactNodes(before, after) {
2901
2883
  }
2902
2884
  return changes;
2903
2885
  }
2904
- function pageContainerKey(value) {
2905
- return `${pathKey(value.path)}|${value.name ?? ''}`;
2906
- }
2907
2886
  /**
2908
2887
  * Compare two accessibility trees at the compact viewport layer plus a few
2909
2888
  * higher-level structures (dialogs, forms, lists).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@geometra/mcp",
3
- "version": "1.59.1",
3
+ "version": "1.60.0",
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",
@@ -17,7 +17,9 @@
17
17
  "README.md"
18
18
  ],
19
19
  "scripts": {
20
- "build": "tsc",
20
+ "build": "rm -rf dist && tsc",
21
+ "check": "tsc --noEmit",
22
+ "test": "npm run check",
21
23
  "dev": "node --loader ts-node/esm src/index.ts",
22
24
  "prepublishOnly": "npm run build"
23
25
  },
@@ -30,7 +32,7 @@
30
32
  "ui-testing"
31
33
  ],
32
34
  "dependencies": {
33
- "@geometra/proxy": "^1.19.23",
35
+ "@geometra/proxy": "^1.60.0",
34
36
  "@modelcontextprotocol/sdk": "^1.12.1",
35
37
  "@razroo/parallel-mcp": "^0.1.0",
36
38
  "ws": "^8.18.0",
@@ -1 +0,0 @@
1
- export {};