ucu-mcp 0.3.1 → 0.3.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.
- package/CHANGELOG.md +24 -0
- package/dist/src/mcp/tools.js +2 -2
- package/dist/src/platform/macos.js +22 -0
- package/dist/src/util/errors.js +2 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,30 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.3.2] - 2026-06-06
|
|
9
|
+
|
|
10
|
+
### Bug fixes
|
|
11
|
+
|
|
12
|
+
- `find_element` with `textMode="regex"` now pre-validates the `value` field for invalid regex patterns and throws `PlatformError`, mirroring the existing `text`-field validation. Before, an invalid value regex was silently swallowed by the JXA-internal `try/catch` and surfaced as "no results" instead of a clear error. (Singer Minor)
|
|
13
|
+
- `find_element` `near` sort now explicitly pushes elements without `bounds` to the end of the sorted result, instead of implicitly treating them as centered at (0,0). Improves semantics for elements without on-screen geometry. (Singer Nit)
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
|
|
17
|
+
- `find_element.value` schema is now `z.string().min(1).optional()`. Empty strings are now rejected at the schema layer with a clear validation error rather than being silently coerced to "no filter". (Singer Minor)
|
|
18
|
+
|
|
19
|
+
### Tests
|
|
20
|
+
|
|
21
|
+
- `macos-platform`: the `index out of range` test now also pins `metrics.matchedCount` to the JXA return value, locking the semantic that out-of-range indexing does not change the underlying match count. (Singer Minor)
|
|
22
|
+
|
|
23
|
+
### Tool description
|
|
24
|
+
|
|
25
|
+
- `find_element` tool description expanded to mention `value` / `index` / `near` selector support, so the model sees the new selectors at the tool level rather than only on individual parameters. (Singer Minor)
|
|
26
|
+
- `UcuError.defaultCode` lookup now has a JSDoc cross-reference explaining the relationship between the static class default and the per-instance `code` field. (Singer Minor)
|
|
27
|
+
|
|
28
|
+
### Hygiene
|
|
29
|
+
|
|
30
|
+
- Tracked 7 files removed from git tracking: `.codex/{config.toml,postmortem-interrupt-loop.md}`, `.claude/{settings.json,settings.local.json,.cozempic-init.lock}`, `docs/{.DS_Store,superpowers/.DS_Store}`. These were local-environment residue that predated the `.gitignore` rules; the ignore rules were already in place, just not enforced on the existing tracked entries. `claude-desktop-config.json` (the root-level sample for Claude Desktop MCP setup) was kept.
|
|
31
|
+
|
|
8
32
|
## [0.3.1] - 2026-06-06
|
|
9
33
|
|
|
10
34
|
### Bug fixes
|
package/dist/src/mcp/tools.js
CHANGED
|
@@ -547,12 +547,12 @@ export function registerTools(server) {
|
|
|
547
547
|
return actionResponse("move", { moved: true, x: pt.x, y: pt.y }, { x: pt.x, y: pt.y, windowId: params.windowId }, params.captureAfter, params.captureFormat, params.captureMaxWidth);
|
|
548
548
|
});
|
|
549
549
|
registry.register("move");
|
|
550
|
-
registerTool("find_element", "Find accessibility elements by text, role, or
|
|
550
|
+
registerTool("find_element", "Find accessibility elements by text, role, or value. Supports value/index/near selectors.", {
|
|
551
551
|
text: z.string().optional().describe("Text to search"), role: z.string().optional().describe("AX role"), app: z.string().optional().describe("Target app"),
|
|
552
552
|
depth: z.number().optional().describe("AX tree depth"), includeBounds: z.boolean().default(true).describe("Include bounds"), maxResults: z.number().min(1).max(200).default(50).describe("Max results"),
|
|
553
553
|
textMode: z.enum(["contains", "exact", "regex"]).default("contains").describe("Text matching mode: contains (default), exact, or regex"),
|
|
554
554
|
visibleOnly: z.boolean().default(false).describe("Only return elements with valid on-screen bounds"),
|
|
555
|
-
value: z.string().optional().describe("Filter by AX element value (
|
|
555
|
+
value: z.string().min(1).optional().describe("Filter by AX element value (text/regex/exact, see textMode). Empty string is treated as unset (omit the field instead)."),
|
|
556
556
|
index: z.number().int().nonnegative().optional().describe("Return only the Nth match (0-based) after all other filtering and sorting"),
|
|
557
557
|
near: z.object({ x: z.number(), y: z.number() }).optional().describe("Sort results by ascending distance to this point and return closest first"),
|
|
558
558
|
}, async (params) => {
|
|
@@ -820,6 +820,17 @@ export class MacOSPlatform {
|
|
|
820
820
|
throw new PlatformError(`Invalid regex pattern: ${text}`);
|
|
821
821
|
}
|
|
822
822
|
}
|
|
823
|
+
// Same pre-validation for value field when regex textMode is requested;
|
|
824
|
+
// otherwise JXA's valueMatches silently returns false on invalid regex,
|
|
825
|
+
// which surfaces as "no results" instead of a clear error.
|
|
826
|
+
if (value && textMode === "regex") {
|
|
827
|
+
try {
|
|
828
|
+
new RegExp(value);
|
|
829
|
+
}
|
|
830
|
+
catch {
|
|
831
|
+
throw new PlatformError(`Invalid regex pattern: ${value}`);
|
|
832
|
+
}
|
|
833
|
+
}
|
|
823
834
|
const startTime = Date.now();
|
|
824
835
|
const jxaScript = `
|
|
825
836
|
var se = Application('System Events');
|
|
@@ -1024,6 +1035,17 @@ export class MacOSPlatform {
|
|
|
1024
1035
|
const nx = options.near.x;
|
|
1025
1036
|
const ny = options.near.y;
|
|
1026
1037
|
finalResults = [...finalResults].sort((a, b) => {
|
|
1038
|
+
// Elements without bounds cannot be meaningfully compared against
|
|
1039
|
+
// a near point. Push them to the end of the sorted result so they
|
|
1040
|
+
// don't pollute the "closest first" ordering. (Singer Nit)
|
|
1041
|
+
const aHasBounds = !!a.bounds;
|
|
1042
|
+
const bHasBounds = !!b.bounds;
|
|
1043
|
+
if (!aHasBounds && !bHasBounds)
|
|
1044
|
+
return 0;
|
|
1045
|
+
if (!aHasBounds)
|
|
1046
|
+
return 1;
|
|
1047
|
+
if (!bHasBounds)
|
|
1048
|
+
return -1;
|
|
1027
1049
|
const acx = (a.bounds?.x ?? 0) + (a.bounds?.width ?? 0) / 2;
|
|
1028
1050
|
const acy = (a.bounds?.y ?? 0) + (a.bounds?.height ?? 0) / 2;
|
|
1029
1051
|
const bcx = (b.bounds?.x ?? 0) + (b.bounds?.width ?? 0) / 2;
|
package/dist/src/util/errors.js
CHANGED
|
@@ -16,6 +16,8 @@ export class UcuError extends Error {
|
|
|
16
16
|
constructor(message, code, retryable = false) {
|
|
17
17
|
super(message);
|
|
18
18
|
if (code === undefined) {
|
|
19
|
+
// The default code applied to instances of this class when no explicit code is passed to the constructor.
|
|
20
|
+
// See the static `defaultCode` declaration above for the per-class override mechanism.
|
|
19
21
|
code = this.constructor.defaultCode;
|
|
20
22
|
}
|
|
21
23
|
this.name = this.constructor.name;
|