Stylespeak

io.github.Patrizzos由 GitHub 帳號 Patrizzos 發布v1.2.0更新於 Oct 24, 2026

MCP server and CLI that makes CSS legible to AI agents — explains what applies and why.

已驗證STDIO僅桌面瀏覽器自動化開發者工具

概覽

AI 產生的概覽

本機 MCP 伺服器兼 CLI,讓 AI 代理讀懂 CSS:解析層疊、追蹤屬性、預測變更影響,並說明哪些樣式生效及原因。

可以做什麼
解析指定選擇器在給定檔案中實際生效的 CSS 屬性,給出勝出規則、被覆寫規則及信心等級,並解析 var() 鏈、備援值與媒體情境取值。追蹤某個屬性在何處被設定,歸併相互競爭的規則並展示完整層疊鏈。預覽修改選擇器或屬性值的影響範圍,包括被更高優先級屏蔽的選擇器、變數鏈與繼承帶來的下游影響。產生專案級壓縮樣式清單,彙整選擇器、屬性、變數、競爭群組與風險熱點。透過 CDP 查詢執行中的 Chromium 瀏覽器,回傳精確的計算樣式與符合規則。支援一般 CSS、SCSS、CSS Modules,以及從 styled-components 與 Emotion 原始碼靜態擷取樣式。
適用情境
適合 AI 編碼代理在修改樣式前需要理解層疊關係的情境,也適合查詢某條規則為何勝出、某屬性在何處設定、某次變更會影響什麼。也可在工作階段開始時一次載入專案級 CSS 概覽。
執行需求
以本機 stdio 程序執行;npm 套件 @patrizzos/stylespeak 需要 Node.js 21+。live_resolve 工具另需以 9222 埠開啟遠端偵錯的 Chromium 瀏覽器。未宣告任何帳號、API 金鑰或環境變數。
安裝前請注意
靜態分析採啟發式:結果帶有信心等級,部分取值(props、主題、跨檔案匯入的常數、keyframes、跨檔案順序)會被標記、捨棄或近似處理,而非真正解析。live_resolve 需開啟 Chrome 遠端偵錯,這會開放本機 API,機器上任何程序都可連線;README 警告不要在不受信任網路或正式環境使用。

安裝

在 SourceWeft 中

  1. 開啟 儀表板中的 Stylespeak,將其新增到工作區。
  2. 為需要使用其工具的對話啟用該服務。

Desktop only,透過 STDIO。 STDIO 服務會啟動本機處理程序,因此需要 SourceWeft 桌面主機。

其他 MCP 客戶端

參照 儲存庫 中的啟動說明。

README

StyleSpeak - MCP and CLI

A companion MCP server and CLI to stylesafe that makes CSS legible to AI agents — resolving cascade, tracing properties, predicting change impact, and explaining what applies and why before an agent touches a single line of styles.

The problem

AI coding agents write CSS without being able to see its effect. They modify a rule, assume it worked, and move on — unaware that a higher-specificity rule elsewhere already overrides it, a combinator rule in another file is silently winning, or a CSS variable resolves to something entirely different than expected. stylespeak gives agents a structured knowledge layer to consult before making changes.

What it does

resolve_styles — answers "what CSS actually applies to this selector?"

Given a selector and a set of files, returns every CSS property the selector would receive, which rule wins for each, and which rules were overridden — with confidence levels since no real DOM is available. CSS custom properties (var()) are resolved to their actual values, including chained variables, fallbacks, and media-context overrides.

trace_property — answers "everywhere this property is set, who wins?"

Given a property name and a set of files, returns every rule that sets it, groups competing rules that target overlapping selectors, and shows the full cascade chain for each group — with resolved variable values included.

impact_preview — answers "if I change this, what else breaks?"

Given a selector, property, and optional new value, predicts the full blast radius of the change before it's made — showing which selectors will see a different value, which are shielded by higher specificity, which cascade relationships are uncertain, and which downstream rules are affected through CSS variable chains or property inheritance. Works without a browser.

style_manifest — answers "what does this entire project's CSS look like at a glance?"

