@ingcreators/annot-mcp 0.1.0 → 0.2.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/index.js CHANGED
@@ -1,56 +1,57 @@
1
- import { loadImage as _, createCanvas as B } from "@napi-rs/canvas";
2
- import st from "pixelmatch";
3
- import { readFile as at, writeFile as x } from "node:fs/promises";
4
- import { isAbsolute as w } from "node:path";
5
- import { createAnnotator as ct } from "@ingcreators/annot-annotator";
6
- import { Server as lt } from "@modelcontextprotocol/sdk/server/index.js";
7
- import { ListToolsRequestSchema as ut, CallToolRequestSchema as dt } from "@modelcontextprotocol/sdk/types.js";
8
- import { StdioServerTransport as ft } from "@modelcontextprotocol/sdk/server/stdio.js";
9
- const ht = { width: 1280, height: 800, deviceScaleFactor: 1 }, pt = "load";
10
- async function H(t, e) {
11
- const r = { ...ht, ...e.viewport }, o = e.waitFor ?? pt, n = e.fullPage ?? !1, s = await t.acquire();
12
- let i, a = !1;
13
- const c = () => {
14
- a || (a = !0, t.release());
1
+ import { SHARED_DEFS as te, DEFAULT_ENCODE_OPTIONS as oe, decodeAndEncodeImage as re, BBOX_ANNOTATION_SCHEMA as ne, bboxAnnotationsToSvg as U, BBOX_REDACT_REGION_SCHEMA as se, createAnnotator as ie } from "@ingcreators/annot-annotator";
2
+ import { BBOX_ANNOTATION_SCHEMA as At, BBOX_REDACT_REGION_SCHEMA as Tt, arrowBetween as Rt, bboxAnnotationsToSvg as It, rectForBoundingBox as _t, textAt as Ct } from "@ingcreators/annot-annotator";
3
+ import { loadImage as C, createCanvas as F } from "@napi-rs/canvas";
4
+ import ae from "pixelmatch";
5
+ import { readFile as ce, writeFile as O } from "node:fs/promises";
6
+ import { isAbsolute as b } from "node:path";
7
+ import { Server as de } from "@modelcontextprotocol/sdk/server/index.js";
8
+ import { ListToolsRequestSchema as le, CallToolRequestSchema as ue } from "@modelcontextprotocol/sdk/types.js";
9
+ import { StdioServerTransport as he } from "@modelcontextprotocol/sdk/server/stdio.js";
10
+ const fe = { width: 1280, height: 800, deviceScaleFactor: 1 }, pe = "load";
11
+ async function W(e, t) {
12
+ const o = { ...fe, ...t.viewport }, n = t.waitFor ?? pe, s = t.fullPage ?? !1, c = await e.acquire();
13
+ let r, a = !1;
14
+ const i = () => {
15
+ a || (a = !0, e.release());
15
16
  };
16
17
  try {
17
- i = await s.newContext({
18
- viewport: { width: r.width, height: r.height },
19
- deviceScaleFactor: r.deviceScaleFactor
18
+ r = await c.newContext({
19
+ viewport: { width: o.width, height: o.height },
20
+ deviceScaleFactor: o.deviceScaleFactor
20
21
  });
21
- const l = await i.newPage();
22
- await l.goto(e.url, { waitUntil: o });
23
- const u = await l.screenshot({ fullPage: n, type: "png" }), h = new Uint8Array(u.buffer, u.byteOffset, u.byteLength), T = i;
24
- return { pngBytes: h, handle: {
25
- page: l,
22
+ const d = await r.newPage();
23
+ await d.goto(t.url, { waitUntil: n });
24
+ const l = await d.screenshot({ fullPage: s, type: "png" }), v = new Uint8Array(l.buffer, l.byteOffset, l.byteLength), u = r;
25
+ return { pngBytes: v, handle: {
26
+ page: d,
26
27
  async close() {
27
28
  if (!a)
28
29
  try {
29
- await T.close();
30
+ await u.close();
30
31
  } finally {
31
- c();
32
+ i();
32
33
  }
33
34
  }
34
35
  } };
35
- } catch (l) {
36
- if (i)
36
+ } catch (d) {
37
+ if (r)
37
38
  try {
38
- await i.close();
39
+ await r.close();
39
40
  } catch {
40
41
  }
41
- throw c(), l;
42
+ throw i(), d;
42
43
  }
43
44
  }
44
- const mt = 3e4;
45
- class gt {
46
- #i;
45
+ const me = 3e4;
46
+ class ge {
47
47
  #s;
48
- #t;
49
- #o;
50
- #e = 0;
48
+ #i;
49
+ #e;
51
50
  #r;
52
- constructor(e, r = {}) {
53
- this.#i = e, this.#s = r.idleTimeoutMs ?? mt;
51
+ #t = 0;
52
+ #o;
53
+ constructor(t, o = {}) {
54
+ this.#s = t, this.#i = o.idleTimeoutMs ?? me;
54
55
  }
55
56
  /**
56
57
  * Acquire a browser instance. The pool launches lazily on the
@@ -63,9 +64,9 @@ class gt {
63
64
  * accurately.
64
65
  */
65
66
  async acquire() {
66
- return this.#n(), this.#e += 1, this.#t ? this.#t : (this.#o || (this.#o = this.#i.launch().then((e) => (this.#t = e, this.#o = void 0, e)).catch((e) => {
67
- throw this.#o = void 0, this.#e = Math.max(0, this.#e - 1), e;
68
- })), this.#o);
67
+ return this.#n(), this.#t += 1, this.#e ? this.#e : (this.#r || (this.#r = this.#s.launch().then((t) => (this.#e = t, this.#r = void 0, t)).catch((t) => {
68
+ throw this.#r = void 0, this.#t = Math.max(0, this.#t - 1), t;
69
+ })), this.#r);
69
70
  }
70
71
  /**
71
72
  * Release a previously-acquired browser. Decrements the
@@ -74,7 +75,7 @@ class gt {
74
75
  * instance; otherwise the timer fires and the browser closes.
75
76
  */
76
77
  release() {
77
- this.#e !== 0 && (this.#e -= 1, this.#e === 0 && this.#t && this.#a());
78
+ this.#t !== 0 && (this.#t -= 1, this.#t === 0 && this.#e && this.#a());
78
79
  }
79
80
  /**
80
81
  * Close the pooled browser immediately, regardless of refcount.
@@ -83,349 +84,239 @@ class gt {
83
84
  */
84
85
  async shutdown() {
85
86
  this.#n();
86
- const e = this.#t;
87
- this.#t = void 0, this.#e = 0, e && await e.close();
87
+ const t = this.#e;
88
+ this.#e = void 0, this.#t = 0, t && await t.close();
88
89
  }
89
90
  /** Refcount of outstanding borrows. Exposed for tests / metrics. */
90
91
  get activeBorrows() {
91
- return this.#e;
92
+ return this.#t;
92
93
  }
93
94
  /** Whether a browser is currently launched. Exposed for tests. */
94
95
  get isLaunched() {
95
- return this.#t !== void 0;
96
+ return this.#e !== void 0;
96
97
  }
97
98
  // ─── internals ────────────────────────────────────────────────
98
99
  #n() {
99
- this.#r && (clearTimeout(this.#r), this.#r = void 0);
100
+ this.#o && (clearTimeout(this.#o), this.#o = void 0);
100
101
  }
101
102
  #a() {
102
- this.#n(), this.#r = setTimeout(() => {
103
- const e = this.#t;
104
- this.#t = void 0, this.#r = void 0, e?.close().catch(() => {
103
+ this.#n(), this.#o = setTimeout(() => {
104
+ const t = this.#e;
105
+ this.#e = void 0, this.#o = void 0, t?.close().catch(() => {
105
106
  });
106
- }, this.#s), this.#r.unref?.();
107
+ }, this.#i), this.#o.unref?.();
107
108
  }
108
109
  }
