Font Lab
NPM · FONT-LAB · SCANNED SEP 20
A decision surface for typography: curate font directions on your real site, ship next/font code.
Available components
How this component scores in each security and reliability category. Every signal is checked automatically from public evidence about the published package, including repeated runs of it in an isolated sandbox, and we only credit what we can confirm. How we score → Why this is hard to score →
Supply Chain Security100
- No malware found by supply-chain analysis.Pass
- No known CVEs affecting this package version or its production dependencies.Pass
- No install/post-install scripts declared.Pass
- 2 of 16 dependencies flagged as unhealthy. View diagnostics → Partial
Provenance & Transparency100
- Source repository is publicly reachable at the declared URL. View diagnostics → Pass
- Cryptographically verified build provenance (signed, bound to jmg698/Font-Lab). View diagnostics → Pass
- Clear OSI-approved license (MIT).Pass
- Actively maintained (last published 55 days ago).Pass
- Publishes a security disclosure policy (SECURITY.md).Pass
Schema Quality & AI Usability65
- AI-judged instruction clarity (excellent).Pass
- Context-footprint check failed: tool/resource definitions use about 7510 tokens (~288/item across 26 items; 26 tools + 0 resources), over budget; trim descriptions and params. See how to fix → Fail
- Usage-examples check failed: none of the tools include examples. See how to fix → Fail
Stability & Change Management80
- Stability observed for 24 of 30 days with no destabilising changes; credit accrues until the full window elapses.Partial
Tool Coverage96
- 100% of tools have a non-trivial description (not blank, and not just the tool's name).Pass
- 87% of tool parameters carry a description.Partial
Tool Safety100
- No prompt-injection markers were found in the server instructions, tool names or descriptions we captured.Pass
- We read all 26 captured tool definition(s), and no name or description among them implies an irreversible operation.Pass
- An AI judge read all 26 captured unit(s) of tool text and found none that tries to manipulate the model reading it.Pass
Capabilities20
- Spec-recency check failed: implements MCP spec 2024-11-05; the latest is 2026-07-28. See how to fix → Fail
How do I install the Font Lab MCP server?
Font Lab runs locally as an npm package, launched with npx -y font-lab. Ready-made configuration for Claude, Cursor, VS Code, Codex and 5 more is on this page, copied from each client's own documentation.
npm · font-lab
claude mcp add jmg698-font-lab -- npx -y font-lab
{
"mcpServers": {
"jmg698-font-lab": {
"command": "npx",
"args": [
"-y",
"font-lab"
]
}
}
} {
"servers": {
"jmg698-font-lab": {
"command": "npx",
"args": [
"-y",
"font-lab"
]
}
}
} codex mcp add jmg698-font-lab -- npx -y font-lab
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"jmg698-font-lab": {
"type": "local",
"command": [
"npx",
"-y",
"font-lab"
],
"enabled": true
}
}
} openclaw mcp add jmg698-font-lab --command npx --arg -y --arg font-lab
mcp_servers:
jmg698-font-lab:
command: "npx"
args: ["-y", "font-lab"] {
"McpServers": {
"jmg698-font-lab": {
"Transport": "stdio",
"Command": "npx",
"Arguments": [
"-y",
"font-lab"
]
}
}
} assistant mcp add jmg698-font-lab -t stdio -c npx -a -y font-lab
{
"mcpServers": {
"jmg698-font-lab": {
"command": "npx",
"args": [
"-y",
"font-lab"
]
}
}
} Every change we have recorded for this component, newest first. Security-relevant changes are always shown. ▲ marks a change for the better, ▼ a change for the worse; unmarked changes are neutral.
- 19 Sept 26 −3
- Stability: pass → 0.77 functional
- 18 Sept 26 0
- Stability: 0.97 → pass security
- 17 Sept 26 +1
No change was recorded against any check on this day. Stability & Change Management went from 93 to 97. That category is still filling its 30-day observation window: 28 days of observed history at the previous scan, 29 at this one. The score rises as the window fills, whether or not the server changes.
- 15 Sept 26 +1
No change was recorded against any check on this day. Stability & Change Management went from 87 to 90. That category is still filling its 30-day observation window: 26 days of observed history at the previous scan, 27 at this one. The score rises as the window fills, whether or not the server changes.
- 13 Sept 26 +1
No change was recorded against any check on this day. Stability & Change Management went from 80 to 83. That category is still filling its 30-day observation window: 24 days of observed history at the previous scan, 25 at this one. The score rises as the window fills, whether or not the server changes.
- 12 Sept 26 −3
- Stability: pass → 0.80 functional
- 11 Sept 26 0
- Stability: 0.97 → pass security
- 10 Sept 26 +1
No change was recorded against any check on this day. Stability & Change Management went from 93 to 97. That category is still filling its 30-day observation window: 28 days of observed history at the previous scan, 29 at this one. The score rises as the window fills, whether or not the server changes.
Diagnostic detail from the automated scan of this channel: what the scanner observed at each step, so you can see exactly where a check passed or failed. It is informational only and never changes the trust score.
Captured 20 Sept 2026 · Analysed npm/font-lab@1.0.0
Provenance Verified
A signed build attestation was found and verified, binding this exact artifact to the source repository it claims to come from.
| Result | Verified |
|---|---|
| Ecosystem | npm |
| Reason | Verified |
| Discovered via | Registry attestation endpoint |
| Source repo | jmg698/Font-Lab |
| Certificate issuer | https://token.actions.githubusercontent.com |
| Certificate SAN | https://github.com/jmg698/Font-Lab/.github/workflows/publish.yml@refs/tags/V1.0.0 |
| Rekor log index | 2256475932 |
| Predicate type | https://slsa.dev/provenance/v1 |
| Subject digest | sha512:c49b57f952598989e8d4ec2b720ad54333bf05fcd22951661f48e1813510d77e88796dc17793abd9fd3ba702cb534d64202a5f5fed19c701411d66edb |
Background: How many MCP packages publish verified provenance →
Dependencies 16 packages
| Packages resolved | 16 |
|---|---|
| Stale | 2 |
| Tree resolution | Complete |
Background: SBOMs and build attestations, explained →
The tools this component advertises to a client, with an estimated token cost for each. Expand a tool to see its parameters and schema. The per-tool counts are indicative and are not scored directly; the schema's total context footprint is one signal in Schema Quality & AI Usability. A tool's description is untrusted text the model reads on every call, which is what makes this list a security surface and not just an inventory: how tool poisoning works →
font_lab_analyze ~226
Audit ANY web project's CURRENT typography before changing it: framework (Next/Vite/Astro/Remix/SvelteKit/TanStack/…), router, Tailwind version, the current display/body/mono fonts, how they're wired, and coverage warnings (e.g. a font that's declared but not actually rendered). The result's `capabilities` + `shipNote` name the right path for THIS stack — live panel on Next App Router, portable preview + css-entry auto-ship elsewhere (Tailwind v4, v3, or var-wired plain CSS), hand-apply only when there's no seam. NEVER treat a non-Next stack as unsupported without reading `capabilities`. Prefer font_lab_start as the front door — it runs this AND returns the design brief (intake questions + what to avoid/reach for). Use this directly only when you just need the raw audit.
| Name | Type | Req | Description |
|---|---|---|---|
| projectDir | string | yes | Absolute path to the user's project root (any framework — Next.js, Vite, Astro, Remix, SvelteKit, TanStack, …). |
No output schema declared.
No examples provided.
font_lab_apply ~292
Ship the human's pick, idempotently and reversibly (backup-first). On Next.js App Router it writes next/font + Tailwind — Google faces via next/font/google, open-foundry faces via next/font/local with the woff2 self-hosted into the source tree (every family is verified buildable BEFORE any file is written; unverifiable families refuse with the reason). On ANY OTHER framework (TanStack/Vite/Astro/Remix/SvelteKit/…) it self-hosts the parity @font-face into the CSS entry and routes it through the project's own seam — Tailwind v4 @theme, Tailwind v3's config-generated font-* utilities + Preflight base, or the project's own CSS font vars — no next/font needed. Refuses only when there's no auto-ship branch (hardcoded font-family, CSS-in-JS), with a clear reason (check font_lab_analyze.capabilities). After applying on Next, run the project's build (or dev-server compile) to confirm it compiles — then close the loop with font_lab_verify (it starts the dev server itself if needed): apply edits files, the receipt proves pixels. Run after wait_for_pick/read_pick returns a selection.
| Name | Type | Req | Description |
|---|---|---|---|
| projectDir | string | yes | Absolute path to the user's project root (any framework — Next.js, Vite, Astro, Remix, SvelteKit, TanStack, …). |
No output schema declared.
No examples provided.
font_lab_check_fonts ~212
Check whether specific fonts can ship with Font Lab's preview==ship guarantee — use this to REACH BEYOND the built-in catalog. Pass family names (any of ~1,500 Google fonts like 'Hedvig Letters Serif', or a curated open-foundry face like 'Cabinet Grotesk' / 'General Sans'). Each returns a verdict: 'guaranteed' (full WYSIWYG), 'best-effort' (shippable, but the preview may not be byte-for-byte — show the human the warnings and let them decide), or 'unavailable' (can't ship, with the reason). The catalog is a floor, not a ceiling: reach for distinctive faces that fit the brief and confirm them here before composing.
| Name | Type | Req | Description |
|---|---|---|---|
| families | array | yes | Font family names to check. |
| projectDir | string | – | Absolute path to the user's project root (any framework — Next.js, Vite, Astro, Remix, SvelteKit, TanStack, …). |
No output schema declared.
No examples provided.
font_lab_compose_directions ~422
The PRIMARY way to build the menu: assemble tailored font directions for the user's brief (from font_lab_start's intake answers). Pass the user's stated direction as `brief` — if you omit it, the result is INFERRED rather than tailored, and you'll get a warning telling you to ask first. Reach PAST the overexposed defaults — give each direction a distinctive face and a one-line rationale tying it to what they asked for. Each direction needs display, body, and mono families. Families can be ANY shippable font (catalog, ~1,500 Google fonts, or a curated open-foundry face) — the gate admits them; check uncertain ones first with font_lab_check_fonts. REJECTS a menu that's too generic (any direction overexposed in both display and body, or a set whose every display is an overexposed default) — fix it with distinctive faces, or pass force:true only if the user explicitly wants the default look. Returns validated, preview-ready directions plus warnings (overexposed-default flags, and a best-effort fidelity note when a font can't be guaranteed byte-for-byte) and the stack's `nextStep`. projectDir is REQUIRED: the composed set persists (.font-lab/preview.json) as the project's default menu, so font_lab_screenshot_directions / font_lab_preview / font_lab_select resolve against EXACTLY these directions on every framework — a compose that didn't persist is how a later capture dead-ends with 'no composed set'.
| Name | Type | Req | Description |
|---|---|---|---|
| brief | string | – | The user's stated direction from intake (what feeling / how bold / brand). Omitting it warns you to ask first. |
| directions | array | yes | – |
| force | boolean | – | Override the anti-generic gate (use only when the user explicitly wants overexposed default fonts). |
| projectDir | string | yes | The project root (REQUIRED). Admitted fonts are cached for the preview build AND the composed set persists as the project's default preview menu — what screenshots/select resolve against on every fra… |
No output schema declared.
No examples provided.
font_lab_curate ~193
FALLBACK menu — ~5 font directions (display+body+mono pairings) that move off the project's current fonts, no LLM. Seeded to THIS project (its name + palette + copy), so the spread differs from project to project instead of being the same five everywhere — but it is still a generic starting point, NOT tailored to the user's brief. Use it only when you have NO brief. When you DO have a brief (from font_lab_start's intake questions), prefer font_lab_compose_directions and tailor the options to what they asked for — that's the better experience. Pass an optional 'vibe' to steer the fallback.
| Name | Type | Req | Description |
|---|---|---|---|
| count | number | – | – |
| projectDir | string | yes | Absolute path to the user's project root (any framework — Next.js, Vite, Astro, Remix, SvelteKit, TanStack, …). |
| vibe | string | – | – |
No output schema declared.
No examples provided.
font_lab_finish ~342
THE FINISHING MOVE — run this when the human is done choosing (they clicked *done ✓* in the panel, said so in chat, or a pendingCleanup note rode a tool result). One call: strips the dev-panel scaffolding (uninit — applied fonts and copy edits are untouched), clears the panel's done signal, and returns the git-verified `commitPlan`: the SHIP pile (the human's copy edits + font apply, cross-checked against `git status` so undone files don't get committed) with ready-to-run `git add`/`git commit` commands and a suggested message, plus what's scaffold, install wiring, or the human's own unrelated work. Relay the commands to the human — NEVER run git commit/push yourself unless they explicitly ask (ephemeral remote workspaces excepted — the environment note says so). Pass uninstall:true when the human is done with Font Lab entirely (also removes MCP registrations, the skill, the AGENTS.md block, and hooks); keepScaffold:true finishes without unmounting the panel. The loop is: apply → verify → finish.
| Name | Type | Req | Description |
|---|---|---|---|
| keepScaffold | boolean | – | Leave the dev panel mounted (the human wants to keep exploring) — just get the commit plan and clear the done signal. |
| projectDir | string | yes | Absolute path to the user's project root (any framework — Next.js, Vite, Astro, Remix, SvelteKit, TanStack, …). |
| uninstall | boolean | – | Also remove Font Lab's install wiring everywhere (MCP configs, skill, AGENTS.md block, hooks). Default false — keep it for next time. |
No output schema declared.
No examples provided.
font_lab_healthcheck ~478
VERIFY THE SETUP BEFORE INVITING THE HUMAN — run this after font_lab_init (and again after any dev-server restart or CSP/config change), and act on it: a white page or a missing panel is an AGENT failure to catch here, not a human setup step. One read-only pass checks: VERSION ALIGNMENT (this process vs the project's installed font-lab vs the panel stamp vs the :7777 endpoint — the npx-cache skew that stamps broken panels), SCAFFOLD COMPLETENESS (every app/_fontlab/ file present and every relative import resolving — the class that becomes `Module not found: Can't resolve './fl-census'` → Next 500 → a white page that reads as a dead server), the DEV SERVER (a real GET of the homepage — expects 200, and sniffs a 500 body to NAME the missing module instead of leaving you guessing), the PICK/EDIT ENDPOINT on :7777, and the CONTENT-SECURITY-POLICY (a strict CSP kills the panel silently: no dev 'unsafe-eval' → the client tree never hydrates → no panel; no connect-src for 127.0.0.1:7777 → picks and copy edits fail — findings come WITH a paste-ready dev-only patch, and the reminder that Next reads headers at STARTUP, so restart the dev server after config changes). Returns { ready, blockers, warnings, checks, nextStep }: ready:true → arm a listener, then invite the human; blockers → each carries its fix (usually `npx font-lab upgrade` or the CSP patch) — clear them and re-run until ready. A dev server that simply isn't running is only a warning (screenshot/verify tools start one themselves), but the LIVE panel path needs it up.
| Name | Type | Req | Description |
|---|---|---|---|
| baseUrl | string | – | The dev server URL the human would open (e.g. http://localhost:3000). Optional — defaults to the recorded dev server; unreachable is a BLOCKER when passed explicitly, a warning otherwise. |
| port | number | – | Pick-endpoint port (default 7777). |
| projectDir | string | yes | Absolute path to the user's project root (any framework — Next.js, Vite, Astro, Remix, SvelteKit, TanStack, …). |
No output schema declared.
No examples provided.
font_lab_init ~567
SET UP the live preview panel in the project, built from the directions YOU composed for the user's brief — self-hosts the bundles, installs the dev panel, mounts it (dev-only). NEXT.JS APP ROUTER ONLY (the panel mounts in layout.tsx): on any other framework (Vite/Astro/Remix/SvelteKit/TanStack/…) SKIP this and use font_lab_preview instead — the pick still ships via font_lab_apply. Pass the `directions` from font_lab_compose_directions; the panel shows exactly those. This REFUSES without directions (so the generic default menu can't be mounted without asking the user first) — only pass allowFallback:true if the user explicitly wants the deterministic default. Run after start → intake → compose. Idempotent + reversible (font_lab_uninit). GUARDED TWO WAYS: it refuses on font-lab VERSION SKEW (running tool ≠ the project's installed font-lab — the npx-cache trap that stamps broken panels; fix: `npx font-lab upgrade`, then reload the session; allowVersionSkew:true only for a deliberately mixed checkout), and it SELF-CHECKS the stamp before reporting success (every file present, every app/_fontlab/ import resolving — so a scaffold that would 500 is refused, never celebrated). AFTER a successful init: start the dev server, then run font_lab_healthcheck and DO NOT invite the human until it reports ready:true. Reported dead roles are SHIP scope, not a preview problem: the panel previews every role by painting the rendered page; a dead chain just means shipping that role needs font_lab_rewire_dead_roles or an agent edit (the pick declares this scope).
| Name | Type | Req | Description |
|---|---|---|---|
| allowFallback | boolean | – | Mount the deterministic default menu without a brief — only if the user explicitly wants it. |
| allowVersionSkew | boolean | – | Proceed despite a font-lab version mismatch between this process and the project's installed package. ONLY for a deliberately mixed checkout — the default refusal exists because skewed stamps ship br… |
| count | number | – | – |
| directions | array | – | The brief-driven directions to show in the panel (from compose_directions). |
| projectDir | string | yes | Absolute path to the user's project root (any framework — Next.js, Vite, Astro, Remix, SvelteKit, TanStack, …). |
| tracked | boolean | – | Commit-track the panel scaffolding instead of self-ignoring it (default false: app/_fontlab/ and public/fontlab/ carry a nested .gitignore so they never show in git status). Set true only when the us… |
| vibe | string | – | – |
No output schema declared.
No examples provided.
font_lab_list_catalog ~98
Browse Font Lab's curated catalog of ship-ready variable Google fonts (each verified for capsize/CLS-safe parity). Filter by role ('display'|'body'|'mono') or vibe tag (e.g. 'editorial','geometric','serif','technical'). Use this to compose your OWN font directions when the default curation isn't what the user asked for.
| Name | Type | Req | Description |
|---|---|---|---|
| role | string | – | – |
| tag | string | – | – |
No output schema declared.
No examples provided.
font_lab_live_instructions ~210
Get ready-to-run commands to launch the FULL live editor (flip / mix / compare directions in a real browser) — for when the headless screenshots aren't enough and the human wants to drive it themselves. Detects the project's dev command. In a LOCAL session these run in the agent's/human's terminal here; in a REMOTE/container session the result reframes them as commands for the human's OWN machine after pulling the branch (never a localhost URL handoff the human can't reach — pass `remote` to override detection).
| Name | Type | Req | Description |
|---|---|---|---|
| projectDir | string | yes | Absolute path to the user's project root (any framework — Next.js, Vite, Astro, Remix, SvelteKit, TanStack, …). |
| remote | boolean | – | Set true if you're a cloud/container agent whose human CANNOT open localhost URLs on this machine (auto-detected for common clouds — Claude Code on the web, Codespaces, Gitpod, Codex cloud; pass expl… |
No output schema declared.
No examples provided.
font_lab_more_directions ~202
Add MORE options to the live panel — when the human wants to keep exploring beyond the current set. Compose additional tailored directions first (font_lab_compose_directions), then pass them here; they're admitted and APPENDED to the panel (existing options are kept), and the panel updates live. Use this whenever the user asks 'what else?' / 'show me more' — the menu is never capped. This ALSO fulfills an in-panel 'more options' request (from font_lab_wait_for_request): appending clears the pending request and flips the menu from provisional 'starter' to tailored. Honor the request's brief and reach past its exclude list so the new options are genuinely different.
| Name | Type | Req | Description |
|---|---|---|---|
| directions | array | yes | The new directions to append (from compose_directions). |
| projectDir | string | yes | Absolute path to the user's project root (any framework — Next.js, Vite, Astro, Remix, SvelteKit, TanStack, …). |
No output schema declared.
No examples provided.
font_lab_prepare_preview ~257
Rebuild the LIVE preview bundle for the directions YOU composed for the user's brief (self-hosted woff2 + exact next/font fallbacks), so the HUMAN can flip through them and pick. Pass the `directions` from compose_directions. REFUSES without directions (don't rebuild the generic default menu without asking the user) — only pass allowFallback:true if they explicitly want the default. Use font_lab_init for first setup; use this to rebuild after changing the options. Never auto-selects. (Fetches fonts.)
| Name | Type | Req | Description |
|---|---|---|---|
| allowFallback | boolean | – | Build the deterministic default menu without a brief — only if the user explicitly wants it. |
| allowVersionSkew | boolean | – | Proceed despite a font-lab version mismatch between this process and the project's installed package (default: refuse — skewed stamps ship broken panels; fix with `npx font-lab upgrade`). |
| count | number | – | – |
| directions | array | – | The brief-driven directions to build (from compose_directions). |
| projectDir | string | yes | Absolute path to the user's project root (any framework — Next.js, Vite, Astro, Remix, SvelteKit, TanStack, …). |
| vibe | string | – | – |
No output schema declared.
No examples provided.
font_lab_preview ~368
LAST-RESORT specimen sheet — a self-contained HTML file of GENERIC CARDS (one per direction, parity fonts embedded, opens offline), rendered on the project's palette and copy. These are NOT the human's pages, so this tool is LOCKED until the real-site path has actually been tried: it REFUSES unless a font_lab_screenshot_directions attempt failed on infrastructure (no Chromium could launch, the dev server wouldn't serve — recorded automatically in .font-lab/capture-blocked.json), or you pass force:true because the human EXPLICITLY asked for a portable offline artifact. On every framework the choosing surface is font_lab_screenshot_directions — it manages the dev server itself, so 'no dev server running' is NOT a reason to be here. When unlocked: directions default to the composed set (errors rather than silently rendering the starter menu; allowFallback:true opts in), the result carries `unlockedBecause`, and each card has a live render-check badge (a real width-diff, not a fonts.check false-positive). SHOW the sheet, have the human pick an id, then font_lab_select → apply.
| Name | Type | Req | Description |
|---|---|---|---|
| allowFallback | boolean | – | Render the deterministic starter menu when nothing was composed — only if the user explicitly wants the untailored default. |
| count | number | – | – |
| directions | array | – | The brief-driven directions to render (from compose_directions). Defaults to the persisted composed set. |
| force | boolean | – | Bypass the screenshots-first lock — ONLY when the human explicitly asked for the portable offline sheet. |
| projectDir | string | yes | Absolute path to the user's project root (any framework — Next.js, Vite, Astro, Remix, SvelteKit, TanStack, …). |
| vibe | string | – | – |
No output schema declared.
No examples provided.
font_lab_preview_screenshots ~428
Headless captures of the LAST-RESORT specimen sheet (font_lab_preview) — one render-checked PNG per generic card. Same lock as font_lab_preview: REFUSES until a real font_lab_screenshot_directions attempt has failed on infrastructure (recorded automatically), or force:true because the human explicitly wants the offline sheet — on every framework the real-site captures are the choosing surface, and this needs a Chromium anyway (if a browser works, real-site screenshots usually work too; the genuine niche is a dev server that can't serve). When unlocked: each card is render-checked (a real width-diff catches a silently-fallen-back font; failures are reported, never passed off as the real face), the result carries `unlockedBecause`, and you SHOW the images and record the human's pick with font_lab_select. Directions default to the persisted composed set (or pass a prior sheet's htmlPath); with nothing composed it ERRORS rather than silently using the starter menu.
| Name | Type | Req | Description |
|---|---|---|---|
| allowFallback | boolean | – | Render the deterministic starter menu when nothing was composed — only if the user explicitly wants the untailored default. |
| directions | array | – | The brief-driven directions to render (from compose_directions). Omit if passing htmlPath; defaults to the persisted composed set. |
| executablePath | string | – | Optional Chrome/Chromium binary path (usually unnecessary). |
| force | boolean | – | Bypass the screenshots-first lock — ONLY when the human explicitly asked for the portable offline sheet. |
| htmlPath | string | – | Path to an already-built preview sheet (from font_lab_preview) to screenshot as-is. |
| inlineImages | boolean | – | Default true over MCP: card images ride the result as image content blocks. false returns paths only. |
| outDir | string | – | Where to write PNGs; defaults to <project>/.font-lab/previews. |
| projectDir | string | yes | Absolute path to the user's project root (any framework — Next.js, Vite, Astro, Remix, SvelteKit, TanStack, …). |
No output schema declared.
No examples provided.
font_lab_read_pick ~97
One-shot read of the human's pick (.font-lab/selection.json); null until they've chosen. Prefer font_lab_wait_for_pick (it blocks and shows the human 'agent listening') — use read_pick for a quick non-blocking check.
| Name | Type | Req | Description |
|---|---|---|---|
| projectDir | string | yes | Absolute path to the user's project root (any framework — Next.js, Vite, Astro, Remix, SvelteKit, TanStack, …). |
No output schema declared.
No examples provided.
font_lab_rewire_dead_roles ~139
Fix a role that font_lab_analyze flags as DEAD — declared but not actually rendered (common with Tailwind v4 @theme inline + a hand-written `font-family: var(--font-display)`, which resolves to nothing). Points those raw usages at the published leaf variable so the font renders, making the swap visible. Reversible via font_lab_undo. Offer this when analyze reports dead roles and the user wants that role to actually change.
| Name | Type | Req | Description |
|---|---|---|---|
| projectDir | string | yes | Absolute path to the user's project root (any framework — Next.js, Vite, Astro, Remix, SvelteKit, TanStack, …). |
No output schema declared.
No examples provided.
font_lab_screenshot_directions ~685
Screenshot the human's REAL RUNNING SITE in each curated direction — THE choosing moment on every framework (and the ONLY sanctioned preview surface off Next until it has actually failed: the generic font_lab_preview sheet stays locked behind a failed attempt here). On Next.js with the panel init'd it drives the panel; on every other stack (Vite / Astro / Remix / SvelteKit / TanStack / plain CSS) it paints the rendered page directly through the census — the same machinery the panel flips with — after injecting the parity @font-face inline (no init, no project writes: preview fonts cache under .font-lab/, never public/). If NO dev server is reachable it STARTS the project's own dev command itself (managed: bound to 127.0.0.1 — sidesteps IPv6-only binds and sandboxed-shell backgrounding — health-checked, and stopped after the capture); pass ensureServer:false to forbid that, or baseUrl to use a server you already run. Directions default to the composed set persisted by font_lab_compose_directions (.font-lab/preview.json) — with none, it ERRORS rather than silently capturing the untailored starter menu (allowFallback:true opts in deliberately). Returns a manifest per direction {id, name, vibe, rationale, fonts, screenshot (full-page PNG), heroShot (viewport JPEG — chat/phone-sized)} plus a 'current' before-shot — over MCP the heroShots ALSO ride the result as inline image blocks, so show them to the human IMMEDIATELY (no file hunting) and ask them to pick an id. If it errors on a missing Playwright driver, install one in the project (`npm i -D playwright-core`) and RETRY THIS TOOL — the install is picked up without a session restart. Makes no edits.
| Name | Type | Req | Description |
|---|---|---|---|
| allowFallback | boolean | – | Capture the deterministic starter menu when nothing was composed — only if the user explicitly wants the untailored default. |
| baseUrl | string | – | A dev server you already run, e.g. http://localhost:3000 or :5173. Optional — with none reachable, Font Lab starts the project's dev command itself (see ensureServer). |
| directions | array | – | The directions to capture (from compose_directions). Defaults to the persisted composed set (.font-lab/preview.json); with neither, this ERRORS instead of silently using the starter menu. |
| ensureServer | boolean | – | Default true: start the project's dev server (managed, 127.0.0.1, stopped after) when none is reachable. false forbids starting processes. |
| executablePath | string | – | Optional path to a Chrome/Chromium binary. Usually unnecessary — it finds a system/pre-installed browser automatically. |
| inlineImages | boolean | – | Default true over MCP: heroShots ride the result as image content blocks so the human sees them in-chat immediately. false returns paths only. |
| outDir | string | – | Where to write images; defaults to <project>/.font-lab/previews. |
| projectDir | string | yes | Absolute path to the user's project root (any framework — Next.js, Vite, Astro, Remix, SvelteKit, TanStack, …). |
| routes | array | – | Route(s) to capture; defaults to ['/']. |
No output schema declared.
No examples provided.
font_lab_select ~219
Record the human's pick by direction id — the HEADLESS counterpart to clicking Pick in the panel. Use AFTER the human has chosen from the screenshots (you must still let the HUMAN make the call — never auto-select). The id resolves against the SAME set the human was shown: explicit `directions` if passed, else the composed set persisted by font_lab_compose_directions. Writes the same selection.json the panel writes, so font_lab_apply ships it identically. Supports a mixed pick: pass roles {display, body, mono} as direction ids to take each role from a different direction.
| Name | Type | Req | Description |
|---|---|---|---|
| directionId | string | yes | The id the human picked (from compose/screenshots). |
| projectDir | string | yes | Absolute path to the user's project root (any framework — Next.js, Vite, Astro, Remix, SvelteKit, TanStack, …). |
| roles | object | – | Optional mixed pick — per-role direction ids, e.g. {display:'editorial-serif', body:'modern-grotesque'}. |
No output schema declared.
No examples provided.
font_lab_start ~366
START HERE when a user wants to choose, change, or improve fonts — on ANY framework (Next.js, Vite, Astro, Remix, SvelteKit, TanStack, …). Returns the project analysis (including `capabilities` + `shipNote` — the preview/ship path for THIS stack, so a non-Next project is a different route, never a dead end), an `environment` block (local vs remote/container session — with the workflow consequences spelled out, e.g. screenshots ARE the choosing moment when the human can't reach this machine's localhost), a `context` block (the project's existing color palette, brand/design docs, and a sample of the real copy — so your options fit THIS project), PLUS Font Lab's design brief: the framing questions to ASK THE HUMAN FIRST (what feeling? how bold a departure? any brand to evoke or avoid?), a strategy scaffold (reason about the brief before naming fonts), the overexposed defaults to AVOID (Inter, Geist, Space Grotesk, …), distinctive references to reach for, and the rule that every direction needs a brief-tied rationale. Read the context, ask the intake questions and WAIT for the answers before proposing any fonts — that's what makes the result tailored instead of generic. The HUMAN always makes the final pick.
| Name | Type | Req | Description |
|---|---|---|---|
| projectDir | string | yes | Absolute path to the user's project root (any framework — Next.js, Vite, Astro, Remix, SvelteKit, TanStack, …). |
| remote | boolean | – | Set true if you're a cloud/container agent whose human CANNOT open localhost URLs on this machine (auto-detected for common clouds — Claude Code on the web, Codespaces, Gitpod, Codex cloud; pass expl… |
No output schema declared.
No examples provided.
font_lab_status ~282
One snapshot of the whole handoff: the current pick (if any), whether it's been shipped (applied), whether an agent is waiting, whether the pick endpoint is up, the environment (local vs remote/container — with the workflow consequences), and the latest backup. Call this when resuming a session, before apply, or whenever you need to know where the loop stands. Its `devServer` field health-checks the dev server the panel last reported — if `devServer.up` is false, either RESTART it or just call font_lab_screenshot_directions / font_lab_verify, which start the project's dev server themselves when none is reachable. Its `commitPlan` field is the git-verified answer to 'what do I actually commit?' — the ship pile (the human's copy edits + font apply) with ready-to-run git commands, separated from scaffold, install wiring, and files Font Lab never touched (`sourceChanges` is the raw ledger behind it). A pending `done` field means the human clicked *done ✓* in the panel — run font_lab_finish.
| Name | Type | Req | Description |
|---|---|---|---|
| port | number | – | Pick-endpoint port (default 7777). |
| projectDir | string | yes | Absolute path to the user's project root (any framework — Next.js, Vite, Astro, Remix, SvelteKit, TanStack, …). |
No output schema declared.
No examples provided.
font_lab_undo ~69
Revert Font Lab's last apply or rewire, restoring the edited files byte-for-byte from the backup.
| Name | Type | Req | Description |
|---|---|---|---|
| projectDir | string | yes | Absolute path to the user's project root (any framework — Next.js, Vite, Astro, Remix, SvelteKit, TanStack, …). |
No output schema declared.
No examples provided.
font_lab_uninit ~147
Strip ONLY the dev-panel scaffolding (the layout.tsx mount, app/_fontlab/, public/fontlab/ preview fonts) — any applied font change and copy edits are left intact. PREFER font_lab_finish: it runs this AND returns the git-verified commit plan in one call, which is what the end of a session actually needs. Reach for bare uninit only when you want the scaffolding gone mid-session (e.g. the human wants to keep exploring later but commit now).
| Name | Type | Req | Description |
|---|---|---|---|
| projectDir | string | yes | Absolute path to the user's project root (any framework — Next.js, Vite, Astro, Remix, SvelteKit, TanStack, …). |
No output schema declared.
No examples provided.
font_lab_verify ~438
THE SHIP RECEIPT — after font_lab_apply (and any rewires or hand edits), re-render the RUNNING site headlessly and MEASURE whether the pick actually reached the pixels: per route, the % of heading/body/label text whose computed font now matches the picked families. Files written is not the same as fonts changed — never declare a font ship done without a converged receipt. If no dev server is reachable it STARTS the project's own dev command itself (managed, bound to 127.0.0.1, stopped after) — pass ensureServer:false to forbid that. Pass the routes the human cares about (include per-route pages — brand islands live there). Returns {converged, receipt, workOrder}: `residue` names every cluster that still renders the old font, WITH provenance (route, inline-style vs stylesheet, sample text). If workOrder is non-null it is written for YOU, the coding agent — execute it: run font_lab_rewire_dead_roles when it says so, ask the human before touching intentional per-route font islands, edit the named spots, then re-run this tool until converged:true. Makes no edits itself; writes .font-lab/receipt.json.
| Name | Type | Req | Description |
|---|---|---|---|
| baseUrl | string | – | The running dev server URL, e.g. http://localhost:3000. Optional: defaults to the recorded dev server, else a managed one is started. |
| ensureServer | boolean | – | Default true: start the project's dev server (managed, 127.0.0.1, stopped after) when none is reachable. false forbids starting processes. |
| executablePath | string | – | Optional Chrome/Chromium binary path (usually unnecessary). |
| projectDir | string | yes | Absolute path to the user's project root (any framework — Next.js, Vite, Astro, Remix, SvelteKit, TanStack, …). |
| routes | array | – | Routes to measure; default ['/']. Include island routes (e.g. '/fontlab'). |
| targets | object | – | Optional explicit families to verify against (defaults to the pick in selection.json). |
No output schema declared.
No examples provided.
font_lab_wait ~340
BLOCK until the human picks a direction, asks for more options, OR clicks *done ✓* in the panel — whichever comes first. This is the unified event loop: one call covers every panel event, so you never miss one because you were waiting for the wrong kind. While blocked, the panel shows 'agent listening' and the human's clicks reach you instead of the copy-a-prompt off-ramp. Returns one of: { event: 'pick', selection } — the human picked. Call font_lab_apply to ship it. { event: 'request', request } — the human wants MORE options. request.brief has their mini-brief (feeling, departure, brand, note); request.exclude lists families already shown. Compose new directions honoring that brief, call font_lab_more_directions, then call font_lab_wait again. { event: 'done', request } — the human is finished choosing. Call font_lab_finish: scaffolding out, commit plan in. { event: 'timeout', timedOut } — no activity yet. Call font_lab_wait again to keep listening. Typical loop: compose → prepare_preview → font_lab_wait → handle the event → font_lab_wait → … → finish
| Name | Type | Req | Description |
|---|---|---|---|
| ignoreExistingPick | boolean | – | Wait for a NEW pick even if a previous selection.json exists. |
| projectDir | string | yes | Absolute path to the user's project root (any framework — Next.js, Vite, Astro, Remix, SvelteKit, TanStack, …). |
| timeoutSec | number | – | Max seconds to block (default 240). Re-call on timeout. |
No output schema declared.
No examples provided.
font_lab_wait_for_pick ~228
BLOCK until the human picks in the live panel (or timeoutSec elapses). Prefer font_lab_wait (unified) — it covers both picks AND 'more options' requests in one call. This single-event variant is still useful when you ONLY want picks (e.g. after the panel is fully stocked and no more rounds are expected). Returns { picked: true, selection } or { picked: false, timedOut: true } — on timeout, call it again to keep waiting. Alternative for harnesses with background terminals: run `npx font-lab serve --once` as a background task — it exits the moment the pick lands, with the selection as its final stdout line.
| Name | Type | Req | Description |
|---|---|---|---|
| ignoreExisting | boolean | – | Wait for a NEW pick even if a previous selection.json exists. |
| projectDir | string | yes | Absolute path to the user's project root (any framework — Next.js, Vite, Astro, Remix, SvelteKit, TanStack, …). |
| timeoutSec | number | – | Max seconds to block (default 240). Re-call on timeout. |
No output schema declared.
No examples provided.
font_lab_wait_for_request ~205
BLOCK until the human clicks 'more options / none of these' in the live panel (or timeoutSec elapses). Prefer font_lab_wait (unified) — it covers both picks AND requests in one call. This single-event variant is still useful when you specifically want to wait ONLY for a request. Returns { requested: true, request } — where request.brief is the mini-brief (feeling / departure / brand / note) and request.exclude lists families already shown. On a request: compose new directions, call font_lab_more_directions, then switch to font_lab_wait. Returns { requested: false, timedOut: true } on timeout — call again.
| Name | Type | Req | Description |
|---|---|---|---|
| projectDir | string | yes | Absolute path to the user's project root (any framework — Next.js, Vite, Astro, Remix, SvelteKit, TanStack, …). |
| timeoutSec | number | – | Max seconds to block (default 240). Re-call on timeout. |
No output schema declared.
No examples provided.
What is the Font Lab MCP server?
Font Lab is an MCP server listed in the public MCP registry as io.github.jmg698/font-lab. A decision surface for typography: curate font directions on your real site, ship next/font code. This page covers its npm package (font-lab).
Is the Font Lab MCP server safe to use?
Font Lab scores 87 out of 100 on VerifyMCP. We found no known CVEs affecting it as of 20 September 2026. It declares no install or post-install scripts. Its build provenance is signed and verified. That is a record of what we were able to check automatically, not an endorsement. The category breakdown on this page shows every signal behind the number, including the ones we could not confirm.
What tools does the Font Lab MCP server expose?
Font Lab exposes 26 tools: font_lab_start, font_lab_analyze, font_lab_list_catalog, font_lab_check_fonts, font_lab_curate, and 21 more. Their descriptions and schemas cost roughly 7,510 tokens of context every time the server is loaded.
Is the Font Lab MCP server still maintained?
Font Lab is still listed as active in the MCP registry. We last reached this channel on 20 September 2026. Those dates come from our own scans of the registry and the channel itself, not from anything the publisher announced.
What licence is the Font Lab MCP server under?
Font Lab declares the MIT licence, which is OSI-approved. That covers the source only, and says nothing about the cost of any service it calls.