Builds a compressed, structured summary of a project's entire CSS knowledge — selectors, properties, variables, competition groups, and risk hotspots — that an agent can load once at the start of a session and keep in context instead of repeatedly querying individual files.

live_resolve — answers "what does the browser actually compute for this selector?"

Queries a running Chromium browser via CDP and returns exact computed styles and matched rules. No heuristics, no confidence levels — the browser resolved it. Requires Chrome running with --remote-debugging-port=9222.

Works on more than plain CSS. The four static tools above analyze .css, .scss, CSS Modules, and — as of v1.3 — styled-components and Emotion source files (.js / .jsx / .ts / .tsx), all through the same cascade engine.

Quick start

As a CLI tool

bash
npm install -g @patrizzos/stylespeak
bash
stylespeak resolve ".btn.primary" src/styles/main.cssstylespeak trace "color" --projectRoot src/stylesstylespeak impact ".btn" "background-color" src/styles/main.css --newValue "#ff0000"stylespeak manifest --projectRoot src/styles
# styled-components / Emotion — components are addressed by variable name (const Button = styled.button`` -> .Button)stylespeak resolve ".Button" src/components/Button.jsxstylespeak manifest --projectRoot src

As an MCP server

Add to your MCP client config (Cursor: .cursor/mcp.json, VS Code: .vscode/mcp.json):

json
{  "mcpServers": {    "stylespeak": {      "command": "node",      "args": ["/absolute/path/to/stylespeak/src/server.js"]    }  }}

Once connected, agents can call:

  • resolve_styles({ selector, files, projectRoot, componentFiles? })
  • trace_property({ property, files, projectRoot })
  • impact_preview({ selector, property, newValue?, files, projectRoot })
  • style_manifest({ files?, projectRoot?, maxSelectors? })
  • live_resolve({ selector, port?, tabUrl? })

Example output

impact_preview

bash
stylespeak impact ".btn" "background-color" src/styles/buttons.css --newValue "#ff0000"
json
{  "change": {    "selector": ".btn",    "property": "background-color",    "currentValue": "var(--color-primary)",    "newValue": "#ff0000"  },  "blastRadius": {    "total": 3,    "valueChanges": 1,    "shielded": 1,    "risks": 0,    "variableDownstream": 0,    "inheritanceDownstream": 1  },  "riskLevel": "low",  "safeToChange": true,  "impacts": [    {      "type": "value-change",      "affectedSelector": ".btn",      "currentValue": "var(--color-primary)",      "newValue": "#ff0000",      "confidence": "certain"    },    {      "type": "shielded",      "affectedSelector": ".btn.primary",      "shieldingValue": "darkblue",      "confidence": "certain",      "reason": ".btn.primary has higher specificity — elements with both classes won't be affected"    }  ],  "summary": "1 selector will see a different value, 1 selector is shielded by higher specificity.",  "agentNote": "Change appears safe to make. Shielded selectors are safe — higher-specificity rules protect those elements."}

resolve_styles

bash
stylespeak resolve ".btn" src/styles/buttons.css
json
{  "properties": {    "background-color": {      "winner": {        "value": "var(--color-primary)",        "resolvedValue": "#2563eb",        "variableChain": ["--color-primary → #2563eb"],        "selector": ".btn",        "specificity": "(0,0,1,0)"      },      "confidence": "certain"    }  },  "variables": { "--color-primary": { "value": "#2563eb", "selector": ":root" } }}

Confidence levels

LevelMeaning
certainExact selector match — rule definitively applies
likelyRule tokens are a subset of the queried selector — applies in most cases
possibleCombinator rule — depends on DOM ancestry, unknown without rendering
exactReturned by live_resolve only — browser-resolved, no heuristics

CSS custom property resolution

