cbrowser 18.73.2 → 18.73.4
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/analysis/accessibility-empathy.d.ts.map +1 -1
- package/dist/analysis/accessibility-empathy.js +88 -4
- package/dist/analysis/accessibility-empathy.js.map +1 -1
- package/dist/analysis/agent-ready-audit.d.ts.map +1 -1
- package/dist/analysis/agent-ready-audit.js +66 -5
- package/dist/analysis/agent-ready-audit.js.map +1 -1
- package/dist/analysis/page-understanding.d.ts.map +1 -1
- package/dist/analysis/page-understanding.js +77 -12
- package/dist/analysis/page-understanding.js.map +1 -1
- package/dist/analysis/webmcp-readiness.d.ts.map +1 -1
- package/dist/analysis/webmcp-readiness.js +18 -7
- package/dist/analysis/webmcp-readiness.js.map +1 -1
- package/dist/browser.d.ts +8 -0
- package/dist/browser.d.ts.map +1 -1
- package/dist/browser.js +93 -19
- package/dist/browser.js.map +1 -1
- package/dist/cli.js +12 -0
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +28 -1
- package/dist/config.js.map +1 -1
- package/dist/mcp-server-remote.d.ts +17 -1
- package/dist/mcp-server-remote.d.ts.map +1 -1
- package/dist/mcp-server-remote.js +22 -3
- package/dist/mcp-server-remote.js.map +1 -1
- package/dist/mcp-server.d.ts.map +1 -1
- package/dist/mcp-server.js +20 -0
- package/dist/mcp-server.js.map +1 -1
- package/dist/mcp-tools/base/analysis-tools.d.ts +1 -1
- package/dist/mcp-tools/base/analysis-tools.d.ts.map +1 -1
- package/dist/mcp-tools/base/analysis-tools.js +83 -10
- package/dist/mcp-tools/base/analysis-tools.js.map +1 -1
- package/dist/mcp-tools/base/assertion-tools.d.ts +1 -1
- package/dist/mcp-tools/base/assertion-tools.d.ts.map +1 -1
- package/dist/mcp-tools/base/assertion-tools.js +26 -3
- package/dist/mcp-tools/base/assertion-tools.js.map +1 -1
- package/dist/mcp-tools/base/audit-tools.d.ts.map +1 -1
- package/dist/mcp-tools/base/audit-tools.js +85 -8
- package/dist/mcp-tools/base/audit-tools.js.map +1 -1
- package/dist/mcp-tools/base/browser-management-tools.d.ts +1 -1
- package/dist/mcp-tools/base/browser-management-tools.d.ts.map +1 -1
- package/dist/mcp-tools/base/browser-management-tools.js +66 -15
- package/dist/mcp-tools/base/browser-management-tools.js.map +1 -1
- package/dist/mcp-tools/base/browser-state-tools.d.ts.map +1 -1
- package/dist/mcp-tools/base/browser-state-tools.js +6 -1
- package/dist/mcp-tools/base/browser-state-tools.js.map +1 -1
- package/dist/mcp-tools/base/cognitive-tools.d.ts.map +1 -1
- package/dist/mcp-tools/base/cognitive-tools.js +103 -30
- package/dist/mcp-tools/base/cognitive-tools.js.map +1 -1
- package/dist/mcp-tools/base/extraction-tools.d.ts.map +1 -1
- package/dist/mcp-tools/base/extraction-tools.js +6 -0
- package/dist/mcp-tools/base/extraction-tools.js.map +1 -1
- package/dist/mcp-tools/base/interaction-tools.d.ts.map +1 -1
- package/dist/mcp-tools/base/interaction-tools.js +76 -11
- package/dist/mcp-tools/base/interaction-tools.js.map +1 -1
- package/dist/mcp-tools/base/session-tools.d.ts +1 -1
- package/dist/mcp-tools/base/session-tools.d.ts.map +1 -1
- package/dist/mcp-tools/base/session-tools.js +64 -7
- package/dist/mcp-tools/base/session-tools.js.map +1 -1
- package/dist/mcp-tools/base/site-knowledge-tools.d.ts.map +1 -1
- package/dist/mcp-tools/base/site-knowledge-tools.js +15 -1
- package/dist/mcp-tools/base/site-knowledge-tools.js.map +1 -1
- package/dist/mcp-tools/base/testing-tools.d.ts.map +1 -1
- package/dist/mcp-tools/base/testing-tools.js +20 -0
- package/dist/mcp-tools/base/testing-tools.js.map +1 -1
- package/dist/mcp-tools/base/visual-testing-tools.d.ts.map +1 -1
- package/dist/mcp-tools/base/visual-testing-tools.js +52 -23
- package/dist/mcp-tools/base/visual-testing-tools.js.map +1 -1
- package/dist/mcp-tools/persona-creation-tools.d.ts.map +1 -1
- package/dist/mcp-tools/persona-creation-tools.js +18 -0
- package/dist/mcp-tools/persona-creation-tools.js.map +1 -1
- package/dist/mcp-tools/session-policy.d.ts +43 -0
- package/dist/mcp-tools/session-policy.d.ts.map +1 -0
- package/dist/mcp-tools/session-policy.js +54 -0
- package/dist/mcp-tools/session-policy.js.map +1 -0
- package/dist/performance/metrics.d.ts +0 -3
- package/dist/performance/metrics.d.ts.map +1 -1
- package/dist/performance/metrics.js +45 -1
- package/dist/performance/metrics.js.map +1 -1
- package/dist/personas.d.ts.map +1 -1
- package/dist/personas.js +38 -4
- package/dist/personas.js.map +1 -1
- package/dist/remediation/patch-generator.d.ts.map +1 -1
- package/dist/remediation/patch-generator.js +36 -2
- package/dist/remediation/patch-generator.js.map +1 -1
- package/dist/testing/coverage.d.ts +8 -0
- package/dist/testing/coverage.d.ts.map +1 -1
- package/dist/testing/coverage.js +12 -0
- package/dist/testing/coverage.js.map +1 -1
- package/dist/types.d.ts +33 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/visual/cognitive-transport.d.ts +15 -2
- package/dist/visual/cognitive-transport.d.ts.map +1 -1
- package/dist/visual/cognitive-transport.js +85 -31
- package/dist/visual/cognitive-transport.js.map +1 -1
- package/dist/visual/perceptual-transport.d.ts +8 -0
- package/dist/visual/perceptual-transport.d.ts.map +1 -1
- package/dist/visual/perceptual-transport.js +19 -0
- package/dist/visual/perceptual-transport.js.map +1 -1
- package/dist/visual/regression.d.ts.map +1 -1
- package/dist/visual/regression.js +76 -3
- package/dist/visual/regression.js.map +1 -1
- package/package.json +1 -1
|
@@ -5,15 +5,21 @@
|
|
|
5
5
|
* @license MIT
|
|
6
6
|
*/
|
|
7
7
|
import { z } from "zod";
|
|
8
|
+
import { refuseUnboundSession } from "../session-policy.js";
|
|
8
9
|
import { findElementByIntent, runAIReadinessBenchmark } from "../../analysis/index.js";
|
|
9
10
|
/**
|
|
10
11
|
* Register analysis tools (4 tools: analyze_page, generate_tests, find_element_by_intent, ai_benchmark)
|
|
11
12
|
*/
|
|
12
|
-
export function registerAnalysisTools(server, { getBrowser }) {
|
|
13
|
+
export function registerAnalysisTools(server, { getBrowser, getBrowserByToken }) {
|
|
13
14
|
server.registerTool("analyze_page", {
|
|
14
15
|
title: "Analyze Page Structure",
|
|
15
|
-
description: "Analyze page structure for forms, buttons, links",
|
|
16
|
-
inputSchema: {
|
|
16
|
+
description: "Analyze page structure for forms, buttons, links. MUST pass _browserToken from a previous tool call to analyze the same page — without it a blank page is analyzed and every count comes back zero.",
|
|
17
|
+
inputSchema: {
|
|
18
|
+
// Took no arguments at all, so on the HTTP transport it always analysed a
|
|
19
|
+
// fresh blank page: 0 forms, 0 buttons, 0 links, hasLogin false. Reads as a
|
|
20
|
+
// real analysis of a sparse page rather than as an error. (2026-07-28)
|
|
21
|
+
_browserToken: z.string().optional().describe("Browser session token from a previous tool call"),
|
|
22
|
+
},
|
|
17
23
|
annotations: {
|
|
18
24
|
title: "Analyze Page Structure",
|
|
19
25
|
readOnlyHint: true,
|
|
@@ -21,14 +27,29 @@ export function registerAnalysisTools(server, { getBrowser }) {
|
|
|
21
27
|
idempotentHint: true,
|
|
22
28
|
openWorldHint: false,
|
|
23
29
|
},
|
|
24
|
-
}, async () => {
|
|
25
|
-
|
|
30
|
+
}, async ({ _browserToken }) => {
|
|
31
|
+
// Reports on page structure, so a blank-page answer (0 forms, 0 buttons,
|
|
32
|
+
// 0 links) reads as a real finding rather than an error.
|
|
33
|
+
const unbound = refuseUnboundSession("analyze_page", { getBrowserByToken }, _browserToken);
|
|
34
|
+
if (unbound)
|
|
35
|
+
return unbound;
|
|
36
|
+
let b;
|
|
37
|
+
let token;
|
|
38
|
+
if (getBrowserByToken) {
|
|
39
|
+
const resolved = await getBrowserByToken(_browserToken);
|
|
40
|
+
b = resolved.browser;
|
|
41
|
+
token = resolved.token;
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
b = await getBrowser();
|
|
45
|
+
}
|
|
26
46
|
const analysis = await b.analyzePage();
|
|
27
47
|
return {
|
|
28
48
|
content: [
|
|
29
49
|
{
|
|
30
50
|
type: "text",
|
|
31
51
|
text: JSON.stringify({
|
|
52
|
+
...(token ? { _browserToken: token } : {}),
|
|
32
53
|
title: analysis.title,
|
|
33
54
|
forms: analysis.forms.length,
|
|
34
55
|
buttons: analysis.buttons.length,
|
|
@@ -46,6 +67,10 @@ export function registerAnalysisTools(server, { getBrowser }) {
|
|
|
46
67
|
description: "Generate test scenarios for a page",
|
|
47
68
|
inputSchema: {
|
|
48
69
|
url: z.string().url().optional().describe("URL to analyze (uses current page if not provided)"),
|
|
70
|
+
// "uses current page if not provided" was unreachable on the HTTP transport:
|
|
71
|
+
// omitting url fell back to a blank page, so the no-url branch generated
|
|
72
|
+
// tests for about:blank. (2026-07-28)
|
|
73
|
+
_browserToken: z.string().optional().describe("Browser session token from a previous tool call — required when url is omitted"),
|
|
49
74
|
},
|
|
50
75
|
annotations: {
|
|
51
76
|
title: "Generate Test Cases",
|
|
@@ -54,20 +79,52 @@ export function registerAnalysisTools(server, { getBrowser }) {
|
|
|
54
79
|
idempotentHint: true,
|
|
55
80
|
openWorldHint: false,
|
|
56
81
|
},
|
|
57
|
-
}, async ({ url }) => {
|
|
58
|
-
|
|
82
|
+
}, async ({ url, _browserToken }) => {
|
|
83
|
+
// Only when it is being asked about "the current page". With an explicit
|
|
84
|
+
// url it navigates itself and needs no session.
|
|
85
|
+
if (!url) {
|
|
86
|
+
const unbound = refuseUnboundSession("generate_tests", { getBrowserByToken }, _browserToken);
|
|
87
|
+
if (unbound)
|
|
88
|
+
return unbound;
|
|
89
|
+
}
|
|
90
|
+
let b;
|
|
91
|
+
let token;
|
|
92
|
+
if (getBrowserByToken) {
|
|
93
|
+
const resolved = await getBrowserByToken(_browserToken);
|
|
94
|
+
b = resolved.browser;
|
|
95
|
+
token = resolved.token;
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
b = await getBrowser();
|
|
99
|
+
}
|
|
59
100
|
const result = await b.generateTests(url);
|
|
60
101
|
return {
|
|
61
102
|
content: [
|
|
62
103
|
{
|
|
63
104
|
type: "text",
|
|
64
105
|
text: JSON.stringify({
|
|
106
|
+
...(token ? { _browserToken: token } : {}),
|
|
65
107
|
testsGenerated: result.tests.length,
|
|
108
|
+
// `steps` used to be t.steps.length — a bare count. A caller got
|
|
109
|
+
// "3 steps" with no way to see or run them, so there was no handoff
|
|
110
|
+
// into nl_test_inline / nl_test_file and the tool could only be
|
|
111
|
+
// read, not used. The step text and a runnable script were both
|
|
112
|
+
// already computed and then discarded. (2026-07-29)
|
|
66
113
|
tests: result.tests.map(t => ({
|
|
67
114
|
name: t.name,
|
|
68
115
|
description: t.description,
|
|
69
|
-
|
|
116
|
+
stepCount: t.steps.length,
|
|
117
|
+
steps: t.steps.map(st => ({
|
|
118
|
+
action: st.action,
|
|
119
|
+
...(st.target ? { target: st.target } : {}),
|
|
120
|
+
...(st.value !== undefined ? { value: st.value } : {}),
|
|
121
|
+
description: st.description,
|
|
122
|
+
})),
|
|
123
|
+
assertions: t.assertions,
|
|
70
124
|
})),
|
|
125
|
+
// Ready to paste straight into nl_test_inline.
|
|
126
|
+
cbrowserScript: result.cbrowserScript,
|
|
127
|
+
usage: "Pass cbrowserScript to nl_test_inline as `content`, or write it to a file and use nl_test_file.",
|
|
71
128
|
}, null, 2),
|
|
72
129
|
},
|
|
73
130
|
],
|
|
@@ -79,6 +136,11 @@ export function registerAnalysisTools(server, { getBrowser }) {
|
|
|
79
136
|
inputSchema: {
|
|
80
137
|
intent: z.string().describe("Natural language description like 'the cheapest product' or 'login form'"),
|
|
81
138
|
verbose: z.boolean().optional().describe("Include alternative matches with confidence scores and AI suggestions"),
|
|
139
|
+
// Without this, the tool searched a blank browser on the HTTP transport and
|
|
140
|
+
// returned `confidence: 0, "No match found"` with empty alternatives — a
|
|
141
|
+
// plausible "nothing here" that is indistinguishable from a real miss.
|
|
142
|
+
// (2026-07-28)
|
|
143
|
+
_browserToken: z.string().optional().describe("Browser session token from a previous tool call"),
|
|
82
144
|
},
|
|
83
145
|
annotations: {
|
|
84
146
|
title: "Find Element by Intent",
|
|
@@ -87,8 +149,19 @@ export function registerAnalysisTools(server, { getBrowser }) {
|
|
|
87
149
|
idempotentHint: true,
|
|
88
150
|
openWorldHint: false,
|
|
89
151
|
},
|
|
90
|
-
}, async ({ intent, verbose }) => {
|
|
91
|
-
|
|
152
|
+
}, async ({ intent, verbose, _browserToken }) => {
|
|
153
|
+
// Searches the page, so a blank-browser search returns "no match found"
|
|
154
|
+
// with confidence 0 — indistinguishable from a genuine miss.
|
|
155
|
+
const unbound = refuseUnboundSession("find_element_by_intent", { getBrowserByToken }, _browserToken);
|
|
156
|
+
if (unbound)
|
|
157
|
+
return unbound;
|
|
158
|
+
let b;
|
|
159
|
+
if (getBrowserByToken) {
|
|
160
|
+
b = (await getBrowserByToken(_browserToken)).browser;
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
b = await getBrowser();
|
|
164
|
+
}
|
|
92
165
|
const result = await findElementByIntent(b, intent, { verbose });
|
|
93
166
|
if (result && result.confidence > 0) {
|
|
94
167
|
return {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"analysis-tools.js","sourceRoot":"","sources":["../../../src/mcp-tools/base/analysis-tools.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,mBAAmB,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AAEvF;;GAEG;AACH,MAAM,UAAU,qBAAqB,CACnC,MAAiB,EACjB,EAAE,UAAU,EAA2B;
|
|
1
|
+
{"version":3,"file":"analysis-tools.js","sourceRoot":"","sources":["../../../src/mcp-tools/base/analysis-tools.ts"],"names":[],"mappings":"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"}
|
|
@@ -8,5 +8,5 @@ import type { McpServer, ToolRegistrationContext } from "../types.js";
|
|
|
8
8
|
/**
|
|
9
9
|
* Register assertion tools (1 tool: assert)
|
|
10
10
|
*/
|
|
11
|
-
export declare function registerAssertionTools(server: McpServer, { getBrowser }: ToolRegistrationContext): void;
|
|
11
|
+
export declare function registerAssertionTools(server: McpServer, { getBrowser, getBrowserByToken }: ToolRegistrationContext): void;
|
|
12
12
|
//# sourceMappingURL=assertion-tools.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"assertion-tools.d.ts","sourceRoot":"","sources":["../../../src/mcp-tools/base/assertion-tools.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,SAAS,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"assertion-tools.d.ts","sourceRoot":"","sources":["../../../src/mcp-tools/base/assertion-tools.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,SAAS,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAGtE;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,SAAS,EACjB,EAAE,UAAU,EAAE,iBAAiB,EAAE,EAAE,uBAAuB,GACzD,IAAI,CAqDN"}
|
|
@@ -5,15 +5,22 @@
|
|
|
5
5
|
* @license MIT
|
|
6
6
|
*/
|
|
7
7
|
import { z } from "zod";
|
|
8
|
+
import { refuseUnboundSession } from "../session-policy.js";
|
|
8
9
|
/**
|
|
9
10
|
* Register assertion tools (1 tool: assert)
|
|
10
11
|
*/
|
|
11
|
-
export function registerAssertionTools(server, { getBrowser }) {
|
|
12
|
+
export function registerAssertionTools(server, { getBrowser, getBrowserByToken }) {
|
|
12
13
|
server.registerTool("assert", {
|
|
13
14
|
title: "Assert Page State",
|
|
14
15
|
description: "Assert a condition using natural language",
|
|
15
16
|
inputSchema: {
|
|
16
17
|
assertion: z.string().describe("Natural language assertion like \"page contains 'Welcome'\" or \"title is 'Home'\""),
|
|
18
|
+
// Without this, assert could not bind to the session navigate created on the
|
|
19
|
+
// HTTP transport: it asserted against a blank page and returned
|
|
20
|
+
// `passed:false, actual:""`. Worse than a crash — a NEGATIVE assertion
|
|
21
|
+
// ("page does not contain 'error'") then PASSED against nothing, so a whole
|
|
22
|
+
// suite could report green while testing an empty browser. (2026-07-28)
|
|
23
|
+
_browserToken: z.string().optional().describe("Browser session token from a previous tool call"),
|
|
17
24
|
},
|
|
18
25
|
annotations: {
|
|
19
26
|
title: "Assert Page State",
|
|
@@ -22,8 +29,23 @@ export function registerAssertionTools(server, { getBrowser }) {
|
|
|
22
29
|
idempotentHint: true,
|
|
23
30
|
openWorldHint: false,
|
|
24
31
|
},
|
|
25
|
-
}, async ({ assertion }) => {
|
|
26
|
-
|
|
32
|
+
}, async ({ assertion, _browserToken }) => {
|
|
33
|
+
// Adding the token was only half the fix: omitting it entirely still
|
|
34
|
+
// asserted against a blank page and returned a confident verdict, with a
|
|
35
|
+
// fresh _browserToken attached that made it look session-backed.
|
|
36
|
+
const unbound = refuseUnboundSession("assert", { getBrowserByToken }, _browserToken);
|
|
37
|
+
if (unbound)
|
|
38
|
+
return unbound;
|
|
39
|
+
let b;
|
|
40
|
+
let token;
|
|
41
|
+
if (getBrowserByToken) {
|
|
42
|
+
const resolved = await getBrowserByToken(_browserToken);
|
|
43
|
+
b = resolved.browser;
|
|
44
|
+
token = resolved.token;
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
b = await getBrowser();
|
|
48
|
+
}
|
|
27
49
|
const result = await b.assert(assertion);
|
|
28
50
|
return {
|
|
29
51
|
content: [
|
|
@@ -34,6 +56,7 @@ export function registerAssertionTools(server, { getBrowser }) {
|
|
|
34
56
|
message: result.message,
|
|
35
57
|
actual: result.actual,
|
|
36
58
|
expected: result.expected,
|
|
59
|
+
...(token ? { _browserToken: token } : {}),
|
|
37
60
|
}, null, 2),
|
|
38
61
|
},
|
|
39
62
|
],
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"assertion-tools.js","sourceRoot":"","sources":["../../../src/mcp-tools/base/assertion-tools.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"assertion-tools.js","sourceRoot":"","sources":["../../../src/mcp-tools/base/assertion-tools.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAE5D;;GAEG;AACH,MAAM,UAAU,sBAAsB,CACpC,MAAiB,EACjB,EAAE,UAAU,EAAE,iBAAiB,EAA2B;IAE1D,MAAM,CAAC,YAAY,CAAC,QAAQ,EAAE;QAC5B,KAAK,EAAE,mBAAmB;QAC1B,WAAW,EAAE,2CAA2C;QACxD,WAAW,EAAE;YACX,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,oFAAoF,CAAC;YACpH,6EAA6E;YAC7E,gEAAgE;YAChE,uEAAuE;YACvE,4EAA4E;YAC5E,wEAAwE;YACxE,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,iDAAiD,CAAC;SACjG;QACD,WAAW,EAAE;YACX,KAAK,EAAE,mBAAmB;YAC1B,YAAY,EAAE,IAAI;YAClB,eAAe,EAAE,KAAK;YACtB,cAAc,EAAE,IAAI;YACpB,aAAa,EAAE,KAAK;SACrB;KACF,EAAE,KAAK,EAAE,EAAE,SAAS,EAAE,aAAa,EAAE,EAAE,EAAE;QACtC,qEAAqE;QACrE,yEAAyE;QACzE,iEAAiE;QACjE,MAAM,OAAO,GAAG,oBAAoB,CAAC,QAAQ,EAAE,EAAE,iBAAiB,EAAE,EAAE,aAAa,CAAC,CAAC;QACrF,IAAI,OAAO;YAAE,OAAO,OAAO,CAAC;QAE5B,IAAI,CAAC,CAAC;QACN,IAAI,KAAyB,CAAC;QAC9B,IAAI,iBAAiB,EAAE,CAAC;YACtB,MAAM,QAAQ,GAAG,MAAM,iBAAiB,CAAC,aAAa,CAAC,CAAC;YACxD,CAAC,GAAG,QAAQ,CAAC,OAAO,CAAC;YACrB,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC;QACzB,CAAC;aAAM,CAAC;YACN,CAAC,GAAG,MAAM,UAAU,EAAE,CAAC;QACzB,CAAC;QACD,MAAM,MAAM,GAAG,MAAM,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QACzC,OAAO;YACL,OAAO,EAAE;gBACP;oBACE,IAAI,EAAE,MAAM;oBACZ,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;wBACnB,MAAM,EAAE,MAAM,CAAC,MAAM;wBACrB,OAAO,EAAE,MAAM,CAAC,OAAO;wBACvB,MAAM,EAAE,MAAM,CAAC,MAAM;wBACrB,QAAQ,EAAE,MAAM,CAAC,QAAQ;wBACzB,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;qBAC3C,EAAE,IAAI,EAAE,CAAC,CAAC;iBACZ;aACF;SACF,CAAC;IACJ,CAAC,CACF,CAAC;AACJ,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audit-tools.d.ts","sourceRoot":"","sources":["../../../src/mcp-tools/base/audit-tools.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,SAAS,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"audit-tools.d.ts","sourceRoot":"","sources":["../../../src/mcp-tools/base/audit-tools.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,SAAS,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAmCtE;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,uBAAuB,GAAG,IAAI,CA8mC7F"}
|
|
@@ -7,6 +7,34 @@
|
|
|
7
7
|
import { z } from "zod";
|
|
8
8
|
import { runAgentReadyAudit, runCompetitiveBenchmark, runEmpathyAudit, runWebMCPReadyAudit, } from "../../analysis/index.js";
|
|
9
9
|
import { listAccessibilityPersonas } from "../../personas.js";
|
|
10
|
+
import { writeFileSync, mkdirSync, existsSync } from "fs";
|
|
11
|
+
import { join } from "path";
|
|
12
|
+
import { homedir } from "os";
|
|
13
|
+
/**
|
|
14
|
+
* Persist an audit screenshot and return its path.
|
|
15
|
+
*
|
|
16
|
+
* empathy_audit inlined base64 page screenshots, which routinely overflowed an
|
|
17
|
+
* MCP client's context and made the tool untestable through a normal client.
|
|
18
|
+
* Writing them out keeps the overlay data usable without carrying megabytes
|
|
19
|
+
* through the transport. Returns undefined rather than throwing if the write
|
|
20
|
+
* fails — a screenshot is not worth failing an audit over. (2026-07-29)
|
|
21
|
+
*/
|
|
22
|
+
function writeAuditScreenshot(base64, persona) {
|
|
23
|
+
if (!base64)
|
|
24
|
+
return undefined;
|
|
25
|
+
try {
|
|
26
|
+
const dir = join(process.env.CBROWSER_DATA_DIR || join(homedir(), ".cbrowser"), "audit-screenshots");
|
|
27
|
+
if (!existsSync(dir))
|
|
28
|
+
mkdirSync(dir, { recursive: true });
|
|
29
|
+
const safe = String(persona || "persona").toLowerCase().replace(/[^a-z0-9-]+/g, "-");
|
|
30
|
+
const path = join(dir, `empathy-${safe}-${Date.now()}.png`);
|
|
31
|
+
writeFileSync(path, Buffer.from(base64, "base64"));
|
|
32
|
+
return path;
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
10
38
|
/**
|
|
11
39
|
* Register audit tools (4 tools + site_cognitive_assessment + visual_cognitive_story)
|
|
12
40
|
*/
|
|
@@ -92,6 +120,11 @@ export function registerAuditTools(server, context) {
|
|
|
92
120
|
wcagLevel: z.enum(["A", "AA", "AAA"]).optional().default("AA").describe("WCAG conformance level"),
|
|
93
121
|
maxSteps: z.number().optional().default(5).describe("Max cognitive journey steps (keep low for MCP)"),
|
|
94
122
|
maxTime: z.number().optional().default(20).describe("Max time per persona in seconds"),
|
|
123
|
+
// Base64 page screenshots are large enough to blow an MCP client's context
|
|
124
|
+
// on a single call — every empathy_audit in the 18.73.3 test pass had to be
|
|
125
|
+
// read off disk instead. Off by default; images go to disk and their paths
|
|
126
|
+
// are returned. (2026-07-29)
|
|
127
|
+
includeScreenshots: z.boolean().optional().default(false).describe("Inline base64 page screenshots in the response. Off by default because they are large enough to overflow an MCP client's context; when off, screenshots are written to disk and screenshotPath is returned instead."),
|
|
95
128
|
scope: z.enum(["viewport", "full_page"]).optional().default("viewport").describe("What to score: 'viewport' (first impression, above-the-fold only — default) or 'full_page' (scroll through entire page, all barriers). Use 'viewport' for landing page optimization; 'full_page' for WCAG compliance audits."),
|
|
96
129
|
device: z.string().optional().describe("Device emulation: 'mobile', 'tablet', 'desktop', or specific device like 'iPhone 15', 'Pixel 7'. Essential for mobile WCAG audits — touch targets, viewport sizing, and responsive barriers differ significantly on mobile."),
|
|
97
130
|
},
|
|
@@ -102,7 +135,7 @@ export function registerAuditTools(server, context) {
|
|
|
102
135
|
idempotentHint: false,
|
|
103
136
|
openWorldHint: true,
|
|
104
137
|
},
|
|
105
|
-
}, async ({ url, goal, disabilities, wcagLevel, maxSteps, maxTime, scope, device }) => {
|
|
138
|
+
}, async ({ url, goal, disabilities, wcagLevel, maxSteps, maxTime, scope, device, includeScreenshots }) => {
|
|
106
139
|
try {
|
|
107
140
|
// Auto-limit to 1 persona to avoid MCP client timeout on Claude.ai (~60s limit)
|
|
108
141
|
const allPersonas = listAccessibilityPersonas();
|
|
@@ -140,11 +173,23 @@ export function registerAuditTools(server, context) {
|
|
|
140
173
|
goalAchieved: r.goalAchieved,
|
|
141
174
|
empathyScore: r.empathyScore,
|
|
142
175
|
// v18.22.0: Added score context for transparency
|
|
176
|
+
// Whitelist projection: anything added to scoreContext upstream is
|
|
177
|
+
// invisible to the caller unless named here. The blend breakdown
|
|
178
|
+
// below is what lets the deductions reconcile to a score the
|
|
179
|
+
// caller can actually see. (2026-07-28)
|
|
143
180
|
scoreContext: r.scoreContext ? {
|
|
144
181
|
explanation: r.scoreContext.explanation,
|
|
145
182
|
deductionsByType: r.scoreContext.deductionsByType,
|
|
183
|
+
totalBarrierDeduction: r.scoreContext.totalBarrierDeduction,
|
|
146
184
|
frictionDeduction: r.scoreContext.frictionDeduction,
|
|
147
185
|
goalDeduction: r.scoreContext.goalDeduction,
|
|
186
|
+
cognitiveOverloadPenalty: r.scoreContext.cognitiveOverloadPenalty,
|
|
187
|
+
appliedBarrierWeights: r.scoreContext.appliedBarrierWeights,
|
|
188
|
+
explainsScore: r.scoreContext.explainsScore,
|
|
189
|
+
barrierOnlyScore: r.scoreContext.barrierOnlyScore,
|
|
190
|
+
perceptualScore: r.scoreContext.perceptualScore,
|
|
191
|
+
blendWeights: r.scoreContext.blendWeights,
|
|
192
|
+
finalScore: r.scoreContext.finalScore,
|
|
148
193
|
} : undefined,
|
|
149
194
|
barrierTypeCount: uniqueTypes.size,
|
|
150
195
|
barrierTypes: Array.from(uniqueTypes),
|
|
@@ -155,6 +200,10 @@ export function registerAuditTools(server, context) {
|
|
|
155
200
|
empathyScoreBarrierOnly: r.empathyScoreBarrierOnly || undefined,
|
|
156
201
|
// v18.27.0: Cognitive load estimation (optimal transport)
|
|
157
202
|
cognitiveLoad: r.cognitiveLoad || undefined,
|
|
203
|
+
// Names the three different "cognitive load" numbers in this
|
|
204
|
+
// payload and what each measures. Same whitelist caveat as
|
|
205
|
+
// scoreContext above: set upstream, invisible unless named here.
|
|
206
|
+
cognitiveLoadReadings: r.cognitiveLoadReadings || undefined,
|
|
158
207
|
// v18.28.0: Attention transport analysis (W₂ saliency)
|
|
159
208
|
attentionAnalysis: r.attentionAnalysis || undefined,
|
|
160
209
|
// v18.29.0: Journey validation — evidence, path, forensics
|
|
@@ -171,14 +220,42 @@ export function registerAuditTools(server, context) {
|
|
|
171
220
|
// v18.60.0: Include per-result screenshots and element rects for WCAG overlay
|
|
172
221
|
pageScreenshots: result.results?.map((r) => ({
|
|
173
222
|
persona: r.persona,
|
|
174
|
-
|
|
223
|
+
...(includeScreenshots
|
|
224
|
+
? { screenshot: r.pageScreenshot }
|
|
225
|
+
: {
|
|
226
|
+
screenshotPath: writeAuditScreenshot(r.pageScreenshot, r.persona),
|
|
227
|
+
screenshotNote: "Pass includeScreenshots:true to inline the base64 instead.",
|
|
228
|
+
}),
|
|
175
229
|
viewportSize: r.viewportSize,
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
230
|
+
// Rects come from getBoundingClientRect with no scroll compensation,
|
|
231
|
+
// so they are VIEWPORT coordinates — which is correct here, because
|
|
232
|
+
// the screenshot above is captured with fullPage: false and these are
|
|
233
|
+
// meant to overlay it. Converting them to document coordinates would
|
|
234
|
+
// break that alignment.
|
|
235
|
+
//
|
|
236
|
+
// The real hazard is scope: "full_page", where the audit scrolls and
|
|
237
|
+
// rects captured at different offsets land in one list against a
|
|
238
|
+
// single viewport image; anything above the captured viewport then
|
|
239
|
+
// carries a negative y and would render off-canvas. Say which space
|
|
240
|
+
// these are in and flag the ones that fall outside the image rather
|
|
241
|
+
// than emitting silent negatives. (2026-07-28)
|
|
242
|
+
coordinateSpace: "viewport",
|
|
243
|
+
barrierRects: r.barriers?.filter((b) => b.rect).map((b) => {
|
|
244
|
+
const vp = r.viewportSize;
|
|
245
|
+
const outside = !!vp && !!b.rect && (b.rect.y + b.rect.height < 0 || b.rect.x + b.rect.width < 0 ||
|
|
246
|
+
b.rect.y > vp.height || b.rect.x > vp.width);
|
|
247
|
+
return {
|
|
248
|
+
type: b.type, severity: b.severity, element: b.element,
|
|
249
|
+
description: b.description, rect: b.rect,
|
|
250
|
+
wcag: b.wcagCriteria,
|
|
251
|
+
...(outside ? { outsideScreenshot: true } : {}),
|
|
252
|
+
};
|
|
253
|
+
}),
|
|
254
|
+
// Keep entries that carry EITHER the inline image or a path to it.
|
|
255
|
+
// Filtering on `screenshot` alone silently dropped every entry once
|
|
256
|
+
// the default stopped inlining base64, taking the barrierRects with
|
|
257
|
+
// them. (2026-07-29)
|
|
258
|
+
})).filter((s) => s.screenshot || s.screenshotPath),
|
|
182
259
|
};
|
|
183
260
|
// Add guidance if we limited the request
|
|
184
261
|
if (wasLimited) {
|