@expressivecss/mcp-server 0.3.1 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "expressivecss-mcp",
3
3
  "displayName": "ExpressiveCSS MCP",
4
- "version": "0.3.1",
4
+ "version": "0.4.0",
5
5
  "description": "ExpressiveCSS workflow server with Setup Expert, Rules Enforcer, Creative Director, Page Arcjitect, Component Syntax Expert, and Quality Inspector.",
6
6
  "author": {
7
7
  "name": "BaezFJ",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "expressivecss-mcp",
3
3
  "displayName": "ExpressiveCSS MCP",
4
- "version": "0.3.1",
4
+ "version": "0.4.0",
5
5
  "description": "ExpressiveCSS workflow server with Setup Expert, Rules Enforcer, Creative Director, Page Arcjitect, Component Syntax Expert, and Quality Inspector.",
6
6
  "author": {
7
7
  "name": "BaezFJ",
package/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.4.0 - 2026-10-11
4
+
5
+ - Command-output redaction no longer stalls the server on long token runs.
6
+ 160K characters of hex output took 6 s to redact; it now takes milliseconds.
7
+ - New `EXPRESSIVECSS_MCP_ALLOWED_PROJECT_ROOTS` setting limits which
8
+ directories a caller can pass as `projectRoot`. It is unset by default, which
9
+ keeps the current behavior.
10
+ - Calls with a `projectRoot` are 3 to 5 times faster. The server resolves the
11
+ ExpressiveCSS version once per call and sizes file buffers to the file
12
+ instead of the read limit.
13
+ - Tool results use compact JSON text, which is about a third smaller.
14
+ - The catalogue, guides and rules cover map, confetti and particles.
15
+ - Bundled framework guidance: ExpressiveCSS 0.14.1.
16
+
3
17
  ## 0.3.1 - 2026-10-08
4
18
 
5
19
  - Fix `npx @expressivecss/mcp-server`, which failed with "could not determine
package/README.md CHANGED
@@ -619,6 +619,7 @@ network limits, evidence handling, and bounded repair workflow.
619
619
  - `EXPRESSIVECSS_MCP_QA_MAX_TOTAL_MB`
620
620
  - `EXPRESSIVECSS_MCP_COMMAND_TIMEOUT_MS`
621
621
  - `EXPRESSIVECSS_MCP_ALLOWED_COMMAND_ROOTS`
622
+ - `EXPRESSIVECSS_MCP_ALLOWED_PROJECT_ROOTS`
622
623
  - `SKIP_SETUP_EXPERT`
623
624
  - `SKIP_RULES_ENFORCER`
624
625
  - `SKIP_CREATIVE_DIRECTOR`
@@ -642,6 +643,8 @@ EXPRESSIVECSS_MCP_ALLOWED_COMMAND_ROOTS=["/srv/projects/site-a","/srv/projects/s
642
643
 
643
644
  The real `projectRoot` must equal or be contained by one of those roots. A tool caller cannot expand this policy. Allowed commands receive only the executable path, system/temp/locale variables, `CI=1`, `NO_COLOR=1`, and `HOME`/`USERPROFILE` reset to the project root. API keys, tokens, passwords, cloud credentials, SSH agent variables, and other MCP-process environment values are not forwarded.
644
645
 
646
+ Any tool accepts a `projectRoot` by default, and `quality_inspector` reports the existence, size and SHA-256 of files under it. To limit which directories a caller can name, set `EXPRESSIVECSS_MCP_ALLOWED_PROJECT_ROOTS` in the same format. A `projectRoot` whose real path is outside every listed root fails with a tool error. An empty or invalid value denies every caller-supplied root. The default root, from `--project-root` or the working directory, is not checked.
647
+
645
648
  ## Sample client configuration
646
649
 
647
650
  ### `mcp/expressivecss/mcp.json`
@@ -1,16 +1,16 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "frameworkVersion": "0.13.1",
4
- "sourceRevision": "3ed93f73a1e5a5ab9d201993b132fd64d81478ca",
3
+ "frameworkVersion": "0.14.1",
4
+ "sourceRevision": "e48e958c184d2de2a972028a06c3a03e0e802197",
5
5
  "basis": "Reviewed checkout snapshot, not proof of published package contents or target-browser conformance. Implemented means only the named scope. Full Material parity remains unassessed.",
6
6
  "browserRun": {
7
- "recordedOn": "2026-10-08",
7
+ "recordedOn": "2026-10-10",
8
8
  "engine": "chromium",
9
9
  "engineVersion": "153.0.8010.12",
10
10
  "nodeVersion": "v24.21.0",
11
11
  "status": "passed",
12
12
  "inputsCurrent": false,
13
- "reportSha256": "b2f7a499810474b2448e87bde25fa13c969ab79a096d7281b84990803d624c47",
13
+ "reportSha256": "901697c1476a6369682ffc54fd0de71c0eed9db9a738b7f16d2c1780956ea287",
14
14
  "collector": "scripts/record-capability-evidence.mjs"
15
15
  },
16
16
  "entries": [
@@ -1432,22 +1432,22 @@
1432
1432
  "file": "tests/message-scroller-browser.test.js",
1433
1433
  "name": "chromium: message scroller follows output, keeps its place on prepend and anchors new turns",
1434
1434
  "scope": "Opening at the end, following streamed text, release on a wheel scroll up, the inert button and resuming from it, following kept through a click on a message, scrollToMessage with a peek and with a missing id, prepend preservation, an appended anchor at the peek with a spacer that clears as the reply grows, and destroy. No spoken-output assertion.",
1435
- "state": "not-recorded"
1435
+ "state": "stale"
1436
1436
  },
1437
1437
  {
1438
1438
  "file": "tests/message-scroller-browser.test.js",
1439
1439
  "name": "chromium: message scroller opens saved transcripts at the requested position",
1440
1440
  "scope": "last-anchor on a long last turn, last-anchor falling back to the end, and start.",
1441
- "state": "not-recorded"
1441
+ "state": "stale"
1442
1442
  },
1443
1443
  {
1444
1444
  "file": "tests/message-scroller-browser.test.js",
1445
1445
  "name": "chromium: message scroller jumps to a message inside a shadow root",
1446
1446
  "scope": "scrollToMessage called right after init finds a message in a shadow root and scrolls it one peek below the top; pressing the button from the keyboard there moves focus to the viewport as the button goes inert.",
1447
- "state": "not-recorded"
1447
+ "state": "stale"
1448
1448
  }
1449
1449
  ],
1450
- "browserEvidence": "incomplete",
1450
+ "browserEvidence": "needs-rerun",
1451
1451
  "recheckWhen": [
1452
1452
  "A reviewed source changes",
1453
1453
  "A mapped test or its recorded inputs change",
@@ -1933,9 +1933,9 @@
1933
1933
  "slug": "pie-chart",
1934
1934
  "title": "Pie chart",
1935
1935
  "kind": "component",
1936
- "support": "implemented",
1936
+ "support": "unassessed",
1937
1937
  "lastReviewedSupport": "implemented",
1938
- "sourceReview": "source-reviewed",
1938
+ "sourceReview": "needs-review",
1939
1939
  "reviewedOn": "2026-10-07",
1940
1940
  "scope": "Slices drawn clockwise from the top from a figure's data table, with a legend of each row's cell and share, donut, values and sparkline modifiers, a <tfoot> total in the donut hole, further columns in the tooltip, and a pointer and keyboard tooltip that highlights the slice.",
1941
1941
  "adaptation": "The <table> stays in the page as the text alternative and the no-JavaScript fallback; the slices, values, total and legend are aria-hidden and the plot is one Tab stop with a polite live tooltip.",
@@ -2006,9 +2006,9 @@
2006
2006
  "slug": "heatmap-chart",
2007
2007
  "title": "Heatmap chart",
2008
2008
  "kind": "component",
2009
- "support": "implemented",
2009
+ "support": "unassessed",
2010
2010
  "lastReviewedSupport": "implemented",
2011
- "sourceReview": "source-reviewed",
2011
+ "sourceReview": "needs-review",
2012
2012
  "reviewedOn": "2026-10-07",
2013
2013
  "scope": "Every body cell of a figure's data table drawn as a shaded grid cell, with row headers beside the grid, column headers below, a scale naming the lowest and highest cells, values, data-label short or hidden labels, data-min and data-max, gaps, and a pointer and keyboard tooltip that moves cell by cell.",
2014
2014
  "adaptation": "The <table> stays in the page as the text alternative and the no-JavaScript fallback; the grid, labels and scale are aria-hidden and the plot is one Tab stop with a polite live tooltip.",
@@ -3242,10 +3242,118 @@
3242
3242
  "file": "tests/marquee-browser.test.js",
3243
3243
  "name": "chromium: marquee loops its two groups, pauses, follows direction and stops under reduced motion",
3244
3244
  "scope": "A short group filling the strip, the copy one gap after it, the half-cycle position horizontally, in RTL and vertically, reverse, pausing on hover, focus and .paused, one link per item in the accessibility tree and the reduced-motion stop with the copy hidden. No spoken-output assertion.",
3245
- "state": "not-recorded"
3245
+ "state": "stale"
3246
+ }
3247
+ ],
3248
+ "browserEvidence": "needs-rerun",
3249
+ "recheckWhen": [
3250
+ "A reviewed source changes",
3251
+ "A mapped test or its recorded inputs change",
3252
+ "Google revises the linked guidance or before claiming a new release matches it"
3253
+ ]
3254
+ },
3255
+ {
3256
+ "slug": "confetti",
3257
+ "title": "Confetti",
3258
+ "kind": "component",
3259
+ "support": "implemented",
3260
+ "lastReviewedSupport": "implemented",
3261
+ "sourceReview": "source-reviewed",
3262
+ "reviewedOn": "2026-10-09",
3263
+ "scope": "Bursts from a button's center over the page, inside an authored canvas, or from script, with count, angle, spread, speed, decay, gravity, drift, lifetime, origin, colors, square, circle and star shapes, and size options, theme colors by default and a promise that resolves when the burst ends.",
3264
+ "adaptation": "A canvas drawn with requestAnimationFrame. The page canvas is an aria-hidden manual popover with pointer events off, so it draws above modal dialogs without blocking clicks. Reduced motion draws nothing.",
3265
+ "upstream": {
3266
+ "source": "https://m3.material.io/components",
3267
+ "reviewedOn": "2026-10-09",
3268
+ "scope": "web-extension-inventory-reviewed",
3269
+ "note": "Requirements and boundaries: design reference web-extensions-review. The inventory page renders client-side, so this review relies on the 2026-09-13 inventory review. Full visual parity and spoken output unverified.",
3270
+ "sections": [
3271
+ "No standalone Material component"
3272
+ ],
3273
+ "sources": [],
3274
+ "requirements": "Keep the effect decorative and out of the accessibility tree, never block input, state the outcome in text and draw nothing when reduced motion is requested."
3275
+ },
3276
+ "relationship": "none",
3277
+ "sources": [
3278
+ {
3279
+ "path": "src/sass/components/_confetti.scss",
3280
+ "sha256": "0dc01d28e2bf26877ab57e5d5504b6dbd734b3ef64f593bf67e34753922a3d63"
3281
+ },
3282
+ {
3283
+ "path": "src/ts/components/confetti.ts",
3284
+ "sha256": "c5f6f8782548d71bce17e1f701b7f7c1376c2a88252fc3e07238699125c6503e"
3285
+ }
3286
+ ],
3287
+ "gaps": [
3288
+ {
3289
+ "kind": "verification",
3290
+ "description": "The burst origin on a button and on a canvas, theme colors, manualStart, the page canvas in the top layer above a modal dialog with pointer events off and aria-hidden, its removal when the burst ends, destroy and the reduced-motion skip are checked in a browser; frame rate on low-end devices and forced colors remain unverified.",
3291
+ "nextStep": "Watch a burst on a low-end phone and in forced colors, and confirm a screen reader announces nothing for it."
3292
+ }
3293
+ ],
3294
+ "browserChecks": [
3295
+ {
3296
+ "file": "tests/confetti-browser.test.js",
3297
+ "name": "chromium: confetti bursts from its trigger in theme colors, on its own canvas, above a modal dialog and not under reduced motion",
3298
+ "scope": "A canvas bursting at its center on init, manualStart waiting for fire(), a button bursting from its center in the theme primary color, the page canvas covering the viewport in the top layer with aria-hidden and no pointer events, fire() resolving after it is removed, a burst over an open modal dialog, destroy and the reduced-motion skip. No spoken-output assertion.",
3299
+ "state": "stale"
3300
+ }
3301
+ ],
3302
+ "browserEvidence": "needs-rerun",
3303
+ "recheckWhen": [
3304
+ "A reviewed source changes",
3305
+ "A mapped test or its recorded inputs change",
3306
+ "Google revises the linked guidance or before claiming a new release matches it"
3307
+ ]
3308
+ },
3309
+ {
3310
+ "slug": "particles",
3311
+ "title": "Particles",
3312
+ "kind": "component",
3313
+ "support": "unassessed",
3314
+ "lastReviewedSupport": "implemented",
3315
+ "sourceReview": "needs-review",
3316
+ "reviewedOn": "2026-10-09",
3317
+ "scope": "A canvas that covers its positioned ancestor with drifting dots that fade near the edges and lean toward the pointer, with quantity, staticity, ease, size, color and drift options and the theme on-surface color by default.",
3318
+ "adaptation": "A canvas drawn with requestAnimationFrame, aria-hidden with pointer events off. The dot color comes from the canvas's CSS color, read each frame so theme changes apply. Reduced motion draws the dots once without motion.",
3319
+ "upstream": {
3320
+ "source": "https://m3.material.io/components",
3321
+ "reviewedOn": "2026-10-09",
3322
+ "scope": "web-extension-inventory-reviewed",
3323
+ "note": "Requirements and boundaries: design reference web-extensions-review. The inventory page renders client-side, so this review relies on the 2026-09-13 inventory review. Full visual parity and spoken output unverified.",
3324
+ "sections": [
3325
+ "No standalone Material component"
3326
+ ],
3327
+ "sources": [],
3328
+ "requirements": "Keep the effect decorative and out of the accessibility tree, never block input, keep text over it readable and stop motion when reduced motion is requested."
3329
+ },
3330
+ "relationship": "none",
3331
+ "sources": [
3332
+ {
3333
+ "path": "src/sass/components/_particles.scss",
3334
+ "sha256": "5d2d67515f7152d5b86d506e7fe42d9c66f058bd7b9e30a06a7b8fedc4738cc3"
3335
+ },
3336
+ {
3337
+ "path": "src/ts/components/particles.ts",
3338
+ "sha256": "7a22a1f580deb76e59bdeef75e8cfe432ee00743106a3348f5ad3c7f05e1f43b"
3246
3339
  }
3247
3340
  ],
3248
- "browserEvidence": "incomplete",
3341
+ "gaps": [
3342
+ {
3343
+ "kind": "verification",
3344
+ "description": "The canvas covering its box, the on-surface default and the color option, following the pointer only while it is over the canvas, drift, refitting on resize, destroy and the still drawing under reduced motion are checked in a browser; frame rate on low-end devices, forced colors and contrast of text over the dots remain unverified.",
3345
+ "nextStep": "Watch a full-width hero of particles on a low-end phone and in forced colors, and check text contrast over the densest area."
3346
+ }
3347
+ ],
3348
+ "browserChecks": [
3349
+ {
3350
+ "file": "tests/particles-browser.test.js",
3351
+ "name": "chromium: particles fill their box in the theme color, follow the pointer, drift, refit on resize and stay still under reduced motion",
3352
+ "scope": "The canvas covering its positioned ancestor, a dot fading in where it was placed in the theme on-surface color, following the pointer inside the canvas and holding when it leaves, destroy clearing the canvas, vx drift, the color option, the pixel buffer after a resize and still dots under reduced motion. No spoken-output assertion.",
3353
+ "state": "stale"
3354
+ }
3355
+ ],
3356
+ "browserEvidence": "needs-rerun",
3249
3357
  "recheckWhen": [
3250
3358
  "A reviewed source changes",
3251
3359
  "A mapped test or its recorded inputs change",
@@ -3360,6 +3468,66 @@
3360
3468
  "Google revises the linked guidance or before claiming a new release matches it"
3361
3469
  ]
3362
3470
  },
