How to drive local Discord theme changes in Chrome via the chrome-devtools MCP.
-
Dev server (background):
npm run serve. Listens onhttp://127.0.0.1:8765, watches the configured theme source and base file, and normally rebuilds within a second.GET /theme.css— current combined themeGET /version—{ version }stamp, bumps every rebuildGET /inject.js— loader
Run it as a long-lived process and keep its process/session handle. Do not append a shell
&, which can detach the server while making the command appear finished. Confirm it is up bycurling/version. -
Open Discord:
new_page url=https://discord.com/app(or reuse vialist_pages). -
Install loader:
await (await fetch('http://127.0.0.1:8765/inject.js')).text().then(eval)
Applies the theme, starts ~1s polling against
/version, and exposeswindow.__themeDev. -
Re-inject after navigation. A full navigation or reload wipes the loader. After
navigate_page, checkwindow.__themeDev; if it is missing, run step 3 again.
reload()— force re-fetch + re-apply.off()— remove injected style + stop polling.start()/stop()— toggle polling.computed(selector)— curated computed styles for the first match.find(text)— elements that directly own matching text nodes (case-insensitive; not visibility-filtered).cssVar(name)— resolve a theme variable (--bg-4,bg-4).trace(selector, prop = 'background-color')— best-effort trace of thevar()chain behind a property, including candidate source stylesheet and!importantstatus. It does not fully model cascade and inheritance (specificity, layers, inline styles, or container-query state), so confirm the result with computed styles or DevTools before editing.
For automatic installation during interactive browser work, import scripts/theme-dev.user.js into a userscript manager. Agents should normally use the explicit loader step above so setup remains visible in the thread.
States that disappear on their own are awkward to iterate against—by the time you've taken a screenshot and changed CSS, the element may be gone.
- Author's workflow (interactive): trigger the state, then press F8 (or
Cmd-\) in DevTools to pause JavaScript. The DOM and CSS stay editable in Elements; resume with F8 when done. - Agent workflow: for a variable-only fix, use
cssVar()(ortrace()on:root/body) as a sanity check after the transient element disappears. This confirms the token's value, not that the component consumes it. For component-level verification, throttle the network (emulate networkConditions=Slow 3G) or capture the relevant computed values in oneevaluate_scriptcall while the element is visible. - For uploads specifically: fixtures must live under the workspace root (
/tmpis rejected byupload_file). Use a small fixture (≤8 MB) to avoid upload-limit UI interfering with the test.
Mechanical edits (placeholder swap, typo, rename): edit and stop.
Anything that changes rendering—messages, codeblocks, embeds, mentions, or specificity-sensitive selectors—verify in real Discord UI. Synthetic fragments miss real class names and cascade; "fixed" in isolation can still be broken in production.
Discord's browser CSP may block third-party fonts or images referenced by the theme. Browser verification is authoritative for Discord DOM, cascade, layout, and colors; verify blocked external assets separately in the desktop client. Check the console before treating an absent font or image as a CSS regression.
Preferred test channel (requires access): https://discord.com/channels/730984700658581574/730984700658581577. navigate_page there, re-inject, post/find content that exercises the case, and screenshot before/after. If it is unavailable, use another channel explicitly designated for testing.
Synthetic fragments are fine only for sanity-checking that a CSS variable resolves.