@linktr.ee/arbor-mcp 0.1.2 → 0.3.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.
- package/README.md +105 -23
- package/dist/index.js +6052 -77
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -6,41 +6,100 @@ Claude Desktop, Cursor, Zed, etc.).
|
|
|
6
6
|
|
|
7
7
|
## What it does
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Registers a set of read-only Arbor design system tools. The
|
|
10
|
+
component-resolution tool is documented in detail below; the DS-as-data
|
|
11
|
+
metadata tools (component inventory, single-component lookup, version
|
|
12
|
+
history) are described under [DS-as-data tools](#ds-as-data-tools).
|
|
10
13
|
|
|
11
|
-
### `
|
|
14
|
+
### `arbor_open_in_playroom`
|
|
15
|
+
|
|
16
|
+
> The legacy name `arbor.open_in_playroom` remains registered as a deprecated
|
|
17
|
+
> alias (it routes to the same handler) and will be removed in the next major.
|
|
18
|
+
> Prefer the snake_case name.
|
|
12
19
|
|
|
13
20
|
Resolves an Arbor PascalCase component name into a Playroom share URL with
|
|
14
21
|
the canonical default snippet pre-loaded.
|
|
15
22
|
|
|
16
|
-
**Input
|
|
23
|
+
**Input** (validated by a strict Zod schema — unknown keys are rejected):
|
|
17
24
|
|
|
18
25
|
```json
|
|
19
26
|
{ "componentName": "Button" }
|
|
20
27
|
```
|
|
21
28
|
|
|
22
|
-
**Output:**
|
|
23
|
-
`
|
|
29
|
+
**Output:** `structuredContent` matching the tool's `outputSchema` (the machine
|
|
30
|
+
payload), plus a concise one-line `text` summary (the human mirror — it does
|
|
31
|
+
**not** duplicate the full JSON):
|
|
24
32
|
|
|
25
33
|
```json
|
|
26
34
|
{
|
|
27
35
|
"coverage": "mapped",
|
|
28
36
|
"arborComponentName": "Button",
|
|
29
37
|
"snippet": "<Button>Save changes</Button>",
|
|
30
|
-
"url": "https://arbor.linktr.ee/playroom
|
|
38
|
+
"url": "https://arbor.linktr.ee/playroom/?slug=flowering-sapling-glen-5",
|
|
39
|
+
"source": "minted",
|
|
40
|
+
"slug": "flowering-sapling-glen-5"
|
|
31
41
|
}
|
|
32
42
|
```
|
|
33
43
|
|
|
34
|
-
`
|
|
44
|
+
A `mapped` result carries the pretty `?slug=` URL when slug minting succeeds
|
|
45
|
+
(`source: "minted"` or `"cached"`). If minting is unavailable it degrades to a
|
|
46
|
+
self-contained lz-string `#?code=…` URL with `degraded: true` and a `reason`,
|
|
47
|
+
and no `slug`.
|
|
48
|
+
|
|
49
|
+
`coverage` (for this by-name tool) is one of:
|
|
35
50
|
|
|
36
51
|
- `mapped` — the component appears in `figma-mappings.ts` AND has a
|
|
37
52
|
canonical entry in `compositions.ts`. High confidence.
|
|
38
|
-
- `fallback` — best-effort name matching (e.g. "Header Bar" → `HeaderBar`).
|
|
39
|
-
Used by the Figma plugin's selection-driven path; the by-name MCP tool
|
|
40
|
-
almost always returns `mapped` or `not_found`.
|
|
41
53
|
- `not_found` — the URL points at the Playroom homepage; agents should
|
|
42
54
|
surface this to the human rather than silently following the link.
|
|
43
55
|
|
|
56
|
+
> A third coverage, `fallback` (best-effort fuzzy name matching), exists only
|
|
57
|
+
> on the Figma plugin's selection-driven node-ID path. This by-name MCP tool
|
|
58
|
+
> never returns it — its `outputSchema` is `mapped | not_found`.
|
|
59
|
+
|
|
60
|
+
## DS-as-data tools
|
|
61
|
+
|
|
62
|
+
Three read-only metadata tools (shipping in a forthcoming release) expose the
|
|
63
|
+
design system as queryable data. They answer "what components / versions exist"
|
|
64
|
+
— **not** token values. (For token values, read the compiled CSS in
|
|
65
|
+
`packages/design-tokens/dist/web/` or the shadcn `registry.json`, which are
|
|
66
|
+
fresher.)
|
|
67
|
+
|
|
68
|
+
- **`arbor_list_versions`** — design system version history: each entry is
|
|
69
|
+
`{ id, name, createdAt, readOnly }`.
|
|
70
|
+
- **`arbor_list_components`** — component inventory: `name`, `id`, and a short
|
|
71
|
+
`description` per component. Returns top-level components only (Figma variant
|
|
72
|
+
instances are filtered out). Accepts an optional case-insensitive `query`
|
|
73
|
+
string that filters by name or description.
|
|
74
|
+
- **`arbor_get_component`** — one component's metadata, looked up by `name`
|
|
75
|
+
(case-insensitive) or `id`. Returns `{ found, component }`.
|
|
76
|
+
|
|
77
|
+
(A fourth tool, `arbor_get_docs`, is deliberately **not** built yet — deferred
|
|
78
|
+
until a docs page-content route exists.)
|
|
79
|
+
|
|
80
|
+
### Runtime config + graceful degradation
|
|
81
|
+
|
|
82
|
+
These tools read the design system's metadata from an internal Lambda
|
|
83
|
+
federation proxy. Two env vars configure the connection — distributed
|
|
84
|
+
internally through the same channel as `FIGMA_ACCESS_TOKEN`:
|
|
85
|
+
|
|
86
|
+
| Variable | Purpose |
|
|
87
|
+
| --------------------------------- | -------------------------------------------------------- |
|
|
88
|
+
| `ARBOR_MCP_SUPERNOVA_PROXY_URL` | Full proxy URL, including the `/supernova/query` path |
|
|
89
|
+
| `ARBOR_MCP_SUPERNOVA_PROXY_TOKEN` | Shared inbound bearer token presented to the proxy |
|
|
90
|
+
|
|
91
|
+
When either var is unset — or the proxy is unreachable — the tools **degrade
|
|
92
|
+
gracefully**: they return a successful result with `source: "unavailable"` and
|
|
93
|
+
a `detail` string explaining why. They never throw and never crash the server.
|
|
94
|
+
An agent should treat `"unavailable"` as "couldn't reach the data" rather than
|
|
95
|
+
"the design system has no components/versions."
|
|
96
|
+
|
|
97
|
+
> **Metadata, not token values.** These tools surface Supernova *metadata*
|
|
98
|
+
> (component names, ids, descriptions, version history) only. They deliberately
|
|
99
|
+
> do **not** return token values — those live in the compiled CSS
|
|
100
|
+
> (`packages/design-tokens/dist/web/`) and the shadcn `registry.json`, which are
|
|
101
|
+
> the fresher source of truth.
|
|
102
|
+
|
|
44
103
|
## Install
|
|
45
104
|
|
|
46
105
|
The package is published publicly to npm as `@linktr.ee/arbor-mcp`. The
|
|
@@ -48,8 +107,13 @@ expected use is `npx`-style spawning by an MCP client — see the wiring
|
|
|
48
107
|
section below. No global install required.
|
|
49
108
|
|
|
50
109
|
```bash
|
|
51
|
-
# Smoke-check the latest release
|
|
52
|
-
|
|
110
|
+
# Smoke-check the latest release. The server speaks JSON-RPC over stdio (there
|
|
111
|
+
# is no --help flag); pipe a handshake in so stdin closes and the process
|
|
112
|
+
# exits. Expect a tools/list response naming arbor_open_in_playroom.
|
|
113
|
+
printf '%s\n%s\n' \
|
|
114
|
+
'{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"smoke","version":"0"}}}' \
|
|
115
|
+
'{"jsonrpc":"2.0","id":2,"method":"tools/list"}' \
|
|
116
|
+
| npx -y @linktr.ee/arbor-mcp
|
|
53
117
|
```
|
|
54
118
|
|
|
55
119
|
If you're working inside the Arbor monorepo, build the local source
|
|
@@ -101,7 +165,7 @@ For portability across clients that don't substitute path variables
|
|
|
101
165
|
|
|
102
166
|
### Verify the connection
|
|
103
167
|
|
|
104
|
-
After restarting your MCP client, the `
|
|
168
|
+
After restarting your MCP client, the `arbor_open_in_playroom` tool should
|
|
105
169
|
appear in the available-tools list. Ask the agent something like:
|
|
106
170
|
|
|
107
171
|
> Use the arbor MCP server to open Button in Playroom.
|
|
@@ -115,8 +179,9 @@ The agent will call the tool and return the URL.
|
|
|
115
179
|
# iterating on tool handlers.
|
|
116
180
|
yarn workspace @linktr.ee/arbor-mcp dev
|
|
117
181
|
|
|
118
|
-
# Run the
|
|
119
|
-
#
|
|
182
|
+
# Run the tests: the in-memory Client handshake + protocol contract
|
|
183
|
+
# (server.test.ts) plus tool metadata, input validation, lookup
|
|
184
|
+
# happy/not-found paths, and the figma-mappings coverage gate.
|
|
120
185
|
yarn workspace @linktr.ee/arbor-mcp test
|
|
121
186
|
|
|
122
187
|
# Smoke-test the full JSON-RPC handshake end-to-end.
|
|
@@ -129,20 +194,29 @@ printf '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion
|
|
|
129
194
|
packages/arbor-mcp/
|
|
130
195
|
├── src/
|
|
131
196
|
│ ├── index.ts # entrypoint: connects stdio transport
|
|
132
|
-
│ ├── server.ts # creates the
|
|
197
|
+
│ ├── server.ts # creates the McpServer + registers ARBOR_TOOLS
|
|
133
198
|
│ └── tools/
|
|
134
|
-
│
|
|
199
|
+
│ ├── registry.ts # ArborTool convention + registerArborTool
|
|
200
|
+
│ └── open-in-playroom.ts # tool: schemas, annotations, handler
|
|
135
201
|
├── tests/
|
|
202
|
+
│ ├── server.test.ts # in-memory Client handshake + contract
|
|
136
203
|
│ └── open-in-playroom.test.ts # node --test, validation + coverage
|
|
137
204
|
├── build.mjs # esbuild bundle to dist/index.js
|
|
138
205
|
└── tsconfig.json # rootDir: ../.. for cross-package imports
|
|
139
206
|
```
|
|
140
207
|
|
|
141
|
-
The `
|
|
208
|
+
The `arbor_open_in_playroom` handler delegates to `lookupByArborName` in
|
|
142
209
|
`apps/playroom/src/canonical-snippet-lookup.ts` — the same module the
|
|
143
210
|
Figma plugin's "Open in Playroom" relaunch button uses. This keeps the
|
|
144
211
|
agent-facing surface and the designer-facing surface in lockstep.
|
|
145
212
|
|
|
213
|
+
The server is built on the SDK's high-level `McpServer` + `registerTool`
|
|
214
|
+
(the modern, non-deprecated API). Every tool is an `ArborTool` registry
|
|
215
|
+
object (`tools/registry.ts`): a `.strict()` Zod `inputSchema`, a Zod
|
|
216
|
+
`outputSchema` (the handler returns matching `structuredContent`),
|
|
217
|
+
annotation hints, and a total handler. Input-validation and handler errors
|
|
218
|
+
surface as `isError: true` results, never protocol rejections.
|
|
219
|
+
|
|
146
220
|
## Why bundle vs. emit `tsc` files?
|
|
147
221
|
|
|
148
222
|
`tsc` would emit one `.js` per `.ts` and preserve cross-package directory
|
|
@@ -155,9 +229,17 @@ runs in the build script for type checking.
|
|
|
155
229
|
|
|
156
230
|
## Future tools
|
|
157
231
|
|
|
158
|
-
When adding a second tool, follow the
|
|
232
|
+
When adding a second tool, follow the registry convention:
|
|
233
|
+
|
|
234
|
+
1. New file under `src/tools/<name>.ts` exporting an `ArborTool` object
|
|
235
|
+
(see `tools/registry.ts`): `name`, optional deprecated `aliases`, a
|
|
236
|
+
`config` with `title` / `description` / `.strict()` Zod `inputSchema` /
|
|
237
|
+
Zod `outputSchema` / `annotations`, and a total `handler` returning
|
|
238
|
+
`{ content, structuredContent }`.
|
|
239
|
+
2. Add it to the `ARBOR_TOOLS` array in `src/server.ts`.
|
|
240
|
+
3. Mirror the test file shape under `tests/` (drive it through the in-memory
|
|
241
|
+
`Client` as in `server.test.ts`, plus pure-handler unit tests).
|
|
159
242
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
3. Mirror the test file shape under `tests/`.
|
|
243
|
+
Pass `inputSchema` as a `z.object({...}).strict()` (a ZodObject, **not** a
|
|
244
|
+
raw shape) — the SDK only preserves `.strict()` rejection of unknown keys
|
|
245
|
+
for a ZodObject; a raw shape is rebuilt non-strict and silently strips them.
|