3471
+ {
3472
+ "slug": "map",
3473
+ "title": "Map",
3474
+ "kind": "component",
3475
+ "support": "unassessed",
3476
+ "lastReviewedSupport": "implemented",
3477
+ "sourceReview": "needs-review",
3478
+ "reviewedOn": "2026-10-09",
3479
+ "scope": "MapLibre map with themed OpenFreeMap styles, markers with labels, tooltips, popups and dragging, routes, arcs, clusters, and zoom, compass, locate and full screen controls.",
3480
+ "adaptation": "The page passes in the maplibre-gl module. Markers, popups and controls are authored HTML moved into MapLibre; routes, arcs and clusters are GeoJSON layers whose colors are read from theme tokens and redrawn when the theme changes.",
3481
+ "upstream": {
3482
+ "source": "https://m3.material.io/components",
3483
+ "reviewedOn": "2026-10-09",
3484
+ "scope": "web-extension-inventory-reviewed",
3485
+ "note": "Requirements and boundaries: design reference web-extensions-review. The inventory page renders client-side, so this review relies on the 2026-09-13 inventory review. Full visual parity and spoken output unverified.",
3486
+ "sections": [
3487
+ "No standalone Material component"
3488
+ ],
3489
+ "sources": [],
3490
+ "requirements": "Name the map region, make interactive markers named buttons, return focus when a popup closes, keep tile attribution visible and give the same information in text."
3491
+ },
3492
+ "relationship": "none",
3493
+ "sources": [
3494
+ {
3495
+ "path": "src/sass/components/_map.scss",
3496
+ "sha256": "3bc9d7ba4dcf2c8d632701d4a6d127dd0e83662b994591260a58581bc2f3d5de"
3497
+ },
3498
+ {
3499
+ "path": "src/ts/components/mapView.ts",
3500
+ "sha256": "eb0804b2b9368c8af1c9e6d6827c8bde42ceb4e01bdd9b3666d13a867bc67b03"
3501
+ }
3502
+ ],
3503
+ "gaps": [
3504
+ {
3505
+ "kind": "verification",
3506
+ "description": "Marker placement, the popup opened from its marker with aria-expanded, Escape and map-click closing with focus back on the marker, zoom limits, the compass bearing, theme-colored routes, arcs and clusters redrawn after a theme swap, removeOverlay and destroy are checked in a browser; the locate and full screen controls, marker dragging, spoken output, touch gestures and forced colors remain unverified.",
3507
+ "nextStep": "Use the map with a screen reader and on a touch device, try locate and full screen with real permissions, drag a marker with the keyboard, and check forced colors."
3508
+ }
3509
+ ],
3510
+ "browserChecks": [
3511
+ {
3512
+ "file": "tests/map-browser.test.js",
3513
+ "name": "chromium: map places markers and popups, runs controls, themes overlays and restores its markup",
3514
+ "scope": "Markers placed with the default dot, the popup without a marker open at init, a marker button opening its popup with aria-expanded, Escape and a map click closing it with focus back on the marker, zoom out disabled at the minimum zoom, zoom in, the compass bearing and reset, route, arcs and clusters in the theme primary color, no count layer without glyphs, all redrawn in the dark primary after a theme change, removeOverlay, destroy restoring the markup and controls, and a location arriving after destroy being ignored. No spoken-output assertion.",
3515
+ "state": "stale"
3516
+ },
3517
+ {
3518
+ "file": "tests/map-browser.test.js",
3519
+ "name": "chromium: a map in a shadow root opens its marker popup",
3520
+ "scope": "A marker button inside a shadow root finding its popup by id, then opening it with aria-expanded. No spoken-output assertion.",
3521
+ "state": "stale"
3522
+ }
3523
+ ],
3524
+ "browserEvidence": "needs-rerun",
3525
+ "recheckWhen": [
3526
+ "A reviewed source changes",
3527
+ "A mapped test or its recorded inputs change",
3528
+ "Google revises the linked guidance or before claiming a new release matches it"
3529
+ ]
3530
+ },
3363
3531
  {
3364
3532
  "slug": "toolbars",
3365
3533
  "title": "Toolbars",
@@ -3660,16 +3828,16 @@
3660
3828
  "file": "tests/rich-text-editor-browser.test.js",
3661
3829
  "name": "chromium: rich text editor formats text, reflects state and keeps the textarea in sync",
3662
3830
  "scope": "Hidden textarea, textbox role and name from the label, starting HTML, label click focus, bold and heading commands with aria-pressed and .active, undo disabled state, textarea value and input event, empty value, and destroy. No spoken-output assertion.",
3663
- "state": "not-recorded"
3831
+ "state": "stale"
3664
3832
  },