109
- function yt(t = {}) {
110
- return new gt(
110
+ function ye(e = {}) {
111
+ return new ge(
111
112
  {
112
113
  async launch() {
113
- let e;
114
+ let t;
114
115
  try {
115
- ({ chromium: e } = await import("playwright-core"));
116
- } catch (r) {
117
- throw new U(
116
+ ({ chromium: t } = await import("playwright-core"));
117
+ } catch (o) {
118
+ throw new q(
118
119
  "Failed to load `playwright-core`. Reinstall `@ingcreators/annot-mcp` to restore the dependency.",
119
- r
120
+ o
120
121
  );
121
122
  }
122
123
  try {
123
- return await e.launch({ headless: !0 });
124
- } catch (r) {
125
- throw new U(
124
+ return await t.launch({ headless: !0 });
125
+ } catch (o) {
126
+ throw new q(
126
127
  "Failed to launch Chromium. Run `npx playwright install chromium` to download the runtime, then retry.",
127
- r
128
+ o
128
129
  );
129
130
  }
130
131
  }
131
132
  },
132
- t
133
+ e
133
134
  );
134
135
  }
135
- class U extends Error {
136
+ class q extends Error {
136
137
  cause;
137
- constructor(e, r) {
138
- super(e), this.name = "ChromiumUnavailableError", this.cause = r;
138
+ constructor(t, o) {
139
+ super(t), this.name = "ChromiumUnavailableError", this.cause = o;
139
140
  }
140
141
  }
141
- class F extends Error {
142
+ class N extends Error {
142
143
  locator;
143
- constructor(e, r) {
144
- super(r), this.name = "LocatorResolutionError", this.locator = e;
144
+ constructor(t, o) {
145
+ super(o), this.name = "LocatorResolutionError", this.locator = t;
145
146
  }
146
147
  }
147
- async function f(t, e) {
148
- const r = await t.locator(e).boundingBox();
149
- if (!r)
150
- throw new F(
151
- e,
152
- `Locator "${e}" resolved to no visible element. Check the selector matches at least one element and the element is inside the captured viewport.`
148
+ async function f(e, t) {
149
+ const o = await e.locator(t).boundingBox();
150
+ if (!o)
151
+ throw new N(
152
+ t,
153
+ `Locator "${t}" resolved to no visible element. Check the selector matches at least one element and the element is inside the captured viewport.`
153
154
  );
154
- return r;
155
+ return o;
155
156
  }
156
- async function wt(t, e) {
157
- switch (e.type) {
157
+ async function we(e, t) {
158
+ switch (t.type) {
158
159
  case "rect": {
159
- const r = e.bbox ?? await f(t, p(e.locator));
160
- return { ...m(e), type: "rect", bbox: r };
160
+ const o = t.bbox ?? await f(e, m(t.locator));
161
+ return { ...g(t), type: "rect", bbox: o };
161
162
  }
162
163
  case "circle": {
163
- if (e.center !== void 0 && e.radius !== void 0)
164
+ if (t.center !== void 0 && t.radius !== void 0)
164
165
  return {
165
- ...m(e),
166
+ ...g(t),
166
167
  type: "circle",
167
- center: e.center,
168
- radius: e.radius
168
+ center: t.center,
169
+ radius: t.radius
169
170
  };
170
- const r = await f(t, p(e.locator));
171
+ const o = await f(e, m(t.locator));
171
172
  return {
172
- ...m(e),
173
+ ...g(t),
173
174
  type: "circle",
174
- center: S(r),
175
- radius: Math.min(r.width, r.height) / 2
175
+ center: T(o),
176
+ radius: Math.min(o.width, o.height) / 2
176
177
  };
177
178
  }
178
179
  case "arrow": {
179
- const r = e.from ?? S(await f(t, p(e.fromLocator))), o = e.to ?? S(await f(t, p(e.toLocator)));
180
+ const o = t.from ?? T(await f(e, m(t.fromLocator))), n = t.to ?? T(await f(e, m(t.toLocator)));
180
181
  return {
181
- ...m(e),
182
+ ...g(t),
182
183
  type: "arrow",
183
- from: r,
184
- to: o
184
+ from: o,
185
+ to: n
185
186
  };
186
187
  }
187
188
  case "text": {
188
- const r = e.at ?? vt(
189
- await f(t, p(e.locator)),
190
- e.fontSize
191
- ), o = {
192
- ...m(e),
189
+ const o = t.at ?? $e(
190
+ await f(e, m(t.locator)),
191
+ t.fontSize
192
+ ), n = {
193
+ ...g(t),
193
194
  type: "text",
194
- at: r,
195
- content: e.content
195
+ at: o,
196
+ content: t.content
196
197
  };
197
- return e.fontSize !== void 0 && (o.fontSize = e.fontSize), e.anchor !== void 0 && (o.anchor = e.anchor), o;
198
+ return t.fontSize !== void 0 && (n.fontSize = t.fontSize), t.anchor !== void 0 && (n.anchor = t.anchor), n;
198
199
  }
199
200
  case "callout": {
200
- const r = e.at ?? await $t(t, e.atLocator), o = e.targetBbox ?? await f(t, p(e.targetLocator));
201
+ const o = t.at ?? await ve(e, t.atLocator), n = t.targetBbox ?? await f(e, m(t.targetLocator));
201
202
  return {
202
- ...m(e),
203
+ ...g(t),
203
204
  type: "callout",
204
- at: r,
205
- targetBbox: o,
206
- content: e.content
205
+ at: o,
206
+ targetBbox: n,
207
+ content: t.content
207
208
  };
208
209
  }
209
210
  case "raw":
210
- return e;
211
+ return t;
211
212
  }
212
213
  }
213
- async function bt(t, e) {
214
- const r = [];
215
- for (const o of e)
216
- r.push(await wt(t, o));
217
- return r;
214
+ async function be(e, t) {
215
+ const o = [];
216
+ for (const n of t)
217
+ o.push(await we(e, n));
218
+ return o;
218
219
  }
219
- async function $t(t, e) {
220
- if (!e)
221
- throw new F(
220
+ async function ve(e, t) {
221
+ if (!t)
222
+ throw new N(
222
223
  "<callout.at>",
223
224
  "callout annotations require either `at` or `atLocator`."
224
225
  );
225
- const r = await f(t, e);
226
- return S(r);
226
+ const o = await f(e, t);
227
+ return T(o);
227
228
  }
228
- function S(t) {
229
- return { x: t.x + t.width / 2, y: t.y + t.height / 2 };
229
+ function T(e) {
230
+ return { x: e.x + e.width / 2, y: e.y + e.height / 2 };
230
231
  }
231
- function vt(t, e) {
232
- const r = (e ?? 14) + 2;
233
- return { x: t.x, y: Math.max(0, t.y - r) };
232
+ function $e(e, t) {
233
+ const o = (t ?? 14) + 2;
234
+ return { x: e.x, y: Math.max(0, e.y - o) };
234
235
  }
235
- function p(t) {
236
- if (t === void 0)
237
- throw new F(
236
+ function m(e) {
237
+ if (e === void 0)
238
+ throw new N(
238
239
  "<missing>",
239
240
  "Internal error: annotation passed schema validation without a locator or bbox. This is a bug."
240
241
  );
241
- return t;
242
+ return e;
242
243
  }
243
- function m(t) {
244
- if (t.type === "raw")
244
+ function g(e) {
245
+ if (e.type === "raw")
245
246
  return {};
246
- const e = {};
247
- return t.intent !== void 0 && (e.intent = t.intent), t.stroke !== void 0 && (e.stroke = t.stroke), t.strokeWidth !== void 0 && (e.strokeWidth = t.strokeWidth), t.fill !== void 0 && (e.fill = t.fill), t.color !== void 0 && (e.color = t.color), e;
248
- }
249
- const xt = 4;
250
- function Et(t, e, r) {
251
- const o = new Uint8Array(e * r), n = [];
252
- for (let s = 0; s < r; s++)
253
- for (let i = 0; i < e; i++) {
254
- const a = s * e + i;
255
- if (o[a] || !X(t, a)) continue;
256
- const c = Pt(t, o, i, s, e, r);
257
- c.count >= xt && n.push({
258
- x: c.minX,
259
- y: c.minY,
260
- width: c.maxX - c.minX + 1,
261
- height: c.maxY - c.minY + 1
247
+ const t = {};
248
+ return e.intent !== void 0 && (t.intent = e.intent), e.stroke !== void 0 && (t.stroke = e.stroke), e.strokeWidth !== void 0 && (t.strokeWidth = e.strokeWidth), e.fill !== void 0 && (t.fill = e.fill), e.color !== void 0 && (t.color = e.color), t;
249
+ }
250
+ const Ee = 4;
251
+ function xe(e, t, o) {
252
+ const n = new Uint8Array(t * o), s = [];
253
+ for (let c = 0; c < o; c++)
254
+ for (let r = 0; r < t; r++) {
255
+ const a = c * t + r;
256
+ if (n[a] || !X(e, a)) continue;
257
+ const i = Oe(e, n, r, c, t, o);
258
+ i.count >= Ee && s.push({
259
+ x: i.minX,
260
+ y: i.minY,
261
+ width: i.maxX - i.minX + 1,
262
+ height: i.maxY - i.minY + 1
262
263
  });
263
264
  }
264
- return n;
265
+ return s;
265
266
  }
266
- function X(t, e) {
267
- return t[e * 4 + 3] > 0;
267
+ function X(e, t) {
268
+ return e[t * 4 + 3] > 0;
268
269
  }
269
- function Pt(t, e, r, o, n, s) {
270
- const i = {
270
+ function Oe(e, t, o, n, s, c) {
271
+ const r = {
271
272
  count: 0,
272
- minX: r,
273
- minY: o,
274
- maxX: r,
275
- maxY: o
276
- }, a = [r, o];
273
+ minX: o,
274
+ minY: n,
275
+ maxX: o,
276
+ maxY: n
277
+ }, a = [o, n];
277
278
  for (; a.length > 0; ) {
278
- const c = a.pop(), l = a.pop();
279
- if (l < 0 || l >= n || c < 0 || c >= s) continue;
280
- const u = c * n + l;
281
- e[u] || X(t, u) && (e[u] = 1, i.count += 1, l < i.minX && (i.minX = l), c < i.minY && (i.minY = c), l > i.maxX && (i.maxX = l), c > i.maxY && (i.maxY = c), a.push(l + 1, c), a.push(l - 1, c), a.push(l, c + 1), a.push(l, c - 1));
279
+ const i = a.pop(), d = a.pop();
280
+ if (d < 0 || d >= s || i < 0 || i >= c) continue;
281
+ const l = i * s + d;
282
+ t[l] || X(e, l) && (t[l] = 1, r.count += 1, d < r.minX && (r.minX = d), i < r.minY && (r.minY = i), d > r.maxX && (r.maxX = d), i > r.maxY && (r.maxY = i), a.push(d + 1, i), a.push(d - 1, i), a.push(d, i + 1), a.push(d, i - 1));
282
283
  }
283
- return i;
284
+ return r;
284
285
  }
285
- class Lt extends Error {
286
- constructor(e, r) {
286
+ class Pe extends Error {
287
+ constructor(t, o) {
287
288
  super(
288
- `Cannot compare screenshots of different dimensions: before is ${e.width}×${e.height}, after is ${r.width}×${r.height}.`
289
+ `Cannot compare screenshots of different dimensions: before is ${t.width}×${t.height}, after is ${o.width}×${o.height}.`
289
290
  ), this.name = "DimensionMismatchError";
290
291
  }
291
292
  }
292
- async function Ot(t, e, r = {}) {
293
- const [o, n] = await Promise.all([
294
- _(Buffer.from(t)),
295
- _(Buffer.from(e))
293
+ async function Se(e, t, o = {}) {
294
+ const [n, s] = await Promise.all([
295
+ C(Buffer.from(e)),
296
+ C(Buffer.from(t))
296
297
  ]);
297
- if (o.width !== n.width || o.height !== n.height)
298
- throw new Lt(
299
- { width: o.width, height: o.height },
300
- { width: n.width, height: n.height }
298
+ if (n.width !== s.width || n.height !== s.height)
299
+ throw new Pe(
300
+ { width: n.width, height: n.height },
301
+ { width: s.width, height: s.height }
301
302
  );
302
- const { width: s, height: i } = o, a = N(o, s, i), c = N(n, s, i), l = new Uint8Array(s * i * 4), u = st(a, c, l, s, i, {
303
- threshold: r.threshold ?? 0.1,
303
+ const { width: c, height: r } = n, a = k(n, c, r), i = k(s, c, r), d = new Uint8Array(c * r * 4), l = ae(a, i, d, c, r, {
304
+ threshold: o.threshold ?? 0.1,
304
305
  includeAA: !1,
305
306
  diffMask: !0
306
- }), h = Et(l, s, i);
307
- return { mismatchedPixels: u, regions: h, width: s, height: i };
307
+ }), v = xe(d, c, r);
308
+ return { mismatchedPixels: l, regions: v, width: c, height: r };
308
309
  }
309
- function N(t, e, r) {
310
- const n = B(e, r).getContext("2d");
311
- n.drawImage(t, 0, 0);
312
- const s = n.getImageData(0, 0, e, r).data;
313
- return new Uint8Array(s.buffer, s.byteOffset, s.byteLength);
310
+ function k(e, t, o) {
311
+ const s = F(t, o).getContext("2d");
312
+ s.drawImage(e, 0, 0);
313
+ const c = s.getImageData(0, 0, t, o).data;
314
+ return new Uint8Array(c.buffer, c.byteOffset, c.byteLength);
314
315
  }
315
- const R = {
316
- BBox: {
317
- type: "object",
318
- required: ["x", "y", "width", "height"],
319
- additionalProperties: !1,
320
- properties: {
321
- x: { type: "number" },
322
- y: { type: "number" },
323
- width: { type: "number", minimum: 0 },
324
- height: { type: "number", minimum: 0 }
325
- }
326
- },
327
- Point: {
328
- type: "object",
329
- required: ["x", "y"],
330
- additionalProperties: !1,
331
- properties: {
332
- x: { type: "number" },
333
- y: { type: "number" }
334
- }
335
- },
336
- Intent: {
337
- type: "string",
338
- enum: ["info", "warning", "error", "success", "neutral"]
339
- },
340
- AnnotationStyle: {
341
- type: "object",
342
- additionalProperties: !1,
343
- properties: {
344
- intent: { $ref: "#/$defs/Intent" },
345
- stroke: { type: "string" },
346
- strokeWidth: { type: "number", minimum: 0 },
347
- fill: { type: "string" },
348
- color: { type: "string" }
349
- }
350
- },
316
+ const I = {
317
+ ...te,
351
318
  Locator: { type: "string", minLength: 1 }
352
- }, St = {
353
- type: "object",
354
- required: ["type", "bbox"],
355
- additionalProperties: !1,
356
- properties: {
357
- type: { const: "rect" },
358
- bbox: { $ref: "#/$defs/BBox" },
359
- intent: { $ref: "#/$defs/Intent" },
360
- stroke: { type: "string" },
361
- strokeWidth: { type: "number", minimum: 0 },
362
- fill: { type: "string" },
363
- color: { type: "string" }
364
- }
365
- }, At = {
366
- type: "object",
367
- required: ["type", "center", "radius"],
368
- additionalProperties: !1,
369
- properties: {
370
- type: { const: "circle" },
371
- center: { $ref: "#/$defs/Point" },
372
- radius: { type: "number", minimum: 0 },
373
- intent: { $ref: "#/$defs/Intent" },
374
- stroke: { type: "string" },
375
- strokeWidth: { type: "number", minimum: 0 },
376
- fill: { type: "string" },
377
- color: { type: "string" }
378
- }
379
- }, Rt = {
380
- type: "object",
381
- required: ["type", "from", "to"],
382
- additionalProperties: !1,
383
- properties: {
384
- type: { const: "arrow" },
385
- from: { $ref: "#/$defs/Point" },
386
- to: { $ref: "#/$defs/Point" },
387
- intent: { $ref: "#/$defs/Intent" },
388
- stroke: { type: "string" },
389
- strokeWidth: { type: "number", minimum: 0 },
390
- color: { type: "string" }
391
- }
392
- }, Tt = {
393
- type: "object",
394
- required: ["type", "at", "content"],
395
- additionalProperties: !1,
396
- properties: {
397
- type: { const: "text" },
398
- at: { $ref: "#/$defs/Point" },
399
- content: { type: "string" },
400
- fontSize: { type: "number", minimum: 0 },
401
- anchor: { type: "string", enum: ["start", "middle", "end"] },
402
- intent: { $ref: "#/$defs/Intent" },
403
- color: { type: "string" }
404
- }
405
- }, kt = {
406
- type: "object",
407
- required: ["type", "at", "targetBbox", "content"],
408
- additionalProperties: !1,
409
- properties: {
410
- type: { const: "callout" },
411
- at: { $ref: "#/$defs/Point" },
412
- targetBbox: { $ref: "#/$defs/BBox" },
413
- content: { type: "string" },
414
- intent: { $ref: "#/$defs/Intent" },
415
- stroke: { type: "string" },
416
- color: { type: "string" }
417
- }
418
- }, z = {
419
- type: "object",
420
- required: ["type", "svgFragment"],
421
- additionalProperties: !1,
422
- properties: {
423
- type: { const: "raw" },
424
- svgFragment: { type: "string" }
425
- }
426
- }, It = {
427
- oneOf: [St, At, Rt, Tt, kt, z]
428
- }, _t = {
319
+ }, Le = {
429
320
  type: "object",
430
321
  required: ["type"],
431
322
  additionalProperties: !1,
@@ -440,7 +331,7 @@ const R = {
440
331
  color: { type: "string" }
441
332
  },
442
333
  anyOf: [{ required: ["bbox"] }, { required: ["locator"] }]
443
- }, Bt = {
334
+ }, Ae = {
444
335
  type: "object",
445
336
  required: ["type"],
446
337
  additionalProperties: !1,
@@ -456,7 +347,7 @@ const R = {
456
347
  color: { type: "string" }
457
348
  },
458
349
  anyOf: [{ required: ["center", "radius"] }, { required: ["locator"] }]
459
- }, Ft = {
350
+ }, Te = {
460
351
  type: "object",
461
352
  required: ["type"],
462
353
  additionalProperties: !1,
@@ -475,7 +366,7 @@ const R = {
475
366
  { anyOf: [{ required: ["from"] }, { required: ["fromLocator"] }] },
476
367
  { anyOf: [{ required: ["to"] }, { required: ["toLocator"] }] }
477
368
  ]
478
- }, Ct = {
369
+ }, Re = {
479
370
  type: "object",
480
371
  required: ["type", "content"],
481
372
  additionalProperties: !1,
@@ -490,7 +381,7 @@ const R = {
490
381
  color: { type: "string" }
491
382
  },
492
383
  anyOf: [{ required: ["at"] }, { required: ["locator"] }]
493
- }, Ut = {
384
+ }, Ie = {
494
385
  type: "object",
495
386
  required: ["type", "content"],
496
387
  additionalProperties: !1,
@@ -509,18 +400,48 @@ const R = {
509
400
  { anyOf: [{ required: ["at"] }, { required: ["atLocator"] }] },
510
401
  { anyOf: [{ required: ["targetBbox"] }, { required: ["targetLocator"] }] }
511
402
  ]
512
- }, Nt = {
513
- oneOf: [_t, Bt, Ft, Ct, Ut, z]
514
- }, qt = {
403
+ }, _e = {
515
404
  type: "object",
516
- required: ["bbox"],
405
+ required: ["type", "svgFragment"],
517
406
  additionalProperties: !1,
518
407
  properties: {
519
- bbox: { $ref: "#/$defs/BBox" },
520
- style: { type: "string", enum: ["solid", "mosaic", "blur"] },
521
- color: { type: "string" }
408
+ type: { const: "raw" },
409
+ svgFragment: { type: "string" }
522
410
  }
523
- }, Wt = {
411
+ }, Ce = {
412
+ oneOf: [Le, Ae, Te, Re, Ie, _e]
413
+ }, P = {
414
+ type: "object",
415
+ additionalProperties: !1,
416
+ properties: {
417
+ format: {
418
+ type: "string",
419
+ enum: ["smart", "png", "jpeg"],
420
+ description: "Encoding strategy. `smart` (default) picks PNG-8 for UI-heavy content and PNG-32 / JPEG for photo-heavy content per `smartFallback`. `png` forces PNG-32 (lossless). `jpeg` forces JPEG at `jpegPercent` quality."
421
+ },
422
+ saveSizePreset: {
423
+ type: "string",
424
+ enum: ["light", "standard", "highQuality", "original"],
425
+ description: "Max-width cap. `light` = 1280px, `standard` = 1920px (default), `highQuality` = 2560px, `original` = no resize. Aspect-preserving; never upscales."
426
+ },
427
+ smartFallback: {
428
+ type: "string",
429
+ enum: ["png", "jpeg"],
430
+ description: "Fallback format used by smart mode when the image is photo-heavy. Default `png`."
431
+ },
432
+ smartColorThreshold: {
433
+ type: "integer",
434
+ minimum: 1,
435
+ description: "Unique-colour count above which smart mode treats the image as photo-heavy. Default 15000."
436
+ },
437
+ jpegPercent: {
438
+ type: "integer",
439
+ minimum: 60,
440
+ maximum: 100,
441
+ description: "JPEG quality 60–100. Default 92."
442
+ }
443
+ }
444
+ }, Ue = {
524
445
  type: "object",
525
446
  additionalProperties: !1,
526
447
  properties: {
@@ -530,237 +451,144 @@ const R = {
530
451
  color: { type: "string" }
531
452
  },
532
453
  anyOf: [{ required: ["bbox"] }, { required: ["locator"] }]
533
- };
534
- function Y(t, e = {}) {
535
- const r = e.stroke ?? "red", o = e.strokeWidth ?? 2, n = e.fill ?? "none";
536
- return `<rect x="${t.x}" y="${t.y}" width="${t.width}" height="${t.height}" fill="${v(n)}" stroke="${v(r)}" stroke-width="${o}"/>`;
537
- }
538
- function V(t, e, r = {}) {
539
- const o = r.color ?? "red", n = r.strokeWidth ?? 2, s = `annot-mcp-arrow-${Dt()}`;
540
- return `<defs><marker id="${s}" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 5 L 0 10 z" fill="${v(o)}"/></marker></defs><line x1="${t.x}" y1="${t.y}" x2="${e.x}" y2="${e.y}" stroke="${v(o)}" stroke-width="${n}" marker-end="url(#${s})"/>`;
541
- }
542
- function J(t, e, r = {}) {
543
- const o = r.color ?? "red", n = r.fontSize ?? 14, s = r.anchor ?? "start";
544
- return `<text x="${t.x}" y="${t.y}" fill="${v(o)}" font-size="${n}" text-anchor="${s}">` + Gt(e) + "</text>";
545
- }
546
- let k = 0;
547
- function Dt() {
548
- return k = k + 1 | 0, k;
549
- }
550
- function v(t) {
551
- return t.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
552
- }
553
- function Gt(t) {
554
- return t.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
555
- }
556
- const Mt = {
557
- info: { stroke: "#3b82f6", fill: "rgba(59, 130, 246, 0.12)", text: "#1e40af" },
558
- warning: { stroke: "#f59e0b", fill: "rgba(245, 158, 11, 0.12)", text: "#92400e" },
559
- error: { stroke: "#ef4444", fill: "rgba(239, 68, 68, 0.12)", text: "#991b1b" },
560
- success: { stroke: "#10b981", fill: "rgba(16, 185, 129, 0.12)", text: "#065f46" },
561
- neutral: { stroke: "#6b7280", fill: "rgba(107, 114, 128, 0.12)", text: "#374151" }
562
- }, jt = "error";
563
- function E(t) {
564
- const e = t.intent ?? jt, r = Mt[e];
565
- return {
566
- stroke: t.stroke ?? r.stroke,
567
- fill: t.fill ?? "none",
568
- text: t.color ?? r.text
569
- };
570
- }
571
- function C(t) {
572
- return t.map(Ht).join("");
573
- }
574
- function Ht(t) {
575
- switch (t.type) {
576
- case "rect":
577
- return Xt(t);
578
- case "circle":
579
- return zt(t);
580
- case "arrow":
581
- return Yt(t);
582
- case "text":
583
- return Vt(t);
584
- case "callout":
585
- return Jt(t);
586
- case "raw":
587
- return Kt(t);
588
- }
589
- }
590
- function Xt(t) {
591
- const e = E(t);
592
- return Y(t.bbox, {
593
- stroke: e.stroke,
594
- strokeWidth: t.strokeWidth ?? 2,
595
- fill: e.fill
596
- });
597
- }
598
- function zt(t) {
599
- const e = E(t), r = t.strokeWidth ?? 2;
600
- return `<circle cx="${t.center.x}" cy="${t.center.y}" r="${t.radius}" fill="${W(e.fill)}" stroke="${W(e.stroke)}" stroke-width="${r}"/>`;
601
- }
602
- function Yt(t) {
603
- const e = E(t);
604
- return V(t.from, t.to, {
605
- color: e.stroke,
606
- strokeWidth: t.strokeWidth ?? 2
607
- });
608
- }
609
- function Vt(t) {
610
- const e = E(t);
611
- return J(t.at, t.content, {
612
- color: e.text,
613
- fontSize: t.fontSize ?? 14,
614
- anchor: t.anchor ?? "start"
615
- });
616
- }
617
- function Jt(t) {
618
- const e = E(t), r = t.targetBbox, o = Zt(r, t.at);
619
- return Y(r, {
620
- stroke: e.stroke,
621
- strokeWidth: t.strokeWidth ?? 2,
622
- fill: e.fill
623
- }) + V(t.at, o, {
624
- color: e.stroke,
625
- strokeWidth: 2
626
- }) + J(t.at, t.content, {
627
- color: e.text,
628
- fontSize: 14,
629
- anchor: "start"
630
- });
631
- }
632
- function Kt(t) {
633
- return t.svgFragment;
634
- }
635
- function Zt(t, e) {
636
- const r = q(e.x, t.x, t.x + t.width), o = q(e.y, t.y, t.y + t.height);
637
- return { x: r, y: o };
638
- }
639
- function q(t, e, r) {
640
- return Math.min(Math.max(t, e), r);
641
- }
642
- function W(t) {
643
- return t.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
644
- }
645
- const D = [137, 80, 78, 71, 13, 10, 26, 10], P = 12, Qt = "IHDR", te = 16, ee = 20, G = 24;
646
- class $ extends Error {
647
- constructor(e) {
648
- super(e), this.name = "InvalidPngError";
454
+ }, j = [137, 80, 78, 71, 13, 10, 26, 10], L = 12, Fe = "IHDR", Ne = 16, Be = 20, G = 24;
455
+ class x extends Error {
456
+ constructor(t) {
457
+ super(t), this.name = "InvalidPngError";
649
458
  }
650
459
  }
651
- function K(t) {
652
- if (t.byteLength < G)
653
- throw new $(
654
- `Expected at least ${G} bytes for a PNG header, got ${t.byteLength}.`
460
+ function B(e) {
461
+ if (e.byteLength < G)
462
+ throw new x(
463
+ `Expected at least ${G} bytes for a PNG header, got ${e.byteLength}.`
655
464
  );
656
- for (let s = 0; s < D.length; s++)
657
- if (t[s] !== D[s])
658
- throw new $(
465
+ for (let c = 0; c < j.length; c++)
466
+ if (e[c] !== j[c])
467
+ throw new x(
659
468
  "Input does not start with the PNG signature (89 50 4E 47 0D 0A 1A 0A). Confirm the input is a PNG — JPEG / WebP / other formats are not supported in v1."
660
469
  );
661
- const e = new DataView(t.buffer, t.byteOffset, t.byteLength), r = String.fromCharCode(
662
- t[P],
663
- t[P + 1],
664
- t[P + 2],
665
- t[P + 3]
470
+ const t = new DataView(e.buffer, e.byteOffset, e.byteLength), o = String.fromCharCode(
471
+ e[L],
472
+ e[L + 1],
473
+ e[L + 2],
474
+ e[L + 3]
666
475
  );
667
- if (r !== Qt)
668
- throw new $(
669
- `Expected IHDR chunk after PNG signature, found "${r}". The file may be a corrupt or non-standard PNG.`
476
+ if (o !== Fe)
477
+ throw new x(
478
+ `Expected IHDR chunk after PNG signature, found "${o}". The file may be a corrupt or non-standard PNG.`
670
479
  );
671
- const o = e.getUint32(te, !1), n = e.getUint32(ee, !1);
672
- if (o === 0 || n === 0)
673
- throw new $(`PNG dimensions ${o}×${n} include a zero axis.`);
674
- return { width: o, height: n };
675
- }
676
- class y extends Error {
677
- constructor(e) {
678
- super(e), this.name = "InvalidImageInputError";
480
+ const n = t.getUint32(Ne, !1), s = t.getUint32(Be, !1);
481
+ if (n === 0 || s === 0)
482
+ throw new x(`PNG dimensions ${n}×${s} include a zero axis.`);
483
+ return { width: n, height: s };
484
+ }
485
+ class w extends Error {
486
+ constructor(t) {
487
+ super(t), this.name = "InvalidImageInputError";
679
488
  }
680
489
  }
681
- const re = "data:", oe = /^data:image\/png;base64,/i;
682
- async function A(t) {
683
- if (typeof t != "string" || t.length === 0)
684
- throw new y(
490
+ const De = "data:", qe = /^data:image\/png;base64,/i;
491
+ async function R(e) {
492
+ if (typeof e != "string" || e.length === 0)
493
+ throw new w(
685
494
  "`image` must be a non-empty string (data URL or absolute filesystem path)."
686
495
  );
687
- if (t.startsWith(re))
688
- return ne(t);
689
- if (!w(t))
690
- throw new y(
691
- `Filesystem path "${t}" is not absolute. Pass an absolute path so resolution doesn't depend on the MCP server's working directory.`
496
+ if (e.startsWith(De))
497
+ return ke(e);
498
+ if (!b(e))
499
+ throw new w(
500
+ `Filesystem path "${e}" is not absolute. Pass an absolute path so resolution doesn't depend on the MCP server's working directory.`
692
501
  );
693
- return ie(t);
502
+ return je(e);
694
503
  }
695
- function ne(t) {
696
- if (!oe.test(t))
697
- throw new y(
504
+ function ke(e) {
505
+ if (!qe.test(e))
506
+ throw new w(
698
507
  "Only `data:image/png;base64,...` URLs are supported. JPEG / WebP / SVG data URLs are not accepted in v1."
699
508
  );
700
- const e = t.slice(t.indexOf(",") + 1), r = se(e), o = Z(r);
701
- return { bytes: r, dataUrl: t, dimensions: o };
509
+ const t = e.slice(e.indexOf(",") + 1), o = Ge(t), n = z(o);
510
+ return { bytes: o, dataUrl: e, dimensions: n };
702
511
  }
703
- async function ie(t) {
704
- let e;
512
+ async function je(e) {
513
+ let t;
705
514
  try {
706
- const n = await at(t);
707
- e = new Uint8Array(n.buffer, n.byteOffset, n.byteLength);
708
- } catch (n) {
709
- const s = n instanceof Error ? n.message : String(n);
710
- throw new y(`Failed to read image at "${t}": ${s}`);
515
+ const s = await ce(e);
516
+ t = new Uint8Array(s.buffer, s.byteOffset, s.byteLength);
517
+ } catch (s) {
518
+ const c = s instanceof Error ? s.message : String(s);
519
+ throw new w(`Failed to read image at "${e}": ${c}`);
711
520
  }
712
- const r = Z(e), o = `data:image/png;base64,${ae(e)}`;
713
- return { bytes: e, dataUrl: o, dimensions: r };
521
+ const o = z(t), n = `data:image/png;base64,${Me(t)}`;
522
+ return { bytes: t, dataUrl: n, dimensions: o };
714
523
  }
715
- function Z(t) {
524
+ function z(e) {
716
525
  try {
717
- return K(t);
718
- } catch (e) {
719
- throw e instanceof $ ? new y(e.message) : e;
526
+ return B(e);
527
+ } catch (t) {
528
+ throw t instanceof x ? new w(t.message) : t;
720
529
  }
721
530
  }
722
- function se(t) {
723
- const e = Buffer.from(t, "base64");
724
- return new Uint8Array(e.buffer, e.byteOffset, e.byteLength);
725
- }
726
- function ae(t) {
727
- return Buffer.from(t.buffer, t.byteOffset, t.byteLength).toString("base64");
728
- }
729
- const ce = "solid", le = "#000000", M = 16, ue = 12;
730
- async function Q(t, e) {
731
- if (e.length === 0)
732
- return t;
733
- const r = await _(Buffer.from(t)), o = B(r.width, r.height), n = o.getContext("2d");
734
- n.drawImage(r, 0, 0);
735
- for (const i of e) {
736
- if (i.bbox.width <= 0 || i.bbox.height <= 0) continue;
737
- switch (i.style ?? ce) {
531
+ function Ge(e) {
532
+ const t = Buffer.from(e, "base64");
533
+ return new Uint8Array(t.buffer, t.byteOffset, t.byteLength);
534
+ }
535
+ function Me(e) {
536
+ return Buffer.from(e.buffer, e.byteOffset, e.byteLength).toString("base64");
537
+ }
538
+ const He = "solid", We = "#000000", M = 16, Xe = 12;
539
+ async function Y(e, t) {
540
+ if (t.length === 0)
541
+ return e;
542
+ const o = await C(Buffer.from(e)), n = F(o.width, o.height), s = n.getContext("2d");
543
+ s.drawImage(o, 0, 0);
544
+ for (const r of t) {
545
+ if (r.bbox.width <= 0 || r.bbox.height <= 0) continue;
546
+ switch (r.style ?? He) {
738
547
  case "solid":
739
- de(n, i.bbox, i.color ?? le);
548
+ ze(s, r.bbox, r.color ?? We);
740
549
  break;
741
550
  case "mosaic":
742
- fe(n, r, i.bbox);
551
+ Ye(s, o, r.bbox);
743
552
  break;
744
553
  case "blur":
745
- he(n, r, i.bbox);
554
+ Ve(s, o, r.bbox);
746
555
  break;
747
556
  }
748
557
  }
749
- const s = o.toBuffer("image/png");
750
- return new Uint8Array(s.buffer, s.byteOffset, s.byteLength);
558
+ const c = n.toBuffer("image/png");
559
+ return new Uint8Array(c.buffer, c.byteOffset, c.byteLength);
751
560
  }
752
- function de(t, e, r) {
753
- t.save(), t.fillStyle = r, t.fillRect(e.x, e.y, e.width, e.height), t.restore();
561
+ function ze(e, t, o) {
562
+ e.save(), e.fillStyle = o, e.fillRect(t.x, t.y, t.width, t.height), e.restore();
754
563
  }
755
- function fe(t, e, r) {
756
- const o = Math.max(1, Math.round(r.width / M)), n = Math.max(1, Math.round(r.height / M)), s = B(o, n), i = s.getContext("2d");
757
- i.imageSmoothingEnabled = !1, i.drawImage(e, r.x, r.y, r.width, r.height, 0, 0, o, n), t.save(), t.imageSmoothingEnabled = !1, t.drawImage(s, 0, 0, o, n, r.x, r.y, r.width, r.height), t.restore();
564
+ function Ye(e, t, o) {
565
+ const n = Math.max(1, Math.round(o.width / M)), s = Math.max(1, Math.round(o.height / M)), c = F(n, s), r = c.getContext("2d");
566
+ r.imageSmoothingEnabled = !1, r.drawImage(t, o.x, o.y, o.width, o.height, 0, 0, n, s), e.save(), e.imageSmoothingEnabled = !1, e.drawImage(c, 0, 0, n, s, o.x, o.y, o.width, o.height), e.restore();
758
567
  }
759
- function he(t, e, r) {
760
- t.save(), t.beginPath(), t.rect(r.x, r.y, r.width, r.height), t.clip(), t.filter = `blur(${ue}px)`, t.drawImage(e, 0, 0), t.restore();
568
+ function Ve(e, t, o) {
569
+ e.save(), e.beginPath(), e.rect(o.x, o.y, o.width, o.height), e.clip(), e.filter = `blur(${Xe}px)`, e.drawImage(t, 0, 0), e.restore();
761
570
  }
762
- const tt = "annot_annotate_screenshot", pe = {
763
- name: tt,
571
+ async function S(e, t, o) {
572
+ if (!o || Object.keys(o).length === 0)
573
+ return {
574
+ bytes: e,
575
+ mimeType: "image/png",
576
+ chosen: "png",
577
+ width: t.width,
578
+ height: t.height
579
+ };
580
+ const n = { ...oe, ...o }, s = await re(e, n);
581
+ return {
582
+ bytes: s.bytes,
583
+ mimeType: s.chosen === "jpeg" ? "image/jpeg" : "image/png",
584
+ chosen: s.chosen,
585
+ width: s.width,
586
+ height: s.height,
587
+ ...s.reason ? { reason: s.reason } : {}
588
+ };
589
+ }
590
+ const V = "annot_annotate_screenshot", Je = {
591
+ name: V,
764
592
  description: "Overlay annotations (rectangles, circles, arrows, callouts, text) on a PNG screenshot. Annotation positions are specified as bounding boxes or points; pair with `annot_annotate_url` if you want locator-driven positioning. Returns the annotated PNG inline unless `output` is specified, in which case the PNG is written to that absolute path.",
765
593
  inputSchema: {
766
594
  type: "object",
@@ -779,76 +607,92 @@ const tt = "annot_annotate_screenshot", pe = {
779
607
  output: {
780
608
  type: "string",
781
609
  description: "Optional absolute filesystem path. When set, the annotated PNG is written here and the tool returns a text confirmation; otherwise the PNG bytes are returned inline."
610
+ },
611
+ encode: {
612
+ $ref: "#/$defs/EncodeOptions",
613
+ description: 'Optional encoder settings. When set, the output PNG-32 is decoded and re-encoded per `format` / `saveSizePreset` for size savings — `format: "smart"` + `saveSizePreset: "standard"` gives ~50–80% byte reduction typical for UI screenshots.'
782
614
  }
783
615
  },
784
616
  $defs: {
785
- ...R,
786
- BboxAnnotation: It
617
+ ...I,
618
+ BboxAnnotation: ne,
619
+ EncodeOptions: P
787
620
  }
788
621
  }
789
622
  };
790
- async function me(t, e) {
623
+ async function Qe(e, t) {
791
624
  try {
792
- const r = ge(t), o = await A(r.image), n = C(r.annotations), s = e.annotator.toPng({
793
- originalDataUrl: o.dataUrl,
794
- annotationsSvg: n,
795
- width: o.dimensions.width,
796
- height: o.dimensions.height
797
- });
798
- return r.output ? (await x(r.output, s), {
799
- content: [
800
- {
801
- type: "text",
802
- text: `Wrote ${s.byteLength}-byte annotated PNG to ${r.output} (${o.dimensions.width}×${o.dimensions.height}, ${r.annotations.length} annotation${r.annotations.length === 1 ? "" : "s"}).`
803
- }
804
- ]
805
- }) : {
625
+ const o = Ke(e), n = await R(o.image), s = U(o.annotations), c = t.annotator.toPng({
626
+ originalDataUrl: n.dataUrl,
627
+ annotationsSvg: s,
628
+ width: n.dimensions.width,
629
+ height: n.dimensions.height
630
+ }), r = await S(c, n.dimensions, o.encode);
631
+ if (o.output) {
632
+ await O(o.output, r.bytes);
633
+ const a = r.reason ? `, reason: ${r.reason}` : "";
634
+ return {
635
+ content: [
636
+ {
637
+ type: "text",
638
+ text: `Wrote ${r.bytes.byteLength}-byte ${r.chosen.toUpperCase()} to ${o.output} (${r.width}×${r.height}, ${o.annotations.length} annotation${o.annotations.length === 1 ? "" : "s"}${a}).`
639
+ }
640
+ ]
641
+ };
642
+ }
643
+ return {
806
644
  content: [
807
645
  {
808
646
  type: "image",
809
- data: we(s),
810
- mimeType: "image/png"
647
+ data: et(r.bytes),
648
+ mimeType: r.mimeType
811
649
  }
812
650
  ]
813
651
  };
814
- } catch (r) {
815
- return ye(r);
652
+ } catch (o) {
653
+ return Ze(o);
816
654
  }
817
655
  }
818
- function ge(t) {
819
- if (typeof t.image != "string")
820
- throw new y("`image` is required and must be a string.");
821
- if (!Array.isArray(t.annotations))
822
- throw new I("`annotations` is required and must be an array.");
823
- const e = t.annotations;
824
- let r;
825
- if (t.output !== void 0) {
826
- if (typeof t.output != "string")
827
- throw new I("`output` must be a string when provided.");
828
- if (!w(t.output))
829
- throw new I(
830
- `\`output\` path "${t.output}" must be absolute so the file lands at a predictable location.`
656
+ function Ke(e) {
657
+ if (typeof e.image != "string")
658
+ throw new w("`image` is required and must be a string.");
659
+ if (!Array.isArray(e.annotations))
660
+ throw new A("`annotations` is required and must be an array.");
661
+ const t = e.annotations;
662
+ let o;
663
+ if (e.output !== void 0) {
664
+ if (typeof e.output != "string")
665
+ throw new A("`output` must be a string when provided.");
666
+ if (!b(e.output))
667
+ throw new A(
668
+ `\`output\` path "${e.output}" must be absolute so the file lands at a predictable location.`
831
669
  );
832
- r = t.output;
670
+ o = e.output;
671
+ }
672
+ let n;
673
+ if (e.encode !== void 0) {
674
+ if (typeof e.encode != "object" || e.encode === null)
675
+ throw new A("`encode` must be an object when provided.");
676
+ n = e.encode;
833
677
  }
834
- return { image: t.image, annotations: e, output: r };
678
+ return { image: e.image, annotations: t, output: o, encode: n };
835
679
  }
836
- class I extends Error {
837
- constructor(e) {
838
- super(e), this.name = "InvalidToolInputError";
680
+ class A extends Error {
681
+ constructor(t) {
682
+ super(t), this.name = "InvalidToolInputError";
839
683
  }
840
684
  }
841
- function ye(t) {
685
+ function Ze(e) {
842
686
  return {
843
- content: [{ type: "text", text: t instanceof Error ? `${t.name}: ${t.message}` : `Unknown error: ${String(t)}` }],
687
+ content: [{ type: "text", text: e instanceof Error ? `${e.name}: ${e.message}` : `Unknown error: ${String(e)}` }],
844
688
  isError: !0
845
689
  };
846
690
  }
847
- function we(t) {
848
- return Buffer.from(t.buffer, t.byteOffset, t.byteLength).toString("base64");
691
+ function et(e) {
692
+ return Buffer.from(e.buffer, e.byteOffset, e.byteLength).toString("base64");
849
693
  }
850
- const et = "annot_annotate_url", be = {
851
- name: et,
694
+ const J = "annot_annotate_url", tt = {
695
+ name: J,
852
696
  description: 'Open a URL in a headless browser, capture a screenshot, and overlay annotations whose positions are specified as Playwright locator strings (`button:has-text("Submit")`, `[data-testid="email"]`, `role=button[name="Sign in"]`) or bounding boxes. Returns the annotated PNG inline unless `output` is specified.',
853
697
  inputSchema: {
854
698
  type: "object",
@@ -888,104 +732,121 @@ const et = "annot_annotate_url", be = {
888
732
  output: {
889
733
  type: "string",
890
734
  description: "Optional absolute filesystem path. When set, the annotated PNG is written here and the tool returns a text confirmation."
735
+ },
736
+ encode: {
737
+ $ref: "#/$defs/EncodeOptions",
738
+ description: "Optional encoder settings (`format` / `saveSizePreset` / `jpegPercent`). Set to shrink the output for ingestion into issue trackers / docs."
891
739
  }
892
740
  },
893
741
  $defs: {
894
- ...R,
895
- LocatorAnnotation: Nt
742
+ ...I,
743
+ LocatorAnnotation: Ce,
744
+ EncodeOptions: P
896
745
  }
897
746
  }
898
747
  };
899
- class b extends Error {
900
- constructor(e) {
901
- super(e), this.name = "InvalidAnnotateUrlInputError";
748
+ class y extends Error {
749
+ constructor(t) {
750
+ super(t), this.name = "InvalidAnnotateUrlInputError";
902
751
  }
903
752
  }
904
- async function $e(t, e) {
753
+ async function ot(e, t) {
905
754
  try {
906
- const r = ve(t), o = await H(e.pool, {
907
- url: r.url,
908
- viewport: r.viewport,
909
- fullPage: r.fullPage,
910
- waitFor: r.waitFor
755
+ const o = rt(e), n = await W(t.pool, {
756
+ url: o.url,
757
+ viewport: o.viewport,
758
+ fullPage: o.fullPage,
759
+ waitFor: o.waitFor
911
760
  });
912
- let n;
761
+ let s;
913
762
  try {
914
- n = await bt(
915
- o.handle.page,
916
- r.annotations
763
+ s = await be(
764
+ n.handle.page,
765
+ o.annotations
917
766
  );
918
767
  } finally {
919
- await o.handle.close();
768
+ await n.handle.close();
920
769
  }
921
- const s = K(o.pngBytes), i = C(n), a = e.annotator.toPng({
922
- originalDataUrl: `data:image/png;base64,${j(o.pngBytes)}`,
923
- annotationsSvg: i,
924
- width: s.width,
925
- height: s.height
926
- });
927
- return r.output ? (await x(r.output, a), {
928
- content: [
929
- {
930
- type: "text",
931
- text: `Wrote ${a.byteLength}-byte annotated PNG to ${r.output} (${s.width}×${s.height}, ${r.annotations.length} annotation${r.annotations.length === 1 ? "" : "s"}, captured from ${r.url}).`
932
- }
933
- ]
934
- }) : {
770
+ const c = B(n.pngBytes), r = U(s), a = t.annotator.toPng({
771
+ originalDataUrl: `data:image/png;base64,${H(n.pngBytes)}`,
772
+ annotationsSvg: r,
773
+ width: c.width,
774
+ height: c.height
775
+ }), i = await S(a, c, o.encode);
776
+ if (o.output) {
777
+ await O(o.output, i.bytes);
778
+ const d = i.reason ? `, reason: ${i.reason}` : "";
779
+ return {
780
+ content: [
781
+ {
782
+ type: "text",
783
+ text: `Wrote ${i.bytes.byteLength}-byte ${i.chosen.toUpperCase()} to ${o.output} (${i.width}×${i.height}, ${o.annotations.length} annotation${o.annotations.length === 1 ? "" : "s"}, captured from ${o.url}${d}).`
784
+ }
785
+ ]
786
+ };
787
+ }
788
+ return {
935
789
  content: [
936
790
  {
937
791
  type: "image",
938
- data: j(a),
939
- mimeType: "image/png"
792
+ data: H(i.bytes),
793
+ mimeType: i.mimeType
940
794
  }
941
795
  ]
942
796
  };
943
- } catch (r) {
944
- return xe(r);
797
+ } catch (o) {
798
+ return nt(o);
945
799
  }
946
800
  }
947
- function ve(t) {
948
- if (typeof t.url != "string" || t.url.length === 0)
949
- throw new b("`url` is required and must be a non-empty string.");
950
- if (!Array.isArray(t.annotations))
951
- throw new b("`annotations` is required and must be an array.");
952
- const e = t.annotations;
953
- let r;
954
- if (t.viewport !== void 0) {
955
- if (typeof t.viewport != "object" || t.viewport === null)
956
- throw new b("`viewport` must be an object when provided.");
957
- const i = t.viewport, a = typeof i.width == "number" ? i.width : 1280, c = typeof i.height == "number" ? i.height : 800, l = typeof i.deviceScaleFactor == "number" ? i.deviceScaleFactor : 1;
958
- r = { width: a, height: c, deviceScaleFactor: l };
801
+ function rt(e) {
802
+ if (typeof e.url != "string" || e.url.length === 0)
803
+ throw new y("`url` is required and must be a non-empty string.");
804
+ if (!Array.isArray(e.annotations))
805
+ throw new y("`annotations` is required and must be an array.");
806
+ const t = e.annotations;
807
+ let o;
808
+ if (e.viewport !== void 0) {
809
+ if (typeof e.viewport != "object" || e.viewport === null)
810
+ throw new y("`viewport` must be an object when provided.");
811
+ const a = e.viewport, i = typeof a.width == "number" ? a.width : 1280, d = typeof a.height == "number" ? a.height : 800, l = typeof a.deviceScaleFactor == "number" ? a.deviceScaleFactor : 1;
812
+ o = { width: i, height: d, deviceScaleFactor: l };
959
813
  }
960
- const o = t.fullPage === !0, n = t.waitFor === "domcontentloaded" || t.waitFor === "networkidle" ? t.waitFor : "load";
961
- let s;
962
- if (t.output !== void 0) {
963
- if (typeof t.output != "string")
964
- throw new b("`output` must be a string when provided.");
965
- if (!w(t.output))
966
- throw new b(`\`output\` path "${t.output}" must be absolute.`);
967
- s = t.output;
814
+ const n = e.fullPage === !0, s = e.waitFor === "domcontentloaded" || e.waitFor === "networkidle" ? e.waitFor : "load";
815
+ let c;
816
+ if (e.output !== void 0) {
817
+ if (typeof e.output != "string")
818
+ throw new y("`output` must be a string when provided.");
819
+ if (!b(e.output))
820
+ throw new y(`\`output\` path "${e.output}" must be absolute.`);
821
+ c = e.output;
822
+ }
823
+ let r;
824
+ if (e.encode !== void 0) {
825
+ if (typeof e.encode != "object" || e.encode === null)
826
+ throw new y("`encode` must be an object when provided.");
827
+ r = e.encode;
968
828
  }
969
829
  return {
970
- url: t.url,
971
- annotations: e,
972
- viewport: r,
973
- fullPage: o,
974
- waitFor: n,
975
- output: s
830
+ url: e.url,
831
+ annotations: t,
832
+ viewport: o,
833
+ fullPage: n,
834
+ waitFor: s,
835
+ output: c,
836
+ encode: r
976
837
  };
977
838
  }
978
- function xe(t) {
839
+ function nt(e) {
979
840
  return {
980
- content: [{ type: "text", text: t instanceof Error ? `${t.name}: ${t.message}` : `Unknown error: ${String(t)}` }],
841
+ content: [{ type: "text", text: e instanceof Error ? `${e.name}: ${e.message}` : `Unknown error: ${String(e)}` }],
981
842
  isError: !0
982
843
  };
983
844
  }
984
- function j(t) {
985
- return Buffer.from(t.buffer, t.byteOffset, t.byteLength).toString("base64");
845
+ function H(e) {
846
+ return Buffer.from(e.buffer, e.byteOffset, e.byteLength).toString("base64");
986
847
  }
987
- const rt = "annot_compare_screenshots", Ee = {
988
- name: rt,
848
+ const Q = "annot_compare_screenshots", st = {
849
+ name: Q,
989
850
  description: "Compare two screenshots (must have identical dimensions) and return a PNG of the `after` image with changed regions highlighted as red rectangles. Useful for visual-diff PR review.",
990
851
  inputSchema: {
991
852
  type: "object",
@@ -1012,69 +873,88 @@ const rt = "annot_compare_screenshots", Ee = {
1012
873
  default: !1,
1013
874
  description: "When true, append a text content block listing the changed-region bboxes alongside the annotated PNG."
1014
875
  },
1015
- output: { type: "string" }
876
+ output: { type: "string" },
877
+ encode: { $ref: "#/$defs/EncodeOptions" }
878
+ },
879
+ $defs: {
880
+ EncodeOptions: P
1016
881
  }
1017
882
  }
1018
883
  };
1019
- class L extends Error {
1020
- constructor(e) {
1021
- super(e), this.name = "InvalidCompareInputError";
884
+ class $ extends Error {
885
+ constructor(t) {
886
+ super(t), this.name = "InvalidCompareInputError";
1022
887
  }
1023
888
  }
1024
- async function Pe(t, e) {
889
+ async function it(e, t) {
1025
890
  try {
1026
- if (typeof t.before != "string")
1027
- throw new L("`before` is required and must be a string.");
1028
- if (typeof t.after != "string")
1029
- throw new L("`after` is required and must be a string.");
1030
- const r = typeof t.threshold == "number" ? t.threshold : 0.1, o = t.includeChangeList === !0;
1031
- let n;
1032
- if (t.output !== void 0) {
1033
- if (typeof t.output != "string")
1034
- throw new L("`output` must be a string when provided.");
1035
- if (!w(t.output))
1036
- throw new L(`\`output\` path "${t.output}" must be absolute.`);
1037
- n = t.output;
891
+ if (typeof e.before != "string")
892
+ throw new $("`before` is required and must be a string.");
893
+ if (typeof e.after != "string")
894
+ throw new $("`after` is required and must be a string.");
895
+ const o = typeof e.threshold == "number" ? e.threshold : 0.1, n = e.includeChangeList === !0;
896
+ let s;
897
+ if (e.output !== void 0) {
898
+ if (typeof e.output != "string")
899
+ throw new $("`output` must be a string when provided.");
900
+ if (!b(e.output))
901
+ throw new $(`\`output\` path "${e.output}" must be absolute.`);
902
+ s = e.output;
1038
903
  }
1039
- const [s, i] = await Promise.all([
1040
- A(t.before),
1041
- A(t.after)
1042
- ]), a = await Ot(s.bytes, i.bytes, { threshold: r }), c = a.regions.map((d) => ({
904
+ let c;
905
+ if (e.encode !== void 0) {
906
+ if (typeof e.encode != "object" || e.encode === null)
907
+ throw new $("`encode` must be an object when provided.");
908
+ c = e.encode;
909
+ }
910
+ const [r, a] = await Promise.all([
911
+ R(e.before),
912
+ R(e.after)
913
+ ]), i = await Se(r.bytes, a.bytes, { threshold: o }), d = i.regions.map((h) => ({
1043
914
  type: "rect",
1044
- bbox: d,
915
+ bbox: h,
1045
916
  intent: "warning"
1046
- })), l = C(c), u = e.annotator.toPng({
1047
- originalDataUrl: i.dataUrl,
917
+ })), l = U(d), v = t.annotator.toPng({
918
+ originalDataUrl: a.dataUrl,
1048
919
  annotationsSvg: l,
1049
- width: a.width,
1050
- height: a.height
1051
- });
1052
- if (n)
1053
- return await x(n, u), { content: [{ type: "text", text: `Wrote ${u.byteLength}-byte diff-annotated PNG to ${n} (${a.width}×${a.height}, ${a.regions.length} changed region${a.regions.length === 1 ? "" : "s"}, ${a.mismatchedPixels} mismatched pixel${a.mismatchedPixels === 1 ? "" : "s"}).` }] };
1054
- const h = {
920
+ width: i.width,
921
+ height: i.height
922
+ }), u = await S(
923
+ v,
924
+ { width: i.width, height: i.height },
925
+ c
926
+ );
927
+ if (s) {
928
+ await O(s, u.bytes);
929
+ const h = u.reason ? `, reason: ${u.reason}` : "";
930
+ return { content: [{ type: "text", text: `Wrote ${u.bytes.byteLength}-byte diff-annotated ${u.chosen.toUpperCase()} to ${s} (${u.width}×${u.height}, ${i.regions.length} changed region${i.regions.length === 1 ? "" : "s"}, ${i.mismatchedPixels} mismatched pixel${i.mismatchedPixels === 1 ? "" : "s"}${h}).` }] };
931
+ }
932
+ const _ = {
1055
933
  type: "image",
1056
- data: Buffer.from(u.buffer, u.byteOffset, u.byteLength).toString(
1057
- "base64"
1058
- ),
1059
- mimeType: "image/png"
934
+ data: Buffer.from(
935
+ u.bytes.buffer,
936
+ u.bytes.byteOffset,
937
+ u.bytes.byteLength
938
+ ).toString("base64"),
939
+ mimeType: u.mimeType
1060
940
  };
1061
- if (!o)
1062
- return { content: [h] };
1063
- const T = `${a.regions.length} changed region${a.regions.length === 1 ? "" : "s"}, ${a.mismatchedPixels} mismatched pixel${a.mismatchedPixels === 1 ? "" : "s"}:
1064
- ` + a.regions.map((d, it) => ` [${it + 1}] x=${d.x} y=${d.y} w=${d.width} h=${d.height}`).join(`
941
+ if (!n)
942
+ return { content: [_] };
943
+ const ee = `${i.regions.length} changed region${i.regions.length === 1 ? "" : "s"}, ${i.mismatchedPixels} mismatched pixel${i.mismatchedPixels === 1 ? "" : "s"}:
944
+ ` + i.regions.map((h, D) => ` [${D + 1}] x=${h.x} y=${h.y} w=${h.width} h=${h.height}`).join(`
1065
945
  `);
1066
946
  return {
1067
- content: [h, { type: "text", text: T }]
947
+ content: [_, { type: "text", text: ee }]
1068
948
  };
1069
- } catch (r) {
949
+ } catch (o) {
1070
950
  return {
1071
- content: [{ type: "text", text: r instanceof Error ? `${r.name}: ${r.message}` : `Unknown error: ${String(r)}` }],
951
+ content: [{ type: "text", text: o instanceof Error ? `${o.name}: ${o.message}` : `Unknown error: ${String(o)}` }],
1072
952
  isError: !0
1073
953
  };
1074
954
  }
1075
955
  }
1076
- const ot = "annot_redact_screenshot", Le = {
1077
- name: ot,
956
+ const K = "annot_redact_screenshot", at = {
957
+ name: K,
1078
958
  description: "Destructively burn redactions (solid / mosaic / blur) into a PNG screenshot. The original pixels under each region are irrecoverably replaced. Returns the redacted PNG inline unless `output` is specified.",
1079
959
  inputSchema: {
1080
960
  type: "object",
@@ -1089,64 +969,80 @@ const ot = "annot_redact_screenshot", Le = {
1089
969
  type: "array",
1090
970
  items: { $ref: "#/$defs/BboxRedactRegion" }
1091
971
  },
1092
- output: { type: "string" }
972
+ output: { type: "string" },
973
+ encode: {
974
+ $ref: "#/$defs/EncodeOptions",
975
+ description: "Optional encoder settings applied after the redactions are burned in. Useful when the redacted screenshot is destined for an issue tracker / docs and size matters."
976
+ }
1093
977
  },
1094
978
  $defs: {
1095
- ...R,
1096
- BboxRedactRegion: qt
979
+ ...I,
980
+ BboxRedactRegion: se,
981
+ EncodeOptions: P
1097
982
  }
1098
983
  }
1099
984
  };
1100
- class O extends Error {
1101
- constructor(e) {
1102
- super(e), this.name = "InvalidRedactInputError";
985
+ class E extends Error {
986
+ constructor(t) {
987
+ super(t), this.name = "InvalidRedactInputError";
1103
988
  }
1104
989
  }
1105
- async function Oe(t) {
990
+ async function ct(e) {
1106
991
  try {
1107
- if (typeof t.image != "string")
1108
- throw new O("`image` is required and must be a string.");
1109
- if (!Array.isArray(t.regions))
1110
- throw new O("`regions` is required and must be an array.");
1111
- const e = t.regions;
1112
- let r;
1113
- if (t.output !== void 0) {
1114
- if (typeof t.output != "string")
1115
- throw new O("`output` must be a string when provided.");
1116
- if (!w(t.output))
1117
- throw new O(`\`output\` path "${t.output}" must be absolute.`);
1118
- r = t.output;
992
+ if (typeof e.image != "string")
993
+ throw new E("`image` is required and must be a string.");
994
+ if (!Array.isArray(e.regions))
995
+ throw new E("`regions` is required and must be an array.");
996
+ const t = e.regions;
997
+ let o;
998
+ if (e.output !== void 0) {
999
+ if (typeof e.output != "string")
1000
+ throw new E("`output` must be a string when provided.");
1001
+ if (!b(e.output))
1002
+ throw new E(`\`output\` path "${e.output}" must be absolute.`);
1003
+ o = e.output;
1119
1004
  }
1120
- const o = await A(t.image), n = await Q(o.bytes, e);
1121
- return r ? (await x(r, n), {
1122
- content: [
1123
- {
1124
- type: "text",
1125
- text: `Wrote ${n.byteLength}-byte redacted PNG to ${r} (${o.dimensions.width}×${o.dimensions.height}, ${e.length} region${e.length === 1 ? "" : "s"}).`
1126
- }
1127
- ]
1128
- }) : {
1005
+ let n;
1006
+ if (e.encode !== void 0) {
1007
+ if (typeof e.encode != "object" || e.encode === null)
1008
+ throw new E("`encode` must be an object when provided.");
1009
+ n = e.encode;
1010
+ }
1011
+ const s = await R(e.image), c = await Y(s.bytes, t), r = await S(c, s.dimensions, n);
1012
+ if (o) {
1013
+ await O(o, r.bytes);
1014
+ const a = r.reason ? `, reason: ${r.reason}` : "";
1015
+ return {
1016
+ content: [
1017
+ {
1018
+ type: "text",
1019
+ text: `Wrote ${r.bytes.byteLength}-byte redacted ${r.chosen.toUpperCase()} to ${o} (${r.width}×${r.height}, ${t.length} region${t.length === 1 ? "" : "s"}${a}).`
1020
+ }
1021
+ ]
1022
+ };
1023
+ }
1024
+ return {
1129
1025
  content: [
1130
1026
  {
1131
1027
  type: "image",
1132
1028
  data: Buffer.from(
1133
- n.buffer,
1134
- n.byteOffset,
1135
- n.byteLength
1029
+ r.bytes.buffer,
1030
+ r.bytes.byteOffset,
1031
+ r.bytes.byteLength
1136
1032
  ).toString("base64"),
1137
- mimeType: "image/png"
1033
+ mimeType: r.mimeType
1138
1034
  }
1139
1035
  ]
1140
1036
  };
1141
- } catch (e) {
1037
+ } catch (t) {
1142
1038
  return {
1143
- content: [{ type: "text", text: e instanceof Error ? `${e.name}: ${e.message}` : `Unknown error: ${String(e)}` }],
1039
+ content: [{ type: "text", text: t instanceof Error ? `${t.name}: ${t.message}` : `Unknown error: ${String(t)}` }],
1144
1040
  isError: !0
1145
1041
  };
1146
1042
  }
1147
1043
  }
1148
- const nt = "annot_redact_url", Se = {
1149
- name: nt,
1044
+ const Z = "annot_redact_url", dt = {
1045
+ name: Z,
1150
1046
  description: "Open a URL in a headless browser, capture a screenshot, and destructively burn redactions over regions identified by locator strings or bboxes. Returns the redacted PNG inline unless `output` is specified.",
1151
1047
  inputSchema: {
1152
1048
  type: "object",
@@ -1173,183 +1069,199 @@ const nt = "annot_redact_url", Se = {
1173
1069
  enum: ["load", "domcontentloaded", "networkidle"],
1174
1070
  default: "load"
1175
1071
  },
1176
- output: { type: "string" }
1072
+ output: { type: "string" },
1073
+ encode: { $ref: "#/$defs/EncodeOptions" }
1177
1074
  },
1178
1075
  $defs: {
1179
- ...R,
1180
- LocatorRedactRegion: Wt
1076
+ ...I,
1077
+ LocatorRedactRegion: Ue,
1078
+ EncodeOptions: P
1181
1079
  }
1182
1080
  }
1183
1081
  };
1184
- class g extends Error {
1185
- constructor(e) {
1186
- super(e), this.name = "InvalidRedactUrlInputError";
1082
+ class p extends Error {
1083
+ constructor(t) {
1084
+ super(t), this.name = "InvalidRedactUrlInputError";
1187
1085
  }
1188
1086
  }
1189
- async function Ae(t, e) {
1087
+ async function lt(e, t) {
1190
1088
  try {
1191
- const r = Re(t), o = await H(e.pool, {
1192
- url: r.url,
1193
- viewport: r.viewport,
1194
- fullPage: r.fullPage,
1195
- waitFor: r.waitFor
1089
+ const o = ut(e), n = await W(t.pool, {
1090
+ url: o.url,
1091
+ viewport: o.viewport,
1092
+ fullPage: o.fullPage,
1093
+ waitFor: o.waitFor
1196
1094
  });
1197
- let n;
1095
+ let s;
1198
1096
  try {
1199
- n = [];
1200
- for (const i of r.regions) {
1201
- const c = { bbox: await Te(o.handle.page, i) };
1202
- i.style !== void 0 && (c.style = i.style), i.color !== void 0 && (c.color = i.color), n.push(c);
1097
+ s = [];
1098
+ for (const i of o.regions) {
1099
+ const l = { bbox: await ht(n.handle.page, i) };
1100
+ i.style !== void 0 && (l.style = i.style), i.color !== void 0 && (l.color = i.color), s.push(l);
1203
1101
  }
1204
1102
  } finally {
1205
- await o.handle.close();
1103
+ await n.handle.close();
1206
1104
  }
1207
- const s = await Q(o.pngBytes, n);
1208
- return r.output ? (await x(r.output, s), {
1209
- content: [
1210
- {
1211
- type: "text",
1212
- text: `Wrote ${s.byteLength}-byte redacted PNG to ${r.output} (${r.regions.length} region${r.regions.length === 1 ? "" : "s"}, captured from ${r.url}).`
1213
- }
1214
- ]
1215
- }) : {
1105
+ const c = await Y(n.pngBytes, s), r = B(n.pngBytes), a = await S(c, r, o.encode);
1106
+ if (o.output) {
1107
+ await O(o.output, a.bytes);
1108
+ const i = a.reason ? `, reason: ${a.reason}` : "";
1109
+ return {
1110
+ content: [
1111
+ {
1112
+ type: "text",
1113
+ text: `Wrote ${a.bytes.byteLength}-byte redacted ${a.chosen.toUpperCase()} to ${o.output} (${a.width}×${a.height}, ${o.regions.length} region${o.regions.length === 1 ? "" : "s"}, captured from ${o.url}${i}).`
1114
+ }
1115
+ ]
1116
+ };
1117
+ }
1118
+ return {
1216
1119
  content: [
1217
1120
  {
1218
1121
  type: "image",
1219
1122
  data: Buffer.from(
1220
- s.buffer,
1221
- s.byteOffset,
1222
- s.byteLength
1123
+ a.bytes.buffer,
1124
+ a.bytes.byteOffset,
1125
+ a.bytes.byteLength
1223
1126
  ).toString("base64"),
1224
- mimeType: "image/png"
1127
+ mimeType: a.mimeType
1225
1128
  }
1226
1129
  ]
1227
1130
  };
1228
- } catch (r) {
1131
+ } catch (o) {
1229
1132
  return {
1230
- content: [{ type: "text", text: r instanceof Error ? `${r.name}: ${r.message}` : `Unknown error: ${String(r)}` }],
1133
+ content: [{ type: "text", text: o instanceof Error ? `${o.name}: ${o.message}` : `Unknown error: ${String(o)}` }],
1231
1134
  isError: !0
1232
1135
  };
1233
1136
  }
1234
1137
  }
1235
- function Re(t) {
1236
- if (typeof t.url != "string" || t.url.length === 0)
1237
- throw new g("`url` is required and must be a non-empty string.");
1238
- if (!Array.isArray(t.regions))
1239
- throw new g("`regions` is required and must be an array.");
1240
- const e = t.regions;
1241
- let r;
1242
- if (t.viewport !== void 0) {
1243
- if (typeof t.viewport != "object" || t.viewport === null)
1244
- throw new g("`viewport` must be an object when provided.");
1245
- const i = t.viewport, a = typeof i.width == "number" ? i.width : 1280, c = typeof i.height == "number" ? i.height : 800, l = typeof i.deviceScaleFactor == "number" ? i.deviceScaleFactor : 1;
1246
- r = { width: a, height: c, deviceScaleFactor: l };
1138
+ function ut(e) {
1139
+ if (typeof e.url != "string" || e.url.length === 0)
1140
+ throw new p("`url` is required and must be a non-empty string.");
1141
+ if (!Array.isArray(e.regions))
1142
+ throw new p("`regions` is required and must be an array.");
1143
+ const t = e.regions;
1144
+ let o;
1145
+ if (e.viewport !== void 0) {
1146
+ if (typeof e.viewport != "object" || e.viewport === null)
1147
+ throw new p("`viewport` must be an object when provided.");
1148
+ const a = e.viewport, i = typeof a.width == "number" ? a.width : 1280, d = typeof a.height == "number" ? a.height : 800, l = typeof a.deviceScaleFactor == "number" ? a.deviceScaleFactor : 1;
1149
+ o = { width: i, height: d, deviceScaleFactor: l };
1247
1150
  }
1248
- const o = t.fullPage === !0, n = t.waitFor === "domcontentloaded" || t.waitFor === "networkidle" ? t.waitFor : "load";
1249
- let s;
1250
- if (t.output !== void 0) {
1251
- if (typeof t.output != "string")
1252
- throw new g("`output` must be a string when provided.");
1253
- if (!w(t.output))
1254
- throw new g(`\`output\` path "${t.output}" must be absolute.`);
1255
- s = t.output;
1151
+ const n = e.fullPage === !0, s = e.waitFor === "domcontentloaded" || e.waitFor === "networkidle" ? e.waitFor : "load";
1152
+ let c;
1153
+ if (e.output !== void 0) {
1154
+ if (typeof e.output != "string")
1155
+ throw new p("`output` must be a string when provided.");
1156
+ if (!b(e.output))
1157
+ throw new p(`\`output\` path "${e.output}" must be absolute.`);
1158
+ c = e.output;
1256
1159
  }
1257
- return { url: t.url, regions: e, viewport: r, fullPage: o, waitFor: n, output: s };
1258
- }
1259
- async function Te(t, e) {
1260
- if (e.bbox !== void 0) return e.bbox;
1261
- if (e.locator !== void 0) return f(t, e.locator);
1262
- throw new g("redact region requires either `bbox` or `locator`.");
1263
- }
1264
- const ke = "annot-mcp", Ie = "0.1.0", _e = [
1265
- pe,
1266
- be,
1267
- Le,
1268
- Se,
1269
- Ee
1160
+ let r;
1161
+ if (e.encode !== void 0) {
1162
+ if (typeof e.encode != "object" || e.encode === null)
1163
+ throw new p("`encode` must be an object when provided.");
1164
+ r = e.encode;
1165
+ }
1166
+ return { url: e.url, regions: t, viewport: o, fullPage: n, waitFor: s, output: c, encode: r };
1167
+ }
1168
+ async function ht(e, t) {
1169
+ if (t.bbox !== void 0) return t.bbox;
1170
+ if (t.locator !== void 0) return f(e, t.locator);
1171
+ throw new p("redact region requires either `bbox` or `locator`.");
1172
+ }
1173
+ const ft = "annot-mcp", pt = "0.1.0", mt = [
1174
+ Je,
1175
+ tt,
1176
+ at,
1177
+ dt,
1178
+ st
1270
1179
  ];
1271
- function Be(t = {}) {
1272
- const e = new lt(
1180
+ function gt(e = {}) {
1181
+ const t = new de(
1273
1182
  {
1274
- name: ke,
1275
- version: t.version ?? Ie
1183
+ name: ft,
1184
+ version: e.version ?? pt
1276
1185
  },
1277
1186
  {
1278
1187
  capabilities: {
1279
1188
  tools: {}
1280
1189
  }
1281
1190
  }
1282
- ), r = t.annotator ?? ct(t.annotatorOptions ?? {}), o = t.pool ?? yt();
1283
- return e.setRequestHandler(ut, async () => ({ tools: _e })), e.setRequestHandler(dt, async (n) => {
1284
- const { name: s, arguments: i } = n.params;
1191
+ ), o = e.annotator ?? ie(e.annotatorOptions ?? {}), n = e.pool ?? ye();
1192
+ return t.setRequestHandler(le, async () => ({ tools: mt })), t.setRequestHandler(ue, async (s) => {
1193
+ const { name: c, arguments: r } = s.params;
1285
1194
  let a;
1286
- switch (s) {
1287
- case tt:
1288
- a = await me(i ?? {}, { annotator: r });
1195
+ switch (c) {
1196
+ case V:
1197
+ a = await Qe(r ?? {}, { annotator: o });
1289
1198
  break;
1290
- case et:
1291
- a = await $e(i ?? {}, { annotator: r, pool: o });
1199
+ case J:
1200
+ a = await ot(r ?? {}, { annotator: o, pool: n });
1292
1201
  break;
1293
- case ot:
1294
- a = await Oe(i ?? {});
1202
+ case K:
1203
+ a = await ct(r ?? {});
1295
1204
  break;
1296
- case nt:
1297
- a = await Ae(i ?? {}, { pool: o });
1205
+ case Z:
1206
+ a = await lt(r ?? {}, { pool: n });
1298
1207
  break;
1299
- case rt:
1300
- a = await Pe(i ?? {}, { annotator: r });
1208
+ case Q:
1209
+ a = await it(r ?? {}, { annotator: o });
1301
1210
  break;
1302
1211
  default:
1303
1212
  a = {
1304
- content: [{ type: "text", text: `Unknown tool: ${s}` }],
1213
+ content: [{ type: "text", text: `Unknown tool: ${c}` }],
1305
1214
  isError: !0
1306
1215
  };
1307
1216
  }
1308
1217
  return a;
1309
- }), e;
1218
+ }), t;
1310
1219
  }
1311
- async function Me() {
1312
- const t = Be(), e = new ft();
1313
- await t.connect(e);
1220
+ async function Pt() {
1221
+ const e = gt(), t = new he();
1222
+ await e.connect(t);
1314
1223
  }
1315
1224
  export {
1316
- tt as ANNOTATE_SCREENSHOT_TOOL_NAME,
1317
- et as ANNOTATE_URL_TOOL_NAME,
1318
- It as BBOX_ANNOTATION_SCHEMA,
1319
- qt as BBOX_REDACT_REGION_SCHEMA,
1320
- gt as BrowserPool,
1321
- rt as COMPARE_SCREENSHOTS_TOOL_NAME,
1322
- U as ChromiumUnavailableError,
1323
- Lt as DimensionMismatchError,
1324
- y as InvalidImageInputError,
1325
- $ as InvalidPngError,
1326
- Nt as LOCATOR_ANNOTATION_SCHEMA,
1327
- Wt as LOCATOR_REDACT_REGION_SCHEMA,
1328
- F as LocatorResolutionError,
1329
- ot as REDACT_SCREENSHOT_TOOL_NAME,
1330
- nt as REDACT_URL_TOOL_NAME,
1331
- R as SHARED_DEFS,
1332
- Et as aggregateDiffRegions,
1333
- pe as annotateScreenshotTool,
1334
- be as annotateUrlTool,
1335
- C as bboxAnnotationsToSvg,
1336
- Q as burnRedactions,
1337
- H as capturePage,
1338
- Ee as compareScreenshotsTool,
1339
- yt as createChromiumPool,
1340
- Be as createServer,
1341
- Ot as diffScreenshots,
1342
- me as handleAnnotateScreenshot,
1343
- $e as handleAnnotateUrl,
1344
- Pe as handleCompareScreenshots,
1345
- Oe as handleRedactScreenshot,
1346
- Ae as handleRedactUrl,
1347
- K as readPngDimensions,
1348
- Le as redactScreenshotTool,
1349
- Se as redactUrlTool,
1350
- A as resolveImageInput,
1225
+ V as ANNOTATE_SCREENSHOT_TOOL_NAME,
1226
+ J as ANNOTATE_URL_TOOL_NAME,
1227
+ At as BBOX_ANNOTATION_SCHEMA,
1228
+ Tt as BBOX_REDACT_REGION_SCHEMA,
1229
+ ge as BrowserPool,
1230
+ Q as COMPARE_SCREENSHOTS_TOOL_NAME,
1231
+ q as ChromiumUnavailableError,
1232
+ Pe as DimensionMismatchError,
1233
+ w as InvalidImageInputError,
1234
+ x as InvalidPngError,
1235
+ Ce as LOCATOR_ANNOTATION_SCHEMA,
1236
+ Ue as LOCATOR_REDACT_REGION_SCHEMA,
1237
+ N as LocatorResolutionError,
1238
+ K as REDACT_SCREENSHOT_TOOL_NAME,
1239
+ Z as REDACT_URL_TOOL_NAME,
1240
+ I as SHARED_DEFS,
1241
+ xe as aggregateDiffRegions,
1242
+ Je as annotateScreenshotTool,
1243
+ tt as annotateUrlTool,
1244
+ Rt as arrowBetween,
1245
+ It as bboxAnnotationsToSvg,
1246
+ Y as burnRedactions,
1247
+ W as capturePage,
1248
+ st as compareScreenshotsTool,
1249
+ ye as createChromiumPool,
1250
+ gt as createServer,
1251
+ Se as diffScreenshots,
1252
+ Qe as handleAnnotateScreenshot,
1253
+ ot as handleAnnotateUrl,
1254
+ it as handleCompareScreenshots,
1255
+ ct as handleRedactScreenshot,
1256
+ lt as handleRedactUrl,
1257
+ B as readPngDimensions,
1258
+ _t as rectForBoundingBox,
1259
+ at as redactScreenshotTool,
1260
+ dt as redactUrlTool,
1261
+ R as resolveImageInput,
1351
1262
  f as resolveLocator,
1352
- wt as resolveLocatorAnnotation,
1353
- bt as resolveLocatorAnnotations,
1354
- Me as runStdioServer
1263
+ we as resolveLocatorAnnotation,
1264
+ be as resolveLocatorAnnotations,
1265
+ Pt as runStdioServer,
1266
+ Ct as textAt
1355
1267
  };