Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions docs/tool-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -320,9 +320,11 @@ so returned values have to JSON-serializable.

### `take_snapshot`

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

**Parameters:** None
**Parameters:**

- **verbose** (boolean) _(optional)_: Whether to include all possible information available in the full a11y tree. Default is false.

---
3 changes: 2 additions & 1 deletion src/McpContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -304,10 +304,11 @@ export class McpContext implements Context {
/**
* Creates a text snapshot of a page.
*/
async createTextSnapshot(): Promise<void> {
async createTextSnapshot(verbose = false): Promise<void> {
const page = this.getSelectedPage();
const rootNode = await page.accessibility.snapshot({
includeIframes: true,
interestingOnly: !verbose,
});
if (!rootNode) {
return;
Expand Down
10 changes: 8 additions & 2 deletions src/McpResponse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ interface NetworkRequestData {
export class McpResponse implements Response {
#includePages = false;
#includeSnapshot = false;
#includeVerboseSnapshot = false;
#attachedNetworkRequestData?: NetworkRequestData;
#includeConsoleData = false;
#textResponseLines: string[] = [];
Expand All @@ -47,8 +48,9 @@ export class McpResponse implements Response {
this.#includePages = value;
}

setIncludeSnapshot(value: boolean): void {
setIncludeSnapshot(value: boolean, verbose = false): void {
this.#includeSnapshot = value;
this.#includeVerboseSnapshot = verbose;
}

setIncludeNetworkRequests(
Expand Down Expand Up @@ -125,6 +127,10 @@ export class McpResponse implements Response {
return this.#includeSnapshot;
}

get includeVersboseSnapshot(): boolean {
return this.#includeVerboseSnapshot;
}

async handle(
toolName: string,
context: McpContext,
Expand All @@ -133,7 +139,7 @@ export class McpResponse implements Response {
await context.createPagesSnapshot();
}
if (this.#includeSnapshot) {
await context.createTextSnapshot();
await context.createTextSnapshot(this.#includeVerboseSnapshot);
}

let formattedConsoleMessages: string[];
Expand Down
17 changes: 12 additions & 5 deletions src/formatters/snapshotFormatter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,18 @@ export function formatA11ySnapshot(
}

function getAttributes(serializedAXNodeRoot: TextSnapshotNode): string[] {
const attributes = [
`uid=${serializedAXNodeRoot.id}`,
serializedAXNodeRoot.role,
`"${serializedAXNodeRoot.name || ''}"`, // Corrected: Added quotes around name
];
const attributes = [`uid=${serializedAXNodeRoot.id}`];
if (serializedAXNodeRoot.role) {
// To match representation in DevTools.
attributes.push(
serializedAXNodeRoot.role === 'none'
? 'ignored'
: serializedAXNodeRoot.role,
);
}
if (serializedAXNodeRoot.name) {
attributes.push(`"${serializedAXNodeRoot.name}"`);
}

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

Expand Down
2 changes: 1 addition & 1 deletion src/tools/ToolDefinition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export interface Response {
options?: {pageSize?: number; pageIdx?: number; resourceTypes?: string[]},
): void;
setIncludeConsoleData(value: boolean): void;
setIncludeSnapshot(value: boolean): void;
setIncludeSnapshot(value: boolean, verbose?: boolean): void;
attachImage(value: ImageContentData): void;
attachNetworkRequest(reqid: number): void;
}
Expand Down
15 changes: 11 additions & 4 deletions src/tools/snapshot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,22 @@ import {defineTool, timeoutSchema} from './ToolDefinition.js';

export const takeSnapshot = defineTool({
name: 'take_snapshot',
description: `Take a text snapshot of the currently selected page. The snapshot lists page elements along with a unique
description: `Take a text snapshot of the currently selected page based on the a11y tree. The snapshot lists page elements along with a unique
identifier (uid). Always use the latest snapshot. Prefer taking a snapshot over taking a screenshot.`,
annotations: {
category: ToolCategories.DEBUGGING,
readOnlyHint: true,
},
schema: {},
handler: async (_request, response) => {
response.setIncludeSnapshot(true);
schema: {
verbose: z
.boolean()
.optional()
.describe(
'Whether to include all possible information available in the full a11y tree. Default is false.',
),
},
handler: async (request, response) => {
response.setIncludeSnapshot(true, request.params.verbose ?? false);
},
});

Expand Down
26 changes: 24 additions & 2 deletions tests/McpResponse.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,9 @@ Testing 2`,
result[0].text,
`# test response
## Page content
uid=1_0 RootWebArea ""
uid=1_0 RootWebArea
uid=1_1 button "Click me" focusable focused
uid=1_2 textbox "" value="Input"
uid=1_2 textbox value="Input"
`,
);
});
Expand Down Expand Up @@ -95,6 +95,28 @@ uid=1_0 RootWebArea "My test page"
});
});

it('returns verbose snapshot', async () => {
await withBrowser(async (response, context) => {
const page = context.getSelectedPage();
await page.setContent(html`<aside>test</aside>`);
response.setIncludeSnapshot(true, true);
const result = await response.handle('test', context);
assert.equal(result[0].type, 'text');
assert.strictEqual(
result[0].text,
`# test response
## Page content
uid=1_0 RootWebArea "My test page"
uid=1_1 ignored
uid=1_2 ignored
uid=1_3 complementary
uid=1_4 StaticText "test"
uid=1_5 InlineTextBox "test"
`,
);
});
});

it('adds throttling setting when it is not null', async () => {
await withBrowser(async (response, context) => {
context.setNetworkConditions('Slow 3G');
Expand Down