npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@linktr.ee/arbor-mcp

v0.5.0

Published

Model Context Protocol server exposing Arbor design system tools: Playroom snippets, Figma→code sync health, UX-writing checks, decision-log lookup, and federated component/version metadata.

Downloads

1,829

Readme

@linktr.ee/arbor-mcp

A Model Context Protocol server that exposes Arbor design system tools to MCP-connected AI agents (Claude Code, Claude Desktop, Cursor, Zed, etc.).

What it does

Registers a set of read-only Arbor design system tools. The component-resolution tool is documented in detail below; the DS-as-data metadata tools (component inventory, single-component lookup, version history) are described under DS-as-data tools.

arbor_open_in_playroom

The legacy name arbor.open_in_playroom remains registered as a deprecated alias (it routes to the same handler) and will be removed in the next major. Prefer the snake_case name.

Resolves an Arbor PascalCase component name into a Playroom share URL with the canonical default snippet pre-loaded.

Input (validated by a strict Zod schema — unknown keys are rejected):

{ "componentName": "Button" }

Output: structuredContent matching the tool's outputSchema (the machine payload), plus a concise one-line text summary (the human mirror — it does not duplicate the full JSON):

{
  "coverage": "mapped",
  "arborComponentName": "Button",
  "snippet": "<Button>Save changes</Button>",
  "url": "https://arbor.linktr.ee/playroom/?slug=flowering-sapling-glen-5",
  "source": "minted",
  "slug": "flowering-sapling-glen-5"
}

A mapped result carries the pretty ?slug= URL when slug minting succeeds (source: "minted" or "cached"). If minting is unavailable it degrades to a self-contained lz-string #?code=… URL with degraded: true and a reason, and no slug.

coverage (for this by-name tool) is one of:

  • mapped — the component appears in figma-mappings.ts AND has a canonical entry in compositions.ts. High confidence.
  • not_found — the URL points at the Playroom homepage; agents should surface this to the human rather than silently following the link.

A third coverage, fallback (best-effort fuzzy name matching), exists only on the Figma plugin's selection-driven node-ID path. This by-name MCP tool never returns it — its outputSchema is mapped | not_found.

DS-as-data tools

Three read-only metadata tools (shipping in a forthcoming release) expose the design system as queryable data. They answer "what components / versions exist" — not token values. (For token values, read the compiled CSS in packages/design-tokens/dist/web/ or the shadcn registry.json, which are fresher.)

  • arbor_list_versions — design system version history: each entry is { id, name, createdAt, readOnly }.
  • arbor_list_components — component inventory: name, id, and a short description per component. Returns top-level components only (Figma variant instances are filtered out). Accepts an optional case-insensitive query string that filters by name or description.
  • arbor_get_component — one component's metadata, looked up by name (case-insensitive) or id. Returns { found, component }.

(A fourth tool, arbor_get_docs, is deliberately not built yet — deferred until a docs page-content route exists.)

Runtime config + graceful degradation

These tools read the design system's metadata from an internal Lambda federation proxy. The proxy URL is built in (override with ARBOR_MCP_SUPERNOVA_PROXY_URL). While the route is access-gated, also set ARBOR_MCP_SUPERNOVA_PROXY_TOKEN — one shared internal value provisioned by the Arbor team (not a per-engineer token, and never bundled in this package). If the route is open, the tools work with no configuration at all:

| Variable | Purpose | | --------------------------------- | -------------------------------------------------------- | | ARBOR_MCP_SUPERNOVA_PROXY_URL | Full proxy URL, including the /supernova/query path | | ARBOR_MCP_SUPERNOVA_PROXY_TOKEN | Shared inbound bearer token presented to the proxy |

When either var is unset — or the proxy is unreachable — the tools degrade gracefully: they return a successful result with source: "unavailable" and a detail string explaining why. They never throw and never crash the server. An agent should treat "unavailable" as "couldn't reach the data" rather than "the design system has no components/versions."

Metadata, not token values. These tools surface Supernova metadata (component names, ids, descriptions, version history) only. They deliberately do not return token values — those live in the compiled CSS (packages/design-tokens/dist/web/) and the shadcn registry.json, which are the fresher source of truth.

Install

The package is published publicly to npm as @linktr.ee/arbor-mcp. The expected use is npx-style spawning by an MCP client — see the wiring section below. No global install required.

# Smoke-check the latest release. The server speaks JSON-RPC over stdio (there
# is no --help flag); pipe a handshake in so stdin closes and the process
# exits. Expect a tools/list response naming arbor_open_in_playroom.
printf '%s\n%s\n' \
  '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"smoke","version":"0"}}}' \
  '{"jsonrpc":"2.0","id":2,"method":"tools/list"}' \
  | npx -y @linktr.ee/arbor-mcp

If you're working inside the Arbor monorepo, build the local source instead so you're testing your changes:

yarn install
yarn workspace @linktr.ee/arbor-mcp build

That produces a single bundled ESM file at packages/arbor-mcp/dist/index.js (with shebang) ready to be spawned by any MCP client.

Wire it into an MCP client

Claude Code / Claude Desktop / Cursor (.mcp.json or equivalent)

The recommended setup uses the public npm package — no clone required:

{
  "mcpServers": {
    "arbor": {
      "command": "npx",
      "args": ["-y", "@linktr.ee/arbor-mcp"]
    }
  }
}

If you're hacking on the server inside the Arbor workspace, point at the local build instead so changes pick up without republishing:

{
  "mcpServers": {
    "arbor": {
      "command": "node",
      "args": ["./packages/arbor-mcp/dist/index.js"]
    }
  }
}

For portability across clients that don't substitute path variables (Claude Desktop, most CLI MCP clients), prefer absolute paths over ${workspaceFolder}.

Verify the connection

After restarting your MCP client, the arbor_open_in_playroom tool should appear in the available-tools list. Ask the agent something like:

Use the arbor MCP server to open Button in Playroom.

The agent will call the tool and return the URL.

Develop

# Run the server against tsx directly (no build step) — useful for
# iterating on tool handlers.
yarn workspace @linktr.ee/arbor-mcp dev

# Run the tests: the in-memory Client handshake + protocol contract
# (server.test.ts) plus tool metadata, input validation, lookup
# happy/not-found paths, and the figma-mappings coverage gate.
yarn workspace @linktr.ee/arbor-mcp test

# Smoke-test the full JSON-RPC handshake end-to-end.
printf '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"manual","version":"0.0.0"}}}\n{"jsonrpc":"2.0","id":2,"method":"tools/list"}\n' | node dist/index.js

Architecture

packages/arbor-mcp/
├── src/
│   ├── index.ts              # entrypoint: connects stdio transport
│   ├── server.ts             # creates the McpServer + registers ARBOR_TOOLS
│   └── tools/
│       ├── registry.ts          # ArborTool convention + registerArborTool
│       └── open-in-playroom.ts  # tool: schemas, annotations, handler
├── tests/
│   ├── server.test.ts           # in-memory Client handshake + contract
│   └── open-in-playroom.test.ts # node --test, validation + coverage
├── build.mjs                 # esbuild bundle to dist/index.js
└── tsconfig.json             # rootDir: ../.. for cross-package imports

The arbor_open_in_playroom handler delegates to lookupByArborName in apps/playroom/src/canonical-snippet-lookup.ts — the same module the Figma plugin's "Open in Playroom" relaunch button uses. This keeps the agent-facing surface and the designer-facing surface in lockstep.

The server is built on the SDK's high-level McpServer + registerTool (the modern, non-deprecated API). Every tool is an ArborTool registry object (tools/registry.ts): a .strict() Zod inputSchema, a Zod outputSchema (the handler returns matching structuredContent), annotation hints, and a total handler. Input-validation and handler errors surface as isError: true results, never protocol rejections.

Why bundle vs. emit tsc files?

tsc would emit one .js per .ts and preserve cross-package directory structure under dist/. Node's ESM resolver doesn't add .js extensions to extensionless imports (the workspace TS source uses extensionless imports because moduleResolution: bundler), so the multi-file output fails to resolve at runtime. Bundling with esbuild flattens the dependency graph into a single self-contained file. tsc --noEmit still runs in the build script for type checking.

Future tools

When adding a second tool, follow the registry convention:

  1. New file under src/tools/<name>.ts exporting an ArborTool object (see tools/registry.ts): name, optional deprecated aliases, a config with title / description / .strict() Zod inputSchema / Zod outputSchema / annotations, and a total handler returning { content, structuredContent }.
  2. Add it to the ARBOR_TOOLS array in src/server.ts.
  3. Mirror the test file shape under tests/ (drive it through the in-memory Client as in server.test.ts, plus pure-handler unit tests).

Pass inputSchema as a z.object({...}).strict() (a ZodObject, not a raw shape) — the SDK only preserves .strict() rejection of unknown keys for a ZodObject; a raw shape is rebuilt non-strict and silently strips them.