* feat(mcp): add list_available_fonts tool for font discovery
The existing list_fonts tool reports only fonts currently used in the
current page (it walks every TEXT node and groups by fontFamily).
Agents authoring new text have no way to discover what fonts the host
can render — they have to guess and hope set_font succeeds.
This adds list_available_fonts, which returns the set of font
families the host advertises. On desktop this is the system fonts
font-kit enumerates plus any bundled fonts the host registers; in
non-desktop runtimes that don't expose enumeration, it returns an
empty list with an explanatory note rather than throwing.
The optional `family` filter mirrors list_fonts for a substring
search, so agents can do `list_available_fonts({family: "mono"})` to
narrow before set_font.
Wired through:
- packages/core/src/figma-api/index.ts: adds optional
listAvailableFontFamilies hook on FigmaAPI (same pattern as
exportImage). Optional so non-desktop hosts aren't forced to
implement it.
- packages/core/src/tools/read.ts: defines the tool.
- packages/core/src/tools/registry.ts: registers it in CORE_TOOLS so
the MCP server advertises it.
- src/automation/figma-factory.ts: provides the desktop
implementation by delegating to engine/fonts.ts#listFamilies which
already wraps the Tauri list_system_fonts sidecar.
* refactor(mcp): reshape to listAvailableFontsAsync matching Figma API
Per review feedback on #210: FigmaAPI mirrors the Figma Plugin API, and
Figma already exposes figma.listAvailableFontsAsync(): Promise<Font[]>
where Font = { fontName: { family, style } }. Replacing the custom
listAvailableFontFamilies hook with the Figma-shaped method means eval'd
plugin code and any future plugin-compat layer get font enumeration for
free. Follows the same pattern as loadFontAsync on FigmaAPI.
- packages/core/src/figma-api/proxy.ts: export FigmaFont type
({ fontName: FigmaFontName }) alongside FigmaFontName.
- packages/core/src/figma-api/index.ts: drop optional
listAvailableFontFamilies; add listAvailableFontsAsync() as a real
method (non-optional) with a safe [] default, mirroring loadFontAsync.
- packages/core/src/tools/read.ts: list_available_fonts execute() now
consumes Font[] and dedupes families via a Set. Drops the
host-capability fallback note — the new method always returns an
array, so a non-desktop host just yields {count: 0, fonts: []}.
- src/engine/fonts.ts: add listFonts() returning TauriFontFamily[]
({family, styles[]}) so the factory can emit one Font per (family,
style) pair.
- src/automation/figma-factory.ts: override listAvailableFontsAsync,
flattening {family, styles[]}[] to Font[].
- oxlint.json: add listFonts to the engine/fonts allowlist.
Tool public shape unchanged (still returns {count, fonts: string[]})
so agents calling list_available_fonts see no change.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add IS_BROWSER constant to core/constants.ts, rewrite IS_TAURI to use it
- Replace 9 raw 'typeof window' checks across canvaskit, fig-export,
figma-api, fig-file, fonts, SafariBanner, use-chat
- Promote no-typeof-window-check lint rule from warn to error
- Extract array mutation helpers (updateArrayItem, removeArrayItem,
toggleArrayVisibility) into useMultiProps — DRY up FillSection and
StrokeSection (removes 20+ duplicated for-loops)
Replace ssrLoadModule hack with simple child_process.spawn.
Same approach as production (Tauri sidecar) — just run the MCP
server entry point directly. No re-evaluation on HMR, no
WebSocket reconnect storms, no globalThis guards.
- New oxlint rule: open-pencil/no-silent-catch — errors on empty catch blocks
- Replace all 8 empty catch blocks with console.warn() logging
- Add worker timeout (30s) and main-thread fallback for .fig parsing
- Fix null crash in renderer filter/picture cache cleanup
- Buffer copy before worker transfer for safe fallback
Three oxlint JS plugin rules in lint/plugin.js:
- no-inline-named-types: configurable shape→name map catches inline
{ x: number; y: number } etc. and suggests Vector, Color, GUID,
Rect, Matrix from @open-pencil/core
- no-structuredclone-scene-arrays: flags structuredClone on
fills/strokes/effects — use typed copy helpers instead
- no-math-random: bans Math.random() in favor of crypto
Fixed 40 violations across 13 files: replaced inline type literals
with named imports, switched figma-api.ts structuredClone calls to
copyFills/copyStrokes/copyEffects.
Add void prefix to 23 fire-and-forget promises across 10 files.
Fix no-base-to-string in bridge.ts by using Buffer.from().toString().
Promote no-floating-promises from warn to error.
Add oxlint-tsgolint dependency and --type-aware --type-check flags.
This replaces the separate tsgo --noEmit step — oxlint now handles
both linting and type checking in a single pass.
Add typescript/no-floating-promises as a warning rule.