Skip to content
Closed
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
a728c04
format
AlinaVarkki Sep 30, 2025
ee14198
format
AlinaVarkki Sep 30, 2025
56ab321
option handle seperately
AlinaVarkki Oct 1, 2025
b1506ed
add test
AlinaVarkki Oct 1, 2025
047c403
make value string
AlinaVarkki Oct 1, 2025
c680b3c
remove value
AlinaVarkki Oct 1, 2025
dcced0f
use option text as value
AlinaVarkki Oct 1, 2025
e873cd9
check if element is selector through ax node and extract method
AlinaVarkki Oct 2, 2025
8775b54
docs: fix macOS typo (#263)
retX0 Oct 7, 2025
b15c44b
fix: guard performance_stop_trace when tracing inactive (#295)
leslieo2 Oct 7, 2025
75f63a2
fix(cli): tolerate empty browser URLs (#298)
OrKoN Oct 7, 2025
5bc6379
fix: change default screen size in headless (#299)
OrKoN Oct 7, 2025
f9290a5
chore(main): release chrome-devtools-mcp 0.6.1 (#297)
browser-automation-bot Oct 8, 2025
c5e399e
docs: fix typo in save configuration shortcut (#307)
harshithluc073 Oct 8, 2025
5fc53d7
feat: add request and response body (#267)
nroscino Oct 8, 2025
af8152f
ci: add rerun for publish (#309)
Lightning00Blade Oct 8, 2025
05bd82c
chore: provide a structured bug template (#301)
Lightning00Blade Oct 8, 2025
85829d9
chore: fix manual workflow (#310)
Lightning00Blade Oct 8, 2025
bb9e22c
chore: fix manual workflow (#312)
Lightning00Blade Oct 8, 2025
d3ae91f
fix: publishing to MCP registry (#313)
Lightning00Blade Oct 8, 2025
799c780
fix: use default ProtocolTimeout (#315)
Lightning00Blade Oct 8, 2025
3890723
chore: switch to issue type in bug template (#325)
Lightning00Blade Oct 9, 2025
c955124
docs: fix arg name typo (#327)
OrKoN Oct 10, 2025
0129f7b
feat: Add offline network emulation support to emulate_network comman…
iamitesh Oct 10, 2025
ffde28b
chore(deps): bump puppeteer (#331)
OrKoN Oct 10, 2025
adebb8f
docs: Add note on node version when building (#333)
zyzyzyryxy Oct 10, 2025
8b14d56
fix: ordering of information in performance trace summary (#334)
jackfranklin Oct 10, 2025
847c8ec
chore(main): release chrome-devtools-mcp 0.7.0 (#308)
browser-automation-bot Oct 10, 2025
28f2e79
chore(deps): bump pptr (#336)
OrKoN Oct 10, 2025
1a64686
fix: document that console and requests are since the last nav (#335)
OrKoN Oct 10, 2025
5062e9d
docs: Add step-by-step guide for connecting to running Chrome instanc…
natorion Oct 10, 2025
a0d9609
chore: experimental devtools-on-devtools support (#51)
OrKoN Oct 10, 2025
ec3848d
chore(main): release chrome-devtools-mcp 0.7.1 (#337)
browser-automation-bot Oct 10, 2025
1a5aa18
feat: support passing args to Chrome (#338)
OrKoN Oct 10, 2025
0009725
chore(main): release chrome-devtools-mcp 0.8.0 (#339)
browser-automation-bot Oct 10, 2025
eac4d7a
docs: update browser URL from localhost to 127.0.0.1 (#342)
OrKoN Oct 11, 2025
6a0d013
refactor: use release-please to bump version in files (#349)
Lightning00Blade Oct 12, 2025
854f71a
chore(deps): bump the dependencies group across 1 directory with 2 up…
dependabot[bot] Oct 12, 2025
7c0e47f
ci: extract devtools frontend to a separate group (#353)
OrKoN Oct 12, 2025
cce927f
refactor: sort tools alphabetically (#346)
OrKoN Oct 12, 2025
9624a68
chore(deps-dev): bump the dev-dependencies group across 1 directory w…
dependabot[bot] Oct 12, 2025
d97c646
docs: add Amp configuration to MCP client setup docs (#343)
jupblb Oct 12, 2025
0e5f914
docs: add instructions for Kiro (#347)
siegerts Oct 12, 2025
8034a29
review changes
AlinaVarkki Oct 13, 2025
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
32 changes: 27 additions & 5 deletions src/McpContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,10 @@ export class McpContext implements Context {
return page.getDefaultNavigationTimeout();
}

getAXNodeByUid(uid: string) {
return this.#textSnapshot?.idToNode.get(uid);
}

async getElementByUid(uid: string): Promise<ElementHandle<Element>> {
if (!this.#textSnapshot?.idToNode.size) {
throw new Error(
Expand Down Expand Up @@ -326,19 +330,37 @@ export class McpContext implements Context {
// will be used for the tree serialization and mapping ids back to nodes.
let idCounter = 0;
const idToNode = new Map<string, TextSnapshotNode>();
const assignIds = (node: SerializedAXNode): TextSnapshotNode => {
const assignIds = async (
node: SerializedAXNode,
): Promise<TextSnapshotNode> => {
const nodeWithId: TextSnapshotNode = {
...node,
id: `${snapshotId}_${idCounter++}`,
children: node.children
? node.children.map(child => assignIds(child))
: [],
children: [],
};

// The AXNode for an option doesn't contain its `value`.
// Therefore, set text content of the option as value.
if (node.role === 'option') {
const handle = await node.elementHandle();
if (handle) {
const textContentHandle = await handle.getProperty('textContent');
const optionText = await textContentHandle.jsonValue();
if (optionText) {
nodeWithId.value = optionText.toString();
}
}
}

nodeWithId.children = node.children
? await Promise.all(node.children.map(child => assignIds(child)))
: [];

idToNode.set(nodeWithId.id, nodeWithId);
return nodeWithId;
};

const rootNodeWithId = assignIds(rootNode);
const rootNodeWithId = await assignIds(rootNode);
this.#textSnapshot = {
root: rootNodeWithId,
snapshotId: String(snapshotId),
Expand Down
2 changes: 2 additions & 0 deletions src/tools/ToolDefinition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import type {Dialog, ElementHandle, Page} from 'puppeteer-core';
import z from 'zod';

import type {TextSnapshotNode} from '../McpContext.js';
import type {TraceResult} from '../trace-processing/parse.js';

import type {ToolCategories} from './categories.js';
Expand Down Expand Up @@ -68,6 +69,7 @@ export type Context = Readonly<{
closePage(pageIdx: number): Promise<void>;
setSelectedPageIdx(idx: number): void;
getElementByUid(uid: string): Promise<ElementHandle<Element>>;
getAXNodeByUid(uid: string): TextSnapshotNode | undefined;
setNetworkConditions(conditions: string | null): void;
setCpuThrottlingRate(rate: number): void;
saveTemporaryFile(
Expand Down
70 changes: 52 additions & 18 deletions src/tools/input.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import type {ElementHandle} from 'puppeteer-core';
import z from 'zod';

import type {McpContext} from '../McpContext.js';

import {ToolCategories} from './categories.js';
import {defineTool} from './ToolDefinition.js';

Expand Down Expand Up @@ -78,6 +80,40 @@ export const hover = defineTool({
},
});

async function fillFormElement(
uid: string,
value: string,
context: McpContext,
) {
const handle = await context.getElementByUid(uid);
try {
// The AXNode for an option doesn't contain its `value`. We set text content of the option as value.
// If the form is a combobox, we need to find the correct option by its text value.
// To do that, loop through the children while checking which child's text matches the requested value (requested value is actually the text content).
// When the correct option is found, use the element handle to get the real value.
const aXNode = context.getAXNodeByUid(uid);
if (aXNode && aXNode.role === 'combobox' && aXNode.children) {
for (const child of aXNode.children) {
if (child.role === 'option' && child.name === value && child.value) {
const childHandle = await child.elementHandle();
if (childHandle) {
const childValueHandle = await childHandle.getProperty('value');
const childValue = await childValueHandle.jsonValue();
if (childValue) {
await handle.asLocator().fill(childValue.toString());
}
break;
}
}
}
} else {
await handle.asLocator().fill(value);
}
} finally {
void handle.dispose();
}
}

export const fill = defineTool({
name: 'fill',
description: `Type text into a input, text area or select an option from a <select> element.`,
Expand All @@ -94,16 +130,15 @@ export const fill = defineTool({
value: z.string().describe('The value to fill in'),
},
handler: async (request, response, context) => {
const handle = await context.getElementByUid(request.params.uid);
try {
await context.waitForEventsAfterAction(async () => {
await handle.asLocator().fill(request.params.value);
});
response.appendResponseLine(`Successfully filled out the element`);
response.setIncludeSnapshot(true);
} finally {
void handle.dispose();
}
await context.waitForEventsAfterAction(async () => {
await fillFormElement(
request.params.uid,
request.params.value,
context as McpContext,
);
});
response.appendResponseLine(`Successfully filled out the element`);
response.setIncludeSnapshot(true);
},
});

Expand Down Expand Up @@ -155,14 +190,13 @@ export const fillForm = defineTool({
},
handler: async (request, response, context) => {
for (const element of request.params.elements) {
const handle = await context.getElementByUid(element.uid);
try {
await context.waitForEventsAfterAction(async () => {
await handle.asLocator().fill(element.value);
});
} finally {
void handle.dispose();
}
await context.waitForEventsAfterAction(async () => {
await fillFormElement(
element.uid,
element.value,
context as McpContext,
);
});
}
response.appendResponseLine(`Successfully filled out the form`);
response.setIncludeSnapshot(true);
Expand Down
29 changes: 29 additions & 0 deletions tests/tools/input.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,35 @@ describe('input', () => {
assert.ok(await page.$('text/test'));
});
});

it('fills out a select by text', async () => {
await withBrowser(async (response, context) => {
const page = context.getSelectedPage();
await page.setContent(
`<!DOCTYPE html><select><option value="v1">one</option><option value="v2">two</option></select>`,
);
await context.createTextSnapshot();
await fill.handler(
{
params: {
uid: '1_1',
value: 'two',
},
},
response,
context,
);
assert.strictEqual(
response.responseLines[0],
'Successfully filled out the element',
);
assert.ok(response.includeSnapshot);
const selectedValue = await page.evaluate(
() => document.querySelector('select')!.value,
);
assert.strictEqual(selectedValue, 'v2');
});
});
});

describe('drags', () => {
Expand Down