@geometra/mcp 1.25.0 → 1.26.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 +13 -3
- package/dist/session.d.ts +5 -0
- package/dist/session.js +19 -4
- package/package.json +1 -1
package/dist/server.js
CHANGED
|
@@ -3,7 +3,7 @@ import { performance } from 'node:perf_hooks';
|
|
|
3
3
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
4
4
|
import { z } from 'zod';
|
|
5
5
|
import { formatConnectFailureMessage, isHttpUrl, normalizeConnectTarget } from './connect-utils.js';
|
|
6
|
-
import { connect, connectThroughProxy, disconnect, getSession, listSessions, getDefaultSessionId, prewarmProxy, sendClick, sendFillFields, sendType, sendKey, sendFileUpload, sendFieldText, sendFieldChoice, sendListboxPick, sendSelectOption, sendSetChecked, sendWheel, sendScreenshot, sendPdfGenerate, buildA11yTree, buildCompactUiIndex, buildFormRequiredSnapshot, buildPageModel, buildFormSchemas, expandPageSection, buildUiDelta, hasUiDelta, nodeIdForPath, nodeContextForNode, summarizeCompactIndex, summarizePageModel, summarizeUiDelta, waitForUiCondition, } from './session.js';
|
|
6
|
+
import { connect, connectThroughProxy, disconnect, getSession, listSessions, getDefaultSessionId, prewarmProxy, sendClick, sendFillFields, sendType, sendKey, sendFileUpload, sendFieldText, sendFieldChoice, sendListboxPick, sendSelectOption, sendSetChecked, sendWheel, sendScreenshot, sendPdfGenerate, buildA11yTree, buildCompactUiIndex, buildFormRequiredSnapshot, buildPageModel, buildFormSchemas, expandPageSection, buildUiDelta, hasUiDelta, nodeIdForPath, nodeContextForNode, parseSectionId, findNodeByPath, summarizeCompactIndex, summarizePageModel, summarizeUiDelta, waitForUiCondition, } from './session.js';
|
|
7
7
|
function checkedStateInput() {
|
|
8
8
|
return z
|
|
9
9
|
.union([z.boolean(), z.literal('mixed')])
|
|
@@ -1755,7 +1755,7 @@ Use this for dropdowns, location pickers, or any scrollable list where items are
|
|
|
1755
1755
|
maxScrollSteps: z.number().int().min(1).max(50).optional().default(20).describe('Max scroll steps before stopping (default 20)'),
|
|
1756
1756
|
scrollDelta: z.number().optional().default(300).describe('Vertical scroll delta per step (default 300)'),
|
|
1757
1757
|
sessionId: sessionIdInput,
|
|
1758
|
-
}, async ({ listId
|
|
1758
|
+
}, async ({ listId, role, scrollX, scrollY, maxItems, maxScrollSteps, scrollDelta, sessionId }) => {
|
|
1759
1759
|
const session = getSession(sessionId);
|
|
1760
1760
|
if (!session)
|
|
1761
1761
|
return err('Not connected. Call geometra_connect first.');
|
|
@@ -1767,7 +1767,17 @@ Use this for dropdowns, location pickers, or any scrollable list where items are
|
|
|
1767
1767
|
const a11y = await sessionA11yWhenReady(session);
|
|
1768
1768
|
if (!a11y)
|
|
1769
1769
|
break;
|
|
1770
|
-
|
|
1770
|
+
// Scope to subtree if listId is provided
|
|
1771
|
+
let searchRoot = a11y;
|
|
1772
|
+
if (listId) {
|
|
1773
|
+
const parsed = parseSectionId(listId);
|
|
1774
|
+
if (parsed) {
|
|
1775
|
+
const node = findNodeByPath(a11y, parsed.path);
|
|
1776
|
+
if (node)
|
|
1777
|
+
searchRoot = node;
|
|
1778
|
+
}
|
|
1779
|
+
}
|
|
1780
|
+
const items = findNodes(searchRoot, { role: itemRole });
|
|
1771
1781
|
let newCount = 0;
|
|
1772
1782
|
for (const item of items) {
|
|
1773
1783
|
const id = nodeIdForPath(item.path);
|
package/dist/session.d.ts
CHANGED
|
@@ -594,6 +594,10 @@ export declare function sendNavigate(session: Session, url: string, timeoutMs?:
|
|
|
594
594
|
*/
|
|
595
595
|
export declare function buildA11yTree(tree: Record<string, unknown>, layout: Record<string, unknown>): A11yNode;
|
|
596
596
|
export declare function nodeIdForPath(path: number[]): string;
|
|
597
|
+
export declare function parseSectionId(id: string): {
|
|
598
|
+
kind: PageSectionKind;
|
|
599
|
+
path: number[];
|
|
600
|
+
} | null;
|
|
597
601
|
/**
|
|
598
602
|
* Flat list of actionable / semantic nodes in the viewport, sorted with focusable first
|
|
599
603
|
* then top-to-bottom reading order. Intended to minimize LLM tokens vs a full nested tree.
|
|
@@ -608,6 +612,7 @@ export declare function buildCompactUiIndex(root: A11yNode, options?: {
|
|
|
608
612
|
context: CompactUiContext;
|
|
609
613
|
};
|
|
610
614
|
export declare function summarizeCompactIndex(nodes: CompactUiNode[], maxLines?: number): string;
|
|
615
|
+
export declare function findNodeByPath(root: A11yNode, path: number[]): A11yNode | null;
|
|
611
616
|
export declare function nodeContextForNode(root: A11yNode, node: A11yNode): NodeContextModel | undefined;
|
|
612
617
|
export declare function buildPageModel(root: A11yNode, options?: {
|
|
613
618
|
maxPrimaryActions?: number;
|
package/dist/session.js
CHANGED
|
@@ -1046,7 +1046,7 @@ function sectionPrefix(kind) {
|
|
|
1046
1046
|
function sectionIdForPath(kind, path) {
|
|
1047
1047
|
return `${sectionPrefix(kind)}:${encodePath(path)}`;
|
|
1048
1048
|
}
|
|
1049
|
-
function parseSectionId(id) {
|
|
1049
|
+
export function parseSectionId(id) {
|
|
1050
1050
|
const [prefix, encoded] = id.split(':', 2);
|
|
1051
1051
|
if (!prefix || !encoded)
|
|
1052
1052
|
return null;
|
|
@@ -1314,7 +1314,7 @@ function firstNamedDescendant(node, allowedRoles) {
|
|
|
1314
1314
|
}
|
|
1315
1315
|
return undefined;
|
|
1316
1316
|
}
|
|
1317
|
-
function findNodeByPath(root, path) {
|
|
1317
|
+
export function findNodeByPath(root, path) {
|
|
1318
1318
|
let current = root;
|
|
1319
1319
|
for (const index of path) {
|
|
1320
1320
|
if (!current.children[index])
|
|
@@ -2054,10 +2054,25 @@ export function buildPageModel(root, options) {
|
|
|
2054
2054
|
};
|
|
2055
2055
|
}
|
|
2056
2056
|
export function buildFormSchemas(root, options) {
|
|
2057
|
-
const
|
|
2057
|
+
const explicitForms = [
|
|
2058
2058
|
...(root.role === 'form' ? [root] : []),
|
|
2059
2059
|
...collectDescendants(root, candidate => candidate.role === 'form'),
|
|
2060
|
-
]
|
|
2060
|
+
];
|
|
2061
|
+
// Infer forms from group/region containers with 2+ form fields (e.g. Ashby-style UIs without <form>)
|
|
2062
|
+
const inferredForms = collectDescendants(root, candidate => {
|
|
2063
|
+
if (candidate.role !== 'group' && candidate.role !== 'region')
|
|
2064
|
+
return false;
|
|
2065
|
+
// Skip descendants of explicit forms
|
|
2066
|
+
for (const form of explicitForms) {
|
|
2067
|
+
if (candidate.path.length > form.path.length &&
|
|
2068
|
+
form.path.every((v, i) => candidate.path[i] === v)) {
|
|
2069
|
+
return false;
|
|
2070
|
+
}
|
|
2071
|
+
}
|
|
2072
|
+
const fields = collectDescendants(candidate, child => FORM_FIELD_ROLES.has(child.role));
|
|
2073
|
+
return fields.length >= 2;
|
|
2074
|
+
});
|
|
2075
|
+
const forms = sortByBounds([...explicitForms, ...inferredForms]);
|
|
2061
2076
|
return forms
|
|
2062
2077
|
.filter(form => !options?.formId || sectionIdForPath('form', form.path) === options.formId)
|
|
2063
2078
|
.map(form => buildFormSchemaForNode(root, form, options));
|
package/package.json
CHANGED