3665
3833
  {
3666
3834
  "file": "tests/rich-text-editor-browser.test.js",
3667
3835
  "name": "chromium: rich text editor follows a disabled textarea",
3668
3836
  "scope": "A disabled textarea makes the editor read-only and disables every toolbar button.",
3669
- "state": "not-recorded"
3837
+ "state": "stale"
3670
3838
  }
3671
3839
  ],
3672
- "browserEvidence": "incomplete",
3840
+ "browserEvidence": "needs-rerun",
3673
3841
  "recheckWhen": [
3674
3842
  "A reviewed source changes",
3675
3843
  "A mapped test or its recorded inputs change",
@@ -3680,9 +3848,9 @@
3680
3848
  "slug": "drop-zone",
3681
3849
  "title": "Drop zone",
3682
3850
  "kind": "component",
3683
- "support": "implemented",
3851
+ "support": "unassessed",
3684
3852
  "lastReviewedSupport": "implemented",
3685
- "sourceReview": "source-reviewed",
3853
+ "sourceReview": "needs-review",
3686
3854
  "reviewedOn": "2026-10-06",
3687
3855
  "scope": "A label-sized native file input with a dragover state and a list of chosen file names and locale-formatted sizes.",
3688
3856
  "adaptation": "The native input covers the label, so clicks, keys and drops are the browser's; the shared Forms behavior marks the drag and fills the list.",