As of v0.2, stylespeak fully resolves CSS custom properties (var()) in all output:

  • value — the raw value as written (var(--color-primary))
  • resolvedValue — the actual resolved value (#2563eb)
  • variableChain — the full resolution path, including chained variables
  • conditionalValues — media-context overrides where the variable resolves differently

Supported: simple, fallback, nested fallback, chained, scoped, media-context, circular reference protection.

CSS-in-JS support (styled-components & Emotion)

As of v1.3, stylespeak statically extracts styles from .js, .jsx, .ts, .tsx, .mjs and .cjs files that import styled-components, @emotion/styled, @emotion/react or @emotion/css, and runs them through the same cascade engine as plain CSS. resolve_styles, trace_property, impact_preview and style_manifest all work on them, and CSS-in-JS and regular CSS can be analyzed together.

Pass JS/TS files in files, or point projectRoot at a directory — files that import one of these libraries are discovered automatically and all other JS/TS files are ignored.

Addressing components

Generated class names are hashed at runtime, so each styled component is addressed by its variable name as a class selector:

js
const Button = styled.button`...`;    // -> .Buttonexport default styled.section`...`;   // -> .Card  (named after the file, e.g. Card.jsx)

Rules are tagged cssInJs, library and component, and are treated as locally scoped (like CSS Modules): two components named Wrapper in different files are independent, never reported as conflicting, and never counted as a "declared in N locations" risk. Querying such a name is flagged as ambiguous, because results will mix both files.

What is extracted

SyntaxHandling
Tagged templatesstyled.div`…`, styled(Comp)`…`, styled.div.attrs(…)`…`, styled.div<Props>`…` (TypeScript generics), css`…`
Object syntaxstyled.div({ … }), styled(Comp)({ … }), css({ … }), styled.div(props => ({ … }))
Global stylescreateGlobalStyle / injectGlobal — real selectors, not locally scoped
Nesting&, && (specificity bump), &:hover, bare :hover, nested selectors, @media / @supports / @container
Component selectors${Icon} { … } becomes .Icon, e.g. .Button .Icon
ConstantsSame-file const strings, numbers and object paths: ${SPACING}, ${colors.primary}, @media ${bp.md}
css helpersSame-file const mixin = css`…` is inlined where interpolated (${mixin}, ...mixin) and is also queryable by its own name (tagged css-helper) — useful because Emotion's css={mixin} prop makes the helper the only visible record of those styles
Object notationcamelCase becomes kebab-case, vendor prefixes (WebkitX → -webkit-x, msX → -ms-x), unitless numbers get px (except unitless properties like opacity, z-index, line-height)

What is flagged, never guessed

Values that depend on props or theme can't be known statically. stylespeak keeps the original expression, marks it, and tells the agent:

json
"color": {  "winner": {    "value": "${props => props.color}",    "selector": ".Button",    "cssInJs": true,    "library": "styled-components",    "component": "Button",    "dynamic": true  },  "confidence": "certain",  "dynamic": true}
  • dynamic: true on a declaration, winner and property means the value depends on runtime props/theme — the winner may differ depending on props.
  • Conditional blocks and mixin calls (${p => p.active && css…}) can't be evaluated. They are dropped and counted in dynamicInterpolations, and agentNote warns that additional styles may apply at runtime.
  • Style blocks that can't be attached to a selector are listed in cssInJs.skipped in style_manifest output instead of being silently ignored.
  • styled(Base) records extends; agentNote tells the agent to also resolve the base component.

Limitations

This is static analysis, so there are boundaries:

  • styled(Base) does not merge the base component's styles into the child — resolve the base selector separately.
  • Constants and theme objects imported from other files are not resolved (same-file only). ThemeProvider themes are not resolved.
  • Emotion's css prop on JSX elements and style blocks not assigned to a named variable are anonymous — there is no selector to target, so they are reported as skipped.
  • keyframes are not modeled, and .attrs({ className }) is not considered.
  • Cross-file injection order is approximated by the order files are provided.
  • For ground truth on a running app, use live_resolve against the real element.

How it pairs with stylesafe

stylesafe catches problems in your CSS — conflicts, dead rules, Tailwind clashes — before they ship.

stylespeak explains your CSS — resolving cascade, tracing properties, predicting impact, resolving variables — so agents understand before they act.

Use stylesafe as a post-edit check. Use stylespeak as a pre-edit consultation. Together they give AI coding agents a complete feedback loop on styles.

Live browser inspection

live_resolve requires a Chromium browser running with remote debugging enabled:

bash
# Windowschrome.exe --remote-debugging-port=9222
# macOS/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --remote-debugging-port=9222
# Linuxgoogle-chrome --remote-debugging-port=9222

Security note: Never run --remote-debugging-port on a machine exposed to untrusted networks or in production. This flag opens a local API that any process on the machine can connect to.

File format support

FormatSupport level
.cssFull
.scssFull — nesting, & references, @media passthrough
.module.css / .module.scssFull — locally scoped classes detected and tagged
vanilla-extract / Linaria / StyleXSupported via compiled CSS output
styled-components / EmotionSupported (v1.3) — static extraction from .js / .jsx / .ts / .tsx / .mjs / .cjs; props/theme-dependent values are flagged dynamic, not guessed
CSS-in-JS object syntaxSupported (v1.3) — styled.div({…}), css({…}), function-returned objects
Emotion css prop (inline JSX)Not extractable — anonymous, reported as skipped

Architecture

src/  cssParser.js            — CSS tokenizer with SCSS nesting support  specificity.js          — standard (id, class, type) specificity calculator  cssomBuilder.js         — in-memory cascade model builder (CSS, SCSS, CSS Modules, CSS-in-JS)  selectorMatcher.js      — heuristic selector matching with confidence levels  variableResolver.js     — CSS custom property resolution  cssModulesAnalyzer.js   — CSS Modules local scope detection  scssNestingExpander.js  — SCSS nesting pre-processor  cssInJsExtractor.js     — styled-components / Emotion static extractor (template + object syntax)  astComponentGraph.js    — AST component graph for graph-aware matching  resolveStyles.js        — resolve_styles tool  traceProperty.js        — trace_property tool  impactPreview.js        — impact_preview tool  styleManifest.js        — style_manifest project-wide knowledge builder  cdpBridge.js            — Chrome DevTools Protocol WebSocket client  liveResolve.js          — live_resolve tool  server.js               — MCP server (stdio JSON-RPC) + CLI entry point

Zero external dependencies. Requires Node.js 21+.

Roadmap

  • v0.2 ✅ — CSS custom property resolution
  • v0.3 ✅ — SCSS nesting, CSS Modules scope awareness, AST component graph
  • v1.0 ✅ — Chrome DevTools Protocol live resolution
  • v1.1 ✅ — impact_preview: blast radius prediction before making a change
  • v1.2 ✅ — style_manifest: compressed project-wide CSS knowledge for agent context
  • v1.3 ✅ — CSS-in-JS: static extraction for styled-components and Emotion (template and object syntax)

Next (not yet scheduled)

Known gaps in CSS-in-JS support, in no particular order: merging styled(Base) styles into the child, resolving constants and theme objects imported from other files, and modeling keyframes and the Emotion css prop.

來源:README.md,提交 9bcac5a

工具

0
SourceWeft 尚未在沙箱中啟動過該軟體包,因此其工具未知。

版本歷史

1
  1. v1.2.0最新Sep 16, 2026

評分

已滿足適用檢查項的 50%

該評分衡量此收錄條目的完整程度,依據註冊中心條目、端點與公開來源計算得出。它不是對伺服器本身的評價,也不表示伺服器安全或目前可用:可連通性是帶日期的檢查結果,評分也不包含安裝量或使用者評價。

  • 近 30 天內可連通25 分

    請保持端點能夠回應 MCP 握手;SourceWeft 每 30 天探測一次。

  • 已宣告認證方式10 分

    請在註冊中心條目中宣告憑證請求標頭,或保持端點能夠回應探測,以便判定其認證方式。

  • 已列出工具15 分

    請讓伺服器在無憑證時回應 tools/list,或發布可在沙箱中無憑證啟動的軟體包。

  • 描述必需15 分
  • 圖示10 分
  • 文件15 分
  • 授權條款10 分