Skip to content

feat: add cookie management tools - #3

Open
steffen-heil-secforge wants to merge 2 commits into
mainfrom
feature/cookie-tools
Open

steffen-heil-secforge wants to merge 2 commits into
mainfrom
feature/cookie-tools

Conversation

@steffen-heil-secforge

@steffen-heil-secforge steffen-heil-secforge commented Sep 13, 2026

Copy link
Copy Markdown
Member

Important

Depends on #2. Stacked on it: set_cookie resolves {{secret:NAME}} placeholders introduced there, so a session-token cookie value never has to be passed as literal text.

Adds get_cookies, set_cookie and clear_cookies, covering the inspect / edit / delete cases raised in ChromeDevTools#408.

Why a tool rather than the skill

The cookie-debugging skill documents what is reachable today, and is explicit that two things are not:

  • HttpOnly cookies are invisible to cookieStore and document.cookie. The skill's workaround is to find them in the Cookie header of a captured request.
  • Cookies for domains the page never requested never appear in the network tools at all, so a profile cannot be enumerated.

That leaves real gaps. ChromeDevTools#408 asked directly "How important is it to support HttpOnly cookies?", and the answer in the thread was "Would want this... Or else, we'll still have to use playwright for HttpOnly for extraction." These tools go through CDP (Storage.getCookies, Network.setCookie, Network.deleteCookies), so both cases are covered.

Tools

get_cookies

Enumerates every cookie in the browser's default context, optionally filtered by domain.

Cookie values are written to a JSON file and never returned inline. The response reports only each cookie's name, domain, path, expiry and security flags. This matters: on a normal profile this tool finds hundreds of cookies, including live session tokens for every site the user is signed in to, and returning those inline would put them into the conversation transcript (and any log of it) wholesale. filePath is therefore required rather than optional.

set_cookie

Sets a cookie; url or domain is required so the browser knows where it applies. Supports path, expires, httpOnly, secure and sameSite. The value is not echoed back in the response. A rejection by the browser is reported with the likely causes (domain mismatch, or sameSite: "None" without secure).

clear_cookies

Deletes cookies matching name and/or domain.

Clearing everything signs the user out of every site, so the tool refuses to do it implicitly: with no filter given it errors and asks for an explicit all: true. This is deliberate — a model reaching for "clear cookies" to reset one site should not silently destroy every session in the profile.

Testing

tests/tools/cookies.test.ts covers: writing values to the file while keeping them out of the response, the domain filter, set_cookie rejecting a call with neither url nor domain, set_cookie actually setting a cookie without echoing its value, clear_cookies refusing an unfiltered wipe, and clear_cookies deleting only the matching cookie.

These require a browser, so they have not been run in my environment — they follow the existing patterns in that directory and I would appreciate CI confirming them. npm run check-format, tsc --noEmit and the tool registration (62 tools, all three present with the expected schemas) were all verified locally.

… MCP server

Lets a password be filled into a page without ever passing the plaintext
to a tool, so it never enters the conversation transcript. fill,
fill_form, type_text and evaluate_script substitute {{secret:NAME}} with
the contents of <data-dir>/secrets/NAME in this MCP server, immediately
before the input is dispatched to the browser.

A secret is consumed (deleted) once the call succeeds; ":keep" retains it
and ":raw" preserves a trailing newline.

{{script:NAME}} does the same for <data-dir>/scripts/NAME, so the same
JavaScript can be run repeatedly without the caller writing it out in
every call. The code is still sent to the browser each time; only the
caller is spared repeating it. A script takes no modifiers: it is never
consumed and always used exactly as stored.

Resolution runs in exactly two passes, scripts then secrets, so a script
may carry a secret but substituted content is never rescanned: scripts do
not nest and a secret value is never interpreted as a placeholder.

Both directories live under this server's own per-OS data directory,
keyed off "chrome-devtools-mcp" rather than any MCP client, and can be
relocated with CHROME_DEVTOOLS_MCP_SECRETS_DIR and
CHROME_DEVTOOLS_MCP_SCRIPTS_DIR. The per-OS resolution telemetry already
used moves to utils/paths.ts so both share it.

References are basename-only so they cannot escape their directory, and
resolved values are kept out of every response: type_text echoes the
unresolved text, and fill errors report the placeholder rather than the
substituted value.

Adds a secret-handling skill covering staging by reference, login and
2FA flows, script reuse, and the request-body caveat.
@steffen-heil-secforge
steffen-heil-secforge force-pushed the feature/cookie-tools branch 3 times, most recently from 00064fe to d85d10a Compare September 13, 2026 18:29
Adds get_cookies, set_cookie and clear_cookies, covering the inspect,
edit and delete cases raised in ChromeDevTools#408.

Cookies in the browser profile are not fully reachable with the existing
tools: cookieStore and document.cookie cannot see or modify HttpOnly
cookies, and the network tools only show cookies for requests that
happened to be captured. These tools go through CDP, so they cover
HttpOnly cookies and domains the page never requested.

Cookie values are kept out of the conversation in both directions.
get_cookies writes them to a JSON file and reports only each cookie's
name, domain, path, expiry and security flags, so enumerating a profile
does not dump live session tokens into the transcript. set_cookie does
not echo the value back, and resolves {{secret:NAME}} placeholders, so a
session token never has to be passed to the tool as literal text.

clear_cookies deletes cookies matching a name and/or domain, passing
each cookie's partition key so partitioned (CHIPS) cookies are not
silently left behind. Clearing every cookie signs the user out of every
site, so it refuses to do that implicitly: an explicit "all: true" is
required when no filter is given.

The domain filter matches on domain-label boundaries, so "example.com"
covers "www.example.com" but never "notexample.com" or
"example.com.evil.test". A substring match would have let clear_cookies
delete cookies the caller never asked for.

All three act on the default browser context, which the cookie-debugging
skill is updated to say, along with the HttpOnly cases that are now
reachable.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant