Skip to main content
Glama
refero-design-mcp
by DarCas

@darcas/refero-design-mcp

Buy me a coffee M8ven Score

The underlying data belongs to Refero Design. This is an unofficial client; no affiliation or endorsement is implied.

Node.js TypeScript Tests

npm NPM Downloads

An MCP server for discovering, searching and extracting design systems from the public pages of styles.refero.design.

Give an assistant a real design system — palette, type scale, spacing, surfaces, do/don't guidance — instead of one it invented.

The package also ships an Agent Skill, refero-design-research, that teaches a coding agent to do evidence-based design research with this server before implementing UI — see The skill.


Why it is built this way

Two decisions shape everything else.

The JSON API is not used. styles.refero.design/robots.txt disallows /api/, and the endpoint is gated in practice: it answers 403 to every non-browser client and 200 to a real browser, which points at TLS fingerprinting rather than authentication. Spoofing headers would not fix that, and would not be the right thing to do regardless.

So this server reads only what Allow: / permits — the published sitemap and the public style pages — reachable with a plain curl and no pretending to be anything else. Requests are conditional, concurrency-capped, and sent with a User-Agent that identifies the project.

Coverage is reported, never implied. A style page is a few hundred KB and there are over a thousand of them, so the local index grows on demand. That makes "I found no matches" ambiguous — it usually means "I looked at 30 of 1,340", not "nothing exists". Every search states its own coverage, and refero_index_status exists so the model can check before concluding a style is absent.

Related MCP server: CDS Components MCP Server

Performance

The index is deliberately lazy, so almost every tool call has to answer "how much do we actually have?" before it can answer anything else. Getting that wrong is the difference between a search that returns in under a millisecond and one that stalls for seconds.

The store keeps cached summaries and known-absent ids in memory, and reads the cache directory once per session instead of probing a path per style. Both directions matter: remembering only the hits would leave the larger half of the catalogue re-read on every call.

Operation

Before

After

Speed-up

refero_search_styles — rank 1,342 summaries

15.5 ms

12.6 ms

1.2×

refero_index_status

27.9 ms

0.15 ms

186×

expand scan when everything is cached

49.0 ms

0.10 ms

490×

First call in a process

372 ms

70 ms

5.3×

speed-up, log scale

 490x  ████████████████████████████████████████   expand scan, all cached
 186x  ██████████████████████████████████         refero_index_status
 5.3x  ███████████                                first call in a process
 1.2x  █                                          rank 1,342 summaries

Two honest notes on those numbers.

The ranking barely moved, and that is deliberate. It was measured before being optimised: at ~13 ms across the whole catalogue it was never the bottleneck, so the scorer was left alone rather than tuned against a guess.

The first call in a process is dominated by one sitemap request, not by the disk. That fetch is what makes coverage honest, and it is why it happens once rather than per call.

Median of 15 runs on an Intel i9-12900F / Linux / Node 22, against a warm local cache holding 26 of 1,342 published styles. "Before" is the previous implementation, measured in the same session on the same machine. Your numbers will differ; the ratio is the point, not the milliseconds.

Tools

Tool

Purpose

refero_index_status

Published styles vs. locally indexed styles

refero_search_styles

Free-text search over names, north stars, colours, fonts, URLs

refero_match_style

Ranks styles against a prose design brief

refero_get_design_md

Renders a style as design.md, by section

refero_get_style

Parsed design system plus measured tokens, as JSON

refero_list_style_ids

Style UUIDs from the sitemap, without reading any page

Every tool declares all four MCP behavioural hints — readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true. None of them mutates the origin, the cache, or anything the caller owns; all of them can read styles.refero.design. Clients that gate on these get an accurate answer without having to infer it.

A resource, refero://style/{id}/design.md, is exposed for clients that prefer resource reads over tool calls.

Sections

refero_get_design_md takes sections, and honours it:

overview · colors · typography · type_scale · spacing · surfaces
imagery · principles · components · similar · custom

Sections with no data are dropped rather than rendered as empty headings.

Installation

npm install -g @darcas/refero-design-mcp

Installs the refero-design-mcp and refero-design-skill commands — the binary names stay unscoped, so client configs stay short.

Requires Node ≥ 22.12. Node 20 reached end-of-life on 2026-04-30, so 1.1.0 drops it; npm reports EBADENGINE on an older runtime. Everything else about installing and running is unchanged.

The skill

The package also ships an Agent Skill — skill/refero-design-research/ — that teaches a coding agent to do evidence-based design research with the Refero Design MCP before implementing substantial UI. It is a separate deliverable from the MCP itself and neither replaces the other: the MCP answers questions about styles, the skill decides how to ask them.

When the MCP is not installed, the skill can set it up for you — after asking, and only for this package. The per-client instructions it follows are in install-mcp.md.

refero-design-skill install

That is the whole command. It detects the agent clients you have, writes one shared copy under ~/.agents/skills/, and links each client to it.

refero-design-skill list                           # what is installed where
refero-design-skill install --client claude,codex  # pick clients
refero-design-skill install --project               # copy into the repository
refero-design-skill install --dry-run              # print the plan, write nothing
refero-design-skill uninstall --client claude      # remove what it installed

Flag

Effect

--client <ids>

Comma-separated ids from skill/refero-design-research/clients.json. An unknown id fails and lists the valid ones

--scope global|project

Defaults to global. Project scope copies into the repository, because a symlink into $HOME breaks on another machine and in CI

--global, --project

Shorthands for --scope global and --scope project, which are what most other installers spell this way

--all

Every client whose detect directory already exists. Bare install does this

--path <dir>

Escape hatch for an unsupported client or an unusual location

--dry-run

Print the resolved plan and touch nothing

--force

Required to overwrite local modifications, and it prints the diff first

Declare the scope once. --global --project and --scope project --global both fail: picking one silently would install to the other place while you believe you chose. A boolean flag given a value (--force=1) fails for the same reason.

Note the two scopes here decide where the skill files go. They are unrelated to the global/local question in Configuration, which decides where the MCP server entry goes in a client's config.

Global scope links rather than copies, so the skill is one file on disk with several names pointing at it; project scope copies, so it can be committed. Links are created as junction on Windows, and a skills directory that is itself a symlink is written through rather than replaced.

There is no postinstall. Nothing is written outside node_modules by installing the package — only an explicit refero-design-skill install writes anything, so upgrade the package and re-run it.

The skill is not affiliated with Refero Design, and it contains no Refero data: only instructions, plus a tool reference generated from src/.

See skill/refero-design-research/README.md for what the skill does and who it is for.

Configuration

Point a client at the package, run on demand through npx — no install step, first run downloads the package:

{
  "mcp": {
    "servers": {
      "refero-design-mcp": {
        "command": ["npx", "-y", "@darcas/refero-design-mcp@1"]
      }
    }
  }
}

-y suppresses the install prompt, which would otherwise block startup. The version is pinned to the current major so an unattended -y cannot pull a breaking release. Starts in about 2s on a cold cache, under 1s once cached.

If the package is installed globally (npm install -g @darcas/refero-design-mcp), the entry can call the binary directly instead:

"refero-design-mcp": {
  "command": ["refero-design-mcp"]
}

This form works only with the global install in place — without it the client fails to start the server (exit 127: refero-design-mcp: not found). Either form behaves identically once running.

One trap, for developing this package rather than using it. Both forms fail with exit 127: sh: 1: refero-design-mcp: not found when the client's working directory is a checkout of this repository, and work everywhere else. Inside its own checkout the package.json matches the package name, so npx resolves the request to the local project instead of the registry — and a project never links its own bins into node_modules/.bin, so npx ends up running the bare binary name, which is not on the PATH. If you hit it, name the package and the binary separately:

"refero-design-mcp": {
  "command": ["npx", "-y", "-p", "@darcas/refero-design-mcp@1", "refero-design-mcp"]
}

No other user hits this: it needs a local package.json with this exact name.

The package is scoped but the binary is not, so the command is refero-design-mcp with no @darcas/ prefix.

The default transport is stdio; see Remote (Streamable HTTP) to serve it over the network. Diagnostics go to stderr only — stdout carries JSON-RPC frames and nothing else. A server started by hand (npm start) will appear to hang: it is waiting for a client on stdin, which is correct behaviour.

Remote (Streamable HTTP)

A second binary, refero-design-mcp-http, serves the same tools over Streamable HTTP at POST /mcp, so a client that cannot spawn a process can reach it by URL.

npx -y -p @darcas/refero-design-mcp refero-design-mcp-http
# listening on http://127.0.0.1:3000/mcp

Or with Docker, which keeps the cache in a named volume:

docker compose up -d --build

Client configuration:

{
  "mcp": {
    "servers": {
      "refero": { "type": "remote", "url": "https://mcp.example.com/mcp" }
    }
  }
}
  • Stateless. Each request is self-contained, so there is no session to lose on restart. GET and DELETE on /mcp answer 405.

  • No authentication. Anyone who can reach the port can use the server, and every call can make it fetch from styles.refero.design. Keep it on a private network, or put a reverse proxy with HTTPS (and whatever access control you need) in front. The compose file publishes on loopback for that reason.

  • GET /health returns {"status":"ok","version":"…"} for probes. HEAD answers the same headers with no body.

  • Proxy timeouts. A call that expands the index can take a few seconds; do not set the proxy's read timeout below ~60 s, and disable response buffering (proxy_buffering off in nginx) so streamed responses arrive as they are sent.

Two settings are specific to this binary and are read by src/cli-http.ts, not src/config.ts: REFERO_HTTP_HOST (interface to bind, 127.0.0.1 by default, 0.0.0.0 in the image) and REFERO_HTTP_PORT (default 3000, falls back to PORT). Every variable in the next section applies here too.

Environment variables

Every setting is optional.

Variable

Default

Purpose

REFERO_CACHE_DIR

~/.cache/refero-design-mcp

On-disk cache location

REFERO_CACHE_TTL_MS

604800000

Cache freshness window (7 days)

REFERO_CONCURRENCY

4

Max simultaneous requests

REFERO_MAX_FETCHES

25

Max page fetches per tool call

REFERO_MAX_RESPONSE_CHARS

40000

Response budget

REFERO_SITE_URL

https://styles.refero.design

Origin base URL

REFERO_STYLES_SITEMAP

{site}/sitemaps/styles.xml

Style index

REFERO_TIMEOUT_MS

30000

Per-request timeout

REFERO_USER_AGENT

see src/config.ts

Request identification

REFERO_VERBOSE

false

Diagnostics to stderr

Alphabetical, so a variable is found by looking rather than by knowing which group it sits in. The defaults are the ones src/config.ts validates at startup; REFERO_MAX_FETCHES, REFERO_CONCURRENCY and REFERO_TIMEOUT_MS are the three worth touching first when you want faster research rather than more of it.

Example session

// The model calls these in sequence
{
  "name": "refero_index_status",
  "arguments": {}
}
// → "Published styles: 1342 / Indexed locally: 1 / Coverage: <1% (1 styles)"

{
  "name": "refero_match_style",
  "arguments": {
    "brief": "dark, dense dashboard for engineers"
  }
}
// → ranked styles, each with matched terms and the north star that drove the match

{
  "name": "refero_get_design_md",
  "arguments": {
    "style_id": "a73148b9-449b-42cd-9f38-86ef694f500e",
    "sections": ["overview", "colors", "typography"]
  }
}
// → # Apple, with the palette table and type scale

How styles are indexed

sitemaps/styles.xml  →  list of ids + lastmod        (small, fetched once)
        ↓
/style/{id}          →  Next.js RSC payload in HTML  (~300 KB, fetched on demand)
        ↓
result.meta / result.raw / result.designSystem         (parsed, validated, cached)

The style record is located by styleId. A page embeds a dozen or more related styles that share the same key names, so anchoring on the id is what prevents returning a neighbour's palette.

The RSC format is internal to Next.js and may change. Every failure surfaces as a typed error rather than a partially-filled object that looks like a valid answer.

Development

npm install
npm run dev        # watch mode
npm run verify     # typecheck + lint + test + build

Script

Action

npm run build

Compile to dist/

npm start

Run the compiled server

npm run typecheck

tsc --noEmit

npm run lint

ESLint, type-aware rules

npm test

Vitest

npm run verify

All of the above, in order

npm run clean

Remove dist/

npm run deploy

Test, build, then publish to npm

Testing

125 tests across 9 files.

Unit tests run offline, against a hand-assembled sample of a real RSC payload — the fragile parts (id anchoring, brace matching, lazy reference detection) are exactly the parts worth pinning.

