Skip to content

Commit 8770da8

Browse files
committed
feat: support verbose a11y snapshot
1 parent 579819b commit 8770da8

File tree

7 files changed

+57
-14
lines changed

7 files changed

+57
-14
lines changed

docs/tool-reference.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -320,9 +320,11 @@ so returned values have to JSON-serializable.
320320

321321
### `take_snapshot`
322322

323-
**Description:** Take a text snapshot of the currently selected page. The snapshot lists page elements along with a unique
323+
**Description:** Take a text snapshot of the currently selected page based on the a11y tree. The snapshot lists page elements along with a unique
324324
identifier (uid). Always use the latest snapshot. Prefer taking a snapshot over taking a screenshot.
325325

326-
**Parameters:** None
326+
**Parameters:**
327+
328+
- **verbose** (boolean) _(optional)_: Whether to include all possible information available in the full a11y tree. Default is false.
327329

328330
---

src/McpContext.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -304,10 +304,11 @@ export class McpContext implements Context {
304304
/**
305305
* Creates a text snapshot of a page.
306306
*/
307-
async createTextSnapshot(): Promise<void> {
307+
async createTextSnapshot(verbose = false): Promise<void> {
308308
const page = this.getSelectedPage();
309309
const rootNode = await page.accessibility.snapshot({
310310
includeIframes: true,
311+
interestingOnly: !verbose,
311312
});
312313
if (!rootNode) {
313314
return;

src/McpResponse.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ interface NetworkRequestData {
3232
export class McpResponse implements Response {
3333
#includePages = false;
3434
#includeSnapshot = false;
35+
#includeVerboseSnapshot = false;
3536
#attachedNetworkRequestData?: NetworkRequestData;
3637
#includeConsoleData = false;
3738
#textResponseLines: string[] = [];
@@ -47,8 +48,9 @@ export class McpResponse implements Response {
4748
this.#includePages = value;
4849
}
4950

50-
setIncludeSnapshot(value: boolean): void {
51+
setIncludeSnapshot(value: boolean, verbose = false): void {
5152
this.#includeSnapshot = value;
53+
this.#includeVerboseSnapshot = verbose;
5254
}
5355

5456
setIncludeNetworkRequests(
@@ -125,6 +127,10 @@ export class McpResponse implements Response {
125127
return this.#includeSnapshot;
126128
}
127129

130+
get includeVersboseSnapshot(): boolean {
131+
return this.#includeVerboseSnapshot;
132+
}
133+
128134
async handle(
129135
toolName: string,
130136
context: McpContext,
@@ -133,7 +139,7 @@ export class McpResponse implements Response {
133139
await context.createPagesSnapshot();
134140
}
135141
if (this.#includeSnapshot) {
136-
await context.createTextSnapshot();
142+
await context.createTextSnapshot(this.#includeVerboseSnapshot);
137143
}
138144

139145
let formattedConsoleMessages: string[];

src/formatters/snapshotFormatter.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,14 @@ export function formatA11ySnapshot(
2424
function getAttributes(serializedAXNodeRoot: TextSnapshotNode): string[] {
2525
const attributes = [
2626
`uid=${serializedAXNodeRoot.id}`,
27-
serializedAXNodeRoot.role,
28-
`"${serializedAXNodeRoot.name || ''}"`, // Corrected: Added quotes around name
2927
];
28+
if (serializedAXNodeRoot.role) {
29+
// To match representation in DevTools.
30+
attributes.push(serializedAXNodeRoot.role === 'none' ? 'ignored' : serializedAXNodeRoot.role);
31+
}
32+
if (serializedAXNodeRoot.name) {
33+
attributes.push(`"${serializedAXNodeRoot.name }"`);
34+
}
3035

3136
const excluded = new Set(['id', 'role', 'name', 'elementHandle', 'children']);
3237

src/tools/ToolDefinition.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ export interface Response {
4848
options?: {pageSize?: number; pageIdx?: number; resourceTypes?: string[]},
4949
): void;
5050
setIncludeConsoleData(value: boolean): void;
51-
setIncludeSnapshot(value: boolean): void;
51+
setIncludeSnapshot(value: boolean, verbose?: boolean): void;
5252
attachImage(value: ImageContentData): void;
5353
attachNetworkRequest(reqid: number): void;
5454
}

src/tools/snapshot.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,22 @@ import {defineTool, timeoutSchema} from './ToolDefinition.js';
1212

1313
export const takeSnapshot = defineTool({
1414
name: 'take_snapshot',
15-
description: `Take a text snapshot of the currently selected page. The snapshot lists page elements along with a unique
15+
description: `Take a text snapshot of the currently selected page based on the a11y tree. The snapshot lists page elements along with a unique
1616
identifier (uid). Always use the latest snapshot. Prefer taking a snapshot over taking a screenshot.`,
1717
annotations: {
1818
category: ToolCategories.DEBUGGING,
1919
readOnlyHint: true,
2020
},
21-
schema: {},
22-
handler: async (_request, response) => {
23-
response.setIncludeSnapshot(true);
21+
schema: {
22+
verbose: z
23+
.boolean()
24+
.optional()
25+
.describe(
26+
'Whether to include all possible information available in the full a11y tree. Default is false.',
27+
),
28+
},
29+
handler: async (request, response) => {
30+
response.setIncludeSnapshot(true, request.params.verbose ?? false);
2431
},
2532
});
2633

tests/McpResponse.test.ts

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,9 @@ Testing 2`,
6161
result[0].text,
6262
`# test response
6363
## Page content
64-
uid=1_0 RootWebArea ""
64+
uid=1_0 RootWebArea
6565
uid=1_1 button "Click me" focusable focused
66-
uid=1_2 textbox "" value="Input"
66+
uid=1_2 textbox value="Input"
6767
`,
6868
);
6969
});
@@ -95,6 +95,28 @@ uid=1_0 RootWebArea "My test page"
9595
});
9696
});
9797

98+
it.only('returns verbose snapshot', async () => {
99+
await withBrowser(async (response, context) => {
100+
const page = context.getSelectedPage();
101+
await page.setContent(html`<aside>test</aside>`);
102+
response.setIncludeSnapshot(true, true);
103+
const result = await response.handle('test', context);
104+
assert.equal(result[0].type, 'text');
105+
assert.strictEqual(
106+
result[0].text,
107+
`# test response
108+
## Page content
109+
uid=1_0 RootWebArea "My test page"
110+
uid=1_1 ignored
111+
uid=1_2 ignored
112+
uid=1_3 complementary
113+
uid=1_4 StaticText "test"
114+
uid=1_5 InlineTextBox "test"
115+
`,
116+
);
117+
});
118+
});
119+
98120
it('adds throttling setting when it is not null', async () => {
99121
await withBrowser(async (response, context) => {
100122
context.setNetworkConditions('Slow 3G');

0 commit comments

Comments
 (0)