Skip to content
Closed
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
5 changes: 4 additions & 1 deletion docs/tool-reference.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<!-- AUTO GENERATED DO NOT EDIT - run 'npm run gen' to update-->

# Chrome DevTools MCP Tool Reference (~6940 cl100k_base tokens)
# Chrome DevTools MCP Tool Reference (~7028 cl100k_base tokens)

- **[Input automation](#input-automation)** (9 tools)
- [`click`](#click)
Expand Down Expand Up @@ -394,6 +394,9 @@ in the DevTools Elements panel (if any).
**Parameters:**

- **filePath** (string) _(optional)_: The absolute path, or a path relative to the current working directory, to save the snapshot to instead of attaching it to the response.
- **name** (string) _(optional)_: Filter elements by their accessibility name (supports regular expressions).
- **role** (string) _(optional)_: Filter elements by their accessibility role.
- **text** (string) _(optional)_: Filter elements by their text content (supports regular expressions).
- **verbose** (boolean) _(optional)_: Whether to include all possible information available in the full a11y tree. Default is false.

---
36 changes: 36 additions & 0 deletions scripts/post-build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,42 @@ export const ExperimentName = {
writeFile(runtimeFile, runtimeContent);

copyDevToolsDescriptionFiles();
copySnapshotFiles();
}

function copySnapshotFiles() {
const testsDir = path.join(process.cwd(), 'tests');
const buildTestsDir = path.join(BUILD_DIR, 'tests');

if (!fs.existsSync(buildTestsDir)) {
fs.mkdirSync(buildTestsDir, {recursive: true});
}

const files = fs.readdirSync(testsDir);
for (const file of files) {
if (file.endsWith('.snapshot')) {
fs.copyFileSync(path.join(testsDir, file), path.join(buildTestsDir, file));
}
}

// Also handle subdirectories if needed, but for now we only have snapshots in the root of tests/
// Wait, let's check tools/
const toolsTestsDir = path.join(testsDir, 'tools');
const buildToolsTestsDir = path.join(buildTestsDir, 'tools');
if (fs.existsSync(toolsTestsDir)) {
if (!fs.existsSync(buildToolsTestsDir)) {
fs.mkdirSync(buildToolsTestsDir, {recursive: true});
}
const toolsFiles = fs.readdirSync(toolsTestsDir);
for (const file of toolsFiles) {
if (file.endsWith('.snapshot')) {
fs.copyFileSync(
path.join(toolsTestsDir, file),
path.join(buildToolsTestsDir, file),
);
}
}
}
}

function copyDevToolsDescriptionFiles() {
Expand Down
105 changes: 104 additions & 1 deletion src/McpContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -748,6 +748,11 @@ export class McpContext implements Context {
page: McpPage,
verbose = false,
devtoolsData: DevToolsData | undefined = undefined,
options: {
role?: string;
name?: string;
text?: string;
} = {},
): Promise<void> {
const rootNode = await page.pptrPage.accessibility.snapshot({
includeIframes: true,
Expand Down Expand Up @@ -801,8 +806,35 @@ export class McpContext implements Context {
};

const rootNodeWithId = assignIds(rootNode);

let filteredRootNode = rootNodeWithId;
if (options.role || options.name || options.text) {
filteredRootNode = filterTree(rootNodeWithId, options)!;

// If everything was filtered out, we might get null.
// But we should at least keep the root if possible or handle null.
if (!filteredRootNode) {
// Return an empty tree or just the root?
// Let's keep the root but with no children if it doesn't match.
filteredRootNode = {
...rootNodeWithId,
children: [],
};
}

// Rebuild idToNode map to only include filtered nodes.
idToNode.clear();
const addToMap = (node: TextSnapshotNode) => {
idToNode.set(node.id, node);
for (const child of node.children) {
addToMap(child);
}
};
addToMap(filteredRootNode);
}

const snapshot: TextSnapshot = {
root: rootNodeWithId,
root: filteredRootNode,
snapshotId: String(snapshotId),
idToNode,
hasSelectedElement: false,
Expand Down Expand Up @@ -964,3 +996,74 @@ export class McpContext implements Context {
return this.#extensionRegistry.getById(id);
}
}

function filterTree(
node: TextSnapshotNode,
options: {
role?: string;
name?: string;
text?: string;
},
): TextSnapshotNode | null {
const matchingChildren: TextSnapshotNode[] = [];
for (const child of node.children) {
const filteredChild = filterTree(child, options);
if (filteredChild) {
matchingChildren.push(filteredChild);
}
}

const matches = isNodeMatching(node, options);

if (matches || matchingChildren.length > 0) {
return {
...node,
children: matchingChildren,
};
}

return null;
}

function isNodeMatching(
node: TextSnapshotNode,
options: {
role?: string;
name?: string;
text?: string;
},
): boolean {
let filterApplied = false;

if (options.role) {
filterApplied = true;
if (node.role !== options.role) {
return false;
}
}

if (options.name) {
filterApplied = true;
const regex = new RegExp(options.name, 'i');
if (!node.name || !regex.test(node.name.toString())) {
return false;
}
}

if (options.text) {
filterApplied = true;
const regex = new RegExp(options.text, 'i');
const textContent = [
node.name?.toString(),
node.value?.toString(),
node.description?.toString(),
]
.filter(Boolean)
.join(' ');
if (!regex.test(textContent)) {
return false;
}
}

return filterApplied;
}
5 changes: 5 additions & 0 deletions src/McpResponse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,11 @@ export class McpResponse implements Response {
this.#page,
this.#snapshotParams.verbose,
this.#devToolsData,
{
role: this.#snapshotParams.role,
name: this.#snapshotParams.name,
text: this.#snapshotParams.text,
},
);
const textSnapshot = this.#page.textSnapshot;
if (textSnapshot) {
Expand Down
Loading
Loading