test/e2e.test.ts drives the server against the live site and skips itself when the origin is unreachable. The reachability probe runs at module load rather than in a hook, because describe.skipIf is evaluated during collection: a probe in beforeAll leaves every test silently skipped, which is indistinguishable from a passing suite.

Design notes

  • Schemas derived from real payloads, not from documentation. spacing.radius is a per-element map ({cards: "28px", buttons: "9999px"}), not a string. similar is [{business, why}] on current styles but bare strings on older ones. surfaces is {hex, name, level, purpose}. A typeScale size arrives as "17" on one style and 17 on the next, and is normalised to a string at the boundary. Every schema is permissive about type and strict about presence, so a partial document degrades instead of throwing away a whole design system.

  • Measured and curated data are both kept. result.raw holds tokens with usage frequency and context; result.designSystem holds the curated reading. Frequency is what separates a signature colour from an incidental one.

  • Truncation respects structure. Cutting markdown with slice(0, n) slices code fences in half, which makes the model read broken CSS as if it were the design. This server trims on a structural boundary instead, and closes any fence it opens, so a shortened document is still valid. Every path that caps a response goes through that helper, including refero_get_design_md.

  • A malformed style_id never costs a request. Ids are checked against the UUID shape before anything reaches the network or the cache directory, so a wrong id returns an error naming the tool that lists valid ones instead of a bare 404.

  • Conditional requests everywhere, so a repeat call usually costs a 304.

License

This project is licensed under the MIT License. See the LICENSE file for details.

Available Tools

6 tools
refero_get_design_mdGet design.md for a styleA
Read-onlyIdempotent

Render a style as a design.md document. The sections argument is honoured: request only the sections you need to stay within budget. Without it you get the whole document, truncated on a structural boundary.

Inputs: style_id (required), refresh, sections.

refero_get_styleGet a style as structured dataA
Read-onlyIdempotent

Return the parsed design system plus the measured tokens scraped from the live site (colours, fonts, radii, spacing) as JSON. Prefer refero_get_design_md when you want prose to feed into a document.

Inputs: style_id (required), refresh, include_measured_tokens.

refero_index_statusRefero index coverageA
Read-onlyIdempotent

Report how many published styles exist and how many are indexed locally. Call this before concluding that a style does not exist: a miss usually means it has not been indexed yet.

Inputs: no named parameters.

refero_list_style_idsList style ids from the sitemapA
Read-onlyIdempotent

Return published style UUIDs and their last-modified timestamps, straight from the public sitemap. Use this to discover ids without reading any style page.

Inputs: limit, offset, updated_since.

refero_match_styleMatch a brief to Refero stylesA
Read-onlyIdempotent

Given a design brief in prose, return the styles that best match it, each with the terms that triggered the match and a short rationale. Use this when the request is qualitative ("I need a dense, data-heavy dashboard") rather than a specific name.

Inputs: brief (required), limit, expand.

refero_search_stylesSearch Refero stylesA
Read-onlyIdempotent

Find design styles on styles.refero.design by free text. Matches style names, north-star statements, colours, fonts and URLs on word boundaries. The local index grows on demand, so results improve as more styles are indexed.

Inputs: query (required), limit, expand.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A4.1/5.0

    Scored across 6 tools

    Disambiguation4/5

    Search vs match overlap somewhat (both find styles), but descriptions clearly separate keyword matching from qualitative brief matching. The two retrieval tools (get_design_md for prose, get_style for JSON tokens) are explicitly differentiated, and index_status/list_style_ids target distinct metadata concerns.

    Naming Consistency5/5

    Every tool follows the identical refero_<verb>_<noun> pattern: refero_search_styles, refero_match_style, refero_get_style, refero_list_style_ids, etc. No camelCase or naming deviations; totally predictable.

    Tool Count5/5

    Six tools is well-scoped for a design-style discovery and retrieval server, with each tool earning its place across discovery, matching, and rendering. No redundancy or bloat.

    Completeness4/5

    Covers the full read lifecycle: discover ids, search, brief-match, fetch JSON tokens, and render prose. Being a read-only scraper of a public site, no CRUD is needed, though minor gaps (e.g. pagination/batch retrieval) exist.

    Maintenance

    ActivityMaintained
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers