@geometra/mcp 1.19.19 → 1.19.20

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
@@ -26,7 +26,7 @@ Proxy-backed sessions stay warm by default on disconnect, and MCP now keeps a sm
26
26
  |---|---|
27
27
  | `geometra_connect` | Connect with `url` (ws://…) **or** `pageUrl` (https://…) to auto-start geometra-proxy; can inline `formSchema` and/or `pageModel` for lower-turn starts |
28
28
  | `geometra_query` | Find elements by stable id, role, name, text content, ancestor/prompt context, current value, or semantic state such as `invalid`, `required`, or `busy` |
29
- | `geometra_wait_for` | Wait for a semantic condition instead of guessing sleeps (`busy`, `disabled`, alerts, values, etc.) |
29
+ | `geometra_wait_for` | Wait for a semantic condition instead of guessing sleeps (`busy`, `disabled`, alerts, values, etc.). **Strict parameters** — use `text` plus `present: false` to wait until a substring disappears (e.g. “Parsing your resume”); there is no `textGone` field |
30
30
  | `geometra_form_schema` | Compact, fill-oriented form schema with stable field ids and collapsed radio/button groups; can auto-connect from `pageUrl` / `url` |
31
31
  | `geometra_fill_form` | Fill a form from `valuesById` / `valuesByLabel` in one MCP call; can auto-connect from `pageUrl` / `url` for the lowest-token known-form path |
32
32
  | `geometra_fill_fields` | Fill labeled text/choice/toggle/file fields in one MCP call; can return final-only status for the smallest responses |
@@ -46,6 +46,10 @@ Proxy-backed sessions stay warm by default on disconnect, and MCP now keeps a sm
46
46
  | `geometra_layout` | Raw computed geometry for every node |
47
47
  | `geometra_disconnect` | Close the connection |
48
48
 
49
+ ### `geometra_wait_for` and loading banners
50
+
51
+ The tool input is **strict**: unknown keys are rejected (so mistaken names like `textGone` fail fast instead of being ignored). To wait until copy such as “Parsing…” or “Parsing your resume” is **gone**, pass a substring that matches the banner in **`text`** and set **`present`** to **`false`**. Example: `{ "text": "Parsing", "present": false }` (adjust the substring per site).
52
+
49
53
  ## Setup
50
54
 
51
55
  <details>
package/dist/server.js CHANGED
@@ -51,7 +51,11 @@ function nodeFilterShape() {
51
51
  function waitConditionShape() {
52
52
  return {
53
53
  ...nodeFilterShape(),
54
- present: z.boolean().optional().default(true).describe('Wait for a matching node to exist (default true) or disappear'),
54
+ present: z
55
+ .boolean()
56
+ .optional()
57
+ .default(true)
58
+ .describe('Wait until at least one node matches the filter (default true), or until no node matches (set false to wait out loading/parsing banners like “Parsing…” or “Parsing your resume”)'),
55
59
  timeoutMs: z
56
60
  .number()
57
61
  .int()
@@ -62,6 +66,15 @@ function waitConditionShape() {
62
66
  .describe('Maximum time to wait before returning an error (default 10000ms)'),
63
67
  };
64
68
  }
69
+ const GEOMETRA_QUERY_FILTER_REQUIRED_MESSAGE = 'Provide at least one filter (id, role, name, text, contextText, value, checked, disabled, focused, selected, expanded, invalid, required, or busy). ' +
70
+ 'This tool uses a strict schema: unknown keys are rejected. There is no textGone parameter — use text for substring matching. ' +
71
+ 'To wait until text disappears from the UI, use geometra_wait_for with text and present: false.';
72
+ const GEOMETRA_WAIT_FILTER_REQUIRED_MESSAGE = 'Provide at least one semantic filter (id, role, name, text, contextText, value, checked, disabled, focused, selected, expanded, invalid, required, or busy). ' +
73
+ 'This tool uses a strict schema: unknown keys are rejected. There is no textGone parameter — use text with a distinctive substring and present: false to wait until that text is gone ' +
74
+ '(common for “Parsing…”, “Parsing your resume”, or similar). Passing only present/timeoutMs is not enough without a filter.';
75
+ /** Strict input so unknown keys (e.g. textGone) fail parse; empty-filter checks happen in handlers / waitForSemanticCondition. */
76
+ const geometraQueryInputSchema = z.object(nodeFilterShape()).strict();
77
+ const geometraWaitForInputSchema = z.object(waitConditionShape()).strict();
65
78
  const timeoutMsInput = z.number().int().min(50).max(60_000).optional();
66
79
  const fillFieldSchema = z.discriminatedUnion('kind', [
67
80
  z.object({
@@ -195,7 +208,7 @@ const batchActionSchema = z.discriminatedUnion('type', [
195
208
  }),
196
209
  ]);
197
210
  export function createServer() {
198
- const server = new McpServer({ name: 'geometra', version: '1.19.17' }, { capabilities: { tools: {} } });
211
+ const server = new McpServer({ name: 'geometra', version: '1.19.20' }, { capabilities: { tools: {} } });
199
212
  // ── connect ──────────────────────────────────────────────────
200
213
  server.tool('geometra_connect', `Connect to a Geometra WebSocket peer, or start \`geometra-proxy\` automatically for a normal web page.
201
214
 
@@ -322,7 +335,11 @@ Chromium opens **visible** by default unless \`headless: true\`. File upload / w
322
335
  // ── query ────────────────────────────────────────────────────
323
336
  server.tool('geometra_query', `Find elements in the current Geometra UI by stable id, role, name, text content, current value, or semantic state. Returns matching elements with their exact pixel bounds {x, y, width, height}, visible in-viewport bounds, an on-screen center point, visibility / scroll-reveal hints, role, name, value, state, and tree path.
324
337
 
325
- This is the Geometra equivalent of Playwright's locator — but instant, structured, and with no browser. Use the returned bounds to click elements or assert on layout.`, nodeFilterShape(), async ({ id, role, name, text, contextText, value, checked, disabled, focused, selected, expanded, invalid, required, busy }) => {
338
+ This is the Geometra equivalent of Playwright's locator — but instant, structured, and with no browser. Use the returned bounds to click elements or assert on layout.
339
+
340
+ Unknown parameter names are rejected (strict schema). To wait until visible text goes away (e.g. a parsing banner), use geometra_wait_for with that substring in text and present: false — there is no textGone field.`,
341
+ // SDK overload typings only list raw shapes; runtime accepts ZodObject via getZodSchemaObject().
342
+ geometraQueryInputSchema, async ({ id, role, name, text, contextText, value, checked, disabled, focused, selected, expanded, invalid, required, busy }) => {
326
343
  const session = getSession();
327
344
  if (!session?.tree || !session?.layout)
328
345
  return err('Not connected. Call geometra_connect first.');
@@ -346,7 +363,7 @@ This is the Geometra equivalent of Playwright's locator — but instant, structu
346
363
  busy,
347
364
  };
348
365
  if (!hasNodeFilter(filter))
349
- return err('Provide at least one query filter (id, role, name, text, contextText, value, or state)');
366
+ return err(GEOMETRA_QUERY_FILTER_REQUIRED_MESSAGE);
350
367
  const matches = findNodes(a11y, filter);
351
368
  if (matches.length === 0) {
352
369
  return ok(`No elements found matching ${JSON.stringify(filter)}`);
@@ -356,27 +373,30 @@ This is the Geometra equivalent of Playwright's locator — but instant, structu
356
373
  });
357
374
  server.tool('geometra_wait_for', `Wait for a semantic UI condition without guessing sleep durations. Use this for slow SPA transitions, resume parsing, custom validation alerts, disabled submit buttons, and value/state confirmation before submit.
358
375
 
359
- The filter matches the same fields as geometra_query. Set \`present: false\` to wait for something to disappear (for example an alert or a "Parsing" status).`, waitConditionShape(), async ({ id, role, name, text, contextText, value, checked, disabled, focused, selected, expanded, invalid, required, busy, present, timeoutMs }) => {
376
+ The filter matches the same fields as geometra_query (strict schema — unknown keys error). Set \`present: false\` to wait until **no** node matches for example Ashby/Lever-style “Parsing your resume” or any “Parsing…” banner: \`{ "text": "Parsing", "present": false }\` (tune the substring to the site). Do not use a textGone parameter; use \`text\` + \`present: false\`.`, geometraWaitForInputSchema, async ({ id, role, name, text, contextText, value, checked, disabled, focused, selected, expanded, invalid, required, busy, present, timeoutMs }) => {
360
377
  const session = getSession();
361
378
  if (!session?.tree || !session?.layout)
362
379
  return err('Not connected. Call geometra_connect first.');
380
+ const filterProbe = {
381
+ id,
382
+ role,
383
+ name,
384
+ text,
385
+ contextText,
386
+ value,
387
+ checked,
388
+ disabled,
389
+ focused,
390
+ selected,
391
+ expanded,
392
+ invalid,
393
+ required,
394
+ busy,
395
+ };
396
+ if (!hasNodeFilter(filterProbe))
397
+ return err(GEOMETRA_WAIT_FILTER_REQUIRED_MESSAGE);
363
398
  const waited = await waitForSemanticCondition(session, {
364
- filter: {
365
- id,
366
- role,
367
- name,
368
- text,
369
- contextText,
370
- value,
371
- checked,
372
- disabled,
373
- focused,
374
- selected,
375
- expanded,
376
- invalid,
377
- required,
378
- busy,
379
- },
399
+ filter: filterProbe,
380
400
  present: present ?? true,
381
401
  timeoutMs: timeoutMs ?? 10_000,
382
402
  });
@@ -1665,7 +1685,7 @@ function compactFilterPayload(filter) {
1665
1685
  }
1666
1686
  async function waitForSemanticCondition(session, options) {
1667
1687
  if (!hasNodeFilter(options.filter)) {
1668
- return { ok: false, error: 'Provide at least one wait filter (id, role, name, text, contextText, value, or state)' };
1688
+ return { ok: false, error: GEOMETRA_WAIT_FILTER_REQUIRED_MESSAGE };
1669
1689
  }
1670
1690
  const startedAt = Date.now();
1671
1691
  const matched = await waitForUiCondition(session, () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@geometra/mcp",
3
- "version": "1.19.19",
3
+ "version": "1.19.20",
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",
@@ -30,7 +30,7 @@
30
30
  "ui-testing"
31
31
  ],
32
32
  "dependencies": {
33
- "@geometra/proxy": "^1.19.19",
33
+ "@geometra/proxy": "^1.19.20",
34
34
  "@modelcontextprotocol/sdk": "^1.12.1",
35
35
  "ws": "^8.18.0",
36
36
  "zod": "^3.23.0"