@linktr.ee/arbor-mcp 0.2.0 → 0.4.0-rc.ef036d0d6.7141

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.
Files changed (3) hide show
  1. package/README.md +50 -1
  2. package/dist/index.js +265 -29
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -6,7 +6,10 @@ Claude Desktop, Cursor, Zed, etc.).
6
6
 
7
7
  ## What it does
8
8
 
9
- Currently registers a single tool:
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`
12
15
 
@@ -54,6 +57,52 @@ and no `slug`.
54
57
  > on the Figma plugin's selection-driven node-ID path. This by-name MCP tool
55
58
  > never returns it — its `outputSchema` is `mapped | not_found`.
56
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. The proxy **URL is built in** (override with
84
+ `ARBOR_MCP_SUPERNOVA_PROXY_URL`). While the route is access-gated, also set
85
+ `ARBOR_MCP_SUPERNOVA_PROXY_TOKEN` — one shared internal value provisioned by
86
+ the Arbor team (not a per-engineer token, and never bundled in this package).
87
+ If the route is open, the tools work with no configuration at all:
88
+
89
+ | Variable | Purpose |
90
+ | --------------------------------- | -------------------------------------------------------- |
91
+ | `ARBOR_MCP_SUPERNOVA_PROXY_URL` | Full proxy URL, including the `/supernova/query` path |
92
+ | `ARBOR_MCP_SUPERNOVA_PROXY_TOKEN` | Shared inbound bearer token presented to the proxy |
93
+
94
+ When either var is unset — or the proxy is unreachable — the tools **degrade
95
+ gracefully**: they return a successful result with `source: "unavailable"` and
96
+ a `detail` string explaining why. They never throw and never crash the server.
97
+ An agent should treat `"unavailable"` as "couldn't reach the data" rather than
98
+ "the design system has no components/versions."
99
+
100
+ > **Metadata, not token values.** These tools surface Supernova *metadata*
101
+ > (component names, ids, descriptions, version history) only. They deliberately
102
+ > do **not** return token values — those live in the compiled CSS
103
+ > (`packages/design-tokens/dist/web/`) and the shadcn `registry.json`, which are
104
+ > the fresher source of truth.
105
+
57
106
  ## Install
58
107
 
59
108
  The package is published publicly to npm as `@linktr.ee/arbor-mcp`. The
package/dist/index.js CHANGED
@@ -1582,8 +1582,8 @@ var require_lz_string = __commonJS({
1582
1582
  var require_hash_code = __commonJS({
1583
1583
  "../../apps/playroom/src/hash-code.js"(exports, module) {
1584
1584
  "use strict";
1585
- function hashCode2(str2) {
1586
- var s = String(str2 == null ? "" : str2);
1585
+ function hashCode2(str3) {
1586
+ var s = String(str3 == null ? "" : str3);
1587
1587
  var h = 2166136261;
1588
1588
  for (var i = 0; i < s.length; i++) {
1589
1589
  h ^= s.charCodeAt(i);
@@ -5913,14 +5913,247 @@ var checkUxWritingTool = {
5913
5913
  handler: handleCheckUxWriting
5914
5914
  };
5915
5915
 
5916
+ // src/lib/supernova-client.ts
5917
+ var CACHE_TTL_MS2 = 5 * 60 * 1e3;
5918
+ var DEFAULT_TIMEOUT_MS2 = 9e3;
5919
+ var DEFAULT_MAX_CHARS2 = 4e6;
5920
+ var DEFAULT_PROXY_URL = "https://on7uu5jns5nmsusrk6gg3suu640clium.lambda-url.us-west-2.on.aws/supernova/query";
5921
+ var moduleCache2 = /* @__PURE__ */ new Map();
5922
+ function statusReason(status) {
5923
+ if (status === 401) return "unauthorized";
5924
+ if (status === 400) return "bad_request";
5925
+ if (status === 502) return "upstream_error";
5926
+ if (status === 503) return "upstream_unconfigured";
5927
+ return "http_error";
5928
+ }
5929
+ async function querySupernova(resource, opts = {}) {
5930
+ const {
5931
+ designSystemId,
5932
+ versionId,
5933
+ proxyUrl = process.env.ARBOR_MCP_SUPERNOVA_PROXY_URL ?? "",
5934
+ proxyToken = process.env.ARBOR_MCP_SUPERNOVA_PROXY_TOKEN ?? "",
5935
+ fetchImpl = globalThis.fetch,
5936
+ now = Date.now,
5937
+ cache = moduleCache2,
5938
+ timeoutMs = DEFAULT_TIMEOUT_MS2,
5939
+ maxChars = DEFAULT_MAX_CHARS2
5940
+ } = opts;
5941
+ const url = proxyUrl || DEFAULT_PROXY_URL;
5942
+ let parsed;
5943
+ try {
5944
+ parsed = new URL(url);
5945
+ } catch {
5946
+ return { ok: false, reason: "not_configured", detail: `proxy URL is not valid: ${url}` };
5947
+ }
5948
+ if (parsed.protocol !== "https:") {
5949
+ return { ok: false, reason: "not_configured", detail: `proxy URL must be https (got ${parsed.protocol})` };
5950
+ }
5951
+ const cacheKey = `${resource}|${designSystemId ?? ""}|${versionId ?? ""}`;
5952
+ const cached = cache.get(cacheKey);
5953
+ if (cached && now() - cached.fetchedAt < CACHE_TTL_MS2) return cached.value;
5954
+ const body = { resource };
5955
+ if (designSystemId) body.designSystemId = designSystemId;
5956
+ if (versionId) body.versionId = versionId;
5957
+ const controller = new AbortController();
5958
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
5959
+ let res;
5960
+ let text;
5961
+ try {
5962
+ res = await fetchImpl(url, {
5963
+ method: "POST",
5964
+ headers: { "Content-Type": "application/json", ...proxyToken ? { Authorization: `Bearer ${proxyToken}` } : {} },
5965
+ body: JSON.stringify(body),
5966
+ signal: controller.signal,
5967
+ redirect: "error"
5968
+ });
5969
+ text = await res.text();
5970
+ } catch (err) {
5971
+ const e = err;
5972
+ return { ok: false, reason: e && e.name === "AbortError" ? "timeout" : "network", detail: e?.message ?? String(err) };
5973
+ } finally {
5974
+ clearTimeout(timer);
5975
+ }
5976
+ if (!res.ok) {
5977
+ return { ok: false, reason: statusReason(res.status), detail: `HTTP ${res.status} from proxy`, status: res.status };
5978
+ }
5979
+ if (text.length > maxChars) {
5980
+ return { ok: false, reason: "too_large", detail: `${text.length} chars > ${maxChars} cap` };
5981
+ }
5982
+ let envelope;
5983
+ try {
5984
+ envelope = JSON.parse(text);
5985
+ } catch (err) {
5986
+ return { ok: false, reason: "parse_error", detail: err?.message ?? String(err) };
5987
+ }
5988
+ if (envelope.data == null) {
5989
+ return { ok: false, reason: "parse_error", detail: "proxy returned 200 with no data payload" };
5990
+ }
5991
+ const value = {
5992
+ ok: true,
5993
+ data: envelope.data,
5994
+ versionId: typeof envelope.versionId === "string" ? envelope.versionId : null,
5995
+ fetchedAt: typeof envelope.fetchedAt === "string" ? envelope.fetchedAt : null
5996
+ };
5997
+ cache.set(cacheKey, { value, fetchedAt: now() });
5998
+ return value;
5999
+ }
6000
+
6001
+ // src/tools/list-components.ts
6002
+ var TOOL_NAME3 = "arbor_list_components";
6003
+ var TOOL_TITLE3 = "List Arbor components";
6004
+ var TOOL_DESCRIPTION3 = "List the components in the Arbor design system (name, id, short description) from Supernova via the Arbor federation proxy. Read-only metadata for discovery; returns top-level components, not Figma variants. Does NOT return token values, source code, or props \u2014 use arbor_get_component for one component, the shadcn registry/Storybook for source, arbor_open_in_playroom for a runnable snippet. Does NOT change anything.";
6005
+ var inputSchema3 = external_exports.object({ query: external_exports.string().trim().min(1).max(100).optional().describe("Optional case-insensitive name/description filter") }).strict();
6006
+ var outputSchema3 = external_exports.object({
6007
+ source: external_exports.enum(["remote", "unavailable"]),
6008
+ total: external_exports.number(),
6009
+ components: external_exports.array(external_exports.object({ id: external_exports.string(), name: external_exports.string(), description: external_exports.string().nullable() })),
6010
+ detail: external_exports.string().nullable()
6011
+ });
6012
+ var annotations3 = { readOnlyHint: true, idempotentHint: true, openWorldHint: true, destructiveHint: false };
6013
+ function extractComponents(data) {
6014
+ const list = data?.result?.components;
6015
+ if (!Array.isArray(list)) return null;
6016
+ return list.filter((c) => {
6017
+ if (!c || typeof c !== "object") return false;
6018
+ const parent = c.parentComponentPersistentId;
6019
+ return !(typeof parent === "string" && parent.length > 0);
6020
+ }).map((c) => {
6021
+ const meta = c.meta && typeof c.meta === "object" ? c.meta : {};
6022
+ const name = typeof meta.name === "string" && meta.name.length > 0 ? meta.name : "(unnamed)";
6023
+ const desc = typeof meta.description === "string" ? meta.description.trim() : "";
6024
+ return { id: String(c.id ?? ""), name, description: desc.length > 0 ? desc : null };
6025
+ });
6026
+ }
6027
+ async function handleListComponents(args, deps = {}) {
6028
+ const query = deps.query ?? ((r) => querySupernova(r));
6029
+ const result = await query("components");
6030
+ let out;
6031
+ const extracted = result.ok ? extractComponents(result.data) : null;
6032
+ if (!result.ok) {
6033
+ out = { source: "unavailable", total: 0, components: [], detail: `could not load components (${result.reason})` };
6034
+ } else if (extracted === null) {
6035
+ out = { source: "unavailable", total: 0, components: [], detail: "proxy returned an unrecognized components payload" };
6036
+ } else {
6037
+ let components = extracted;
6038
+ const q = args.query?.toLowerCase();
6039
+ if (q) {
6040
+ components = components.filter(
6041
+ (c) => c.name.toLowerCase().includes(q) || (c.description ? c.description.toLowerCase().includes(q) : false)
6042
+ );
6043
+ }
6044
+ out = { source: "remote", total: components.length, components, detail: null };
6045
+ }
6046
+ const summary = out.source === "unavailable" ? `Could not load components (${out.detail}). Treat as unavailable \u2014 do not assume there are none.` : `${out.total} component${out.total === 1 ? "" : "s"}${args.query ? ` matching "${args.query}"` : ""}.`;
6047
+ return { content: [{ type: "text", text: summary }], structuredContent: out };
6048
+ }
6049
+ var listComponentsTool = {
6050
+ name: TOOL_NAME3,
6051
+ config: { title: TOOL_TITLE3, description: TOOL_DESCRIPTION3, inputSchema: inputSchema3, outputSchema: outputSchema3, annotations: annotations3 },
6052
+ handler: handleListComponents
6053
+ };
6054
+
6055
+ // src/tools/get-component.ts
6056
+ var TOOL_NAME4 = "arbor_get_component";
6057
+ var TOOL_TITLE4 = "Get one Arbor component";
6058
+ var TOOL_DESCRIPTION4 = "Get a single Arbor component's metadata by name (case-insensitive) or id from Supernova via the Arbor federation proxy (name, id, description). Read-only; matches top-level components, not Figma variants. Does NOT return token values or rendered source \u2014 use the shadcn registry / Storybook for implementation and arbor_open_in_playroom for a runnable snippet. Does NOT change anything.";
6059
+ var inputSchema4 = external_exports.object({
6060
+ name: external_exports.string().trim().min(1).max(100).optional().describe('Component name (case-insensitive), e.g. "Button"'),
6061
+ id: external_exports.string().trim().min(1).max(100).optional().describe("Supernova component id")
6062
+ }).strict();
6063
+ var outputSchema4 = external_exports.object({
6064
+ source: external_exports.enum(["remote", "unavailable"]),
6065
+ found: external_exports.boolean(),
6066
+ component: external_exports.object({ id: external_exports.string(), name: external_exports.string(), description: external_exports.string().nullable() }).nullable(),
6067
+ detail: external_exports.string().nullable()
6068
+ });
6069
+ var annotations4 = { readOnlyHint: true, idempotentHint: true, openWorldHint: true, destructiveHint: false };
6070
+ async function handleGetComponent(args, deps = {}) {
6071
+ const name = args.name?.trim();
6072
+ const id = args.id?.trim();
6073
+ if (!name && !id) {
6074
+ const out2 = { source: "remote", found: false, component: null, detail: "provide name or id" };
6075
+ return {
6076
+ content: [{ type: "text", text: 'Provide a component name or id \u2014 e.g. {"name":"Button"} or {"id":"35988997"}.' }],
6077
+ structuredContent: out2
6078
+ };
6079
+ }
6080
+ const query = deps.query ?? ((r) => querySupernova(r));
6081
+ const result = await query("components");
6082
+ let out;
6083
+ if (!result.ok) {
6084
+ out = { source: "unavailable", found: false, component: null, detail: `could not load components (${result.reason})` };
6085
+ } else {
6086
+ const components = extractComponents(result.data);
6087
+ if (components === null) {
6088
+ out = { source: "unavailable", found: false, component: null, detail: "proxy returned an unrecognized components payload" };
6089
+ } else {
6090
+ const match = id ? components.find((c) => c.id === id) : components.find((c) => c.name.toLowerCase() === name.toLowerCase());
6091
+ out = match ? { source: "remote", found: true, component: match, detail: null } : { source: "remote", found: false, component: null, detail: `no component ${id ? `with id ${id}` : `named "${name}"`}` };
6092
+ }
6093
+ }
6094
+ const summary = out.source === "unavailable" ? `Could not load components (${out.detail}). Treat as unavailable.` : out.found ? `${out.component.name}${out.component.description ? ` \u2014 ${out.component.description}` : " (no description)"}.` : `No Arbor component ${args.id ? `with id ${args.id.trim()}` : `named "${args.name?.trim()}"`} exists.`;
6095
+ return { content: [{ type: "text", text: summary }], structuredContent: out };
6096
+ }
6097
+ var getComponentTool = {
6098
+ name: TOOL_NAME4,
6099
+ config: { title: TOOL_TITLE4, description: TOOL_DESCRIPTION4, inputSchema: inputSchema4, outputSchema: outputSchema4, annotations: annotations4 },
6100
+ handler: handleGetComponent
6101
+ };
6102
+
6103
+ // src/tools/list-versions.ts
6104
+ var TOOL_NAME5 = "arbor_list_versions";
6105
+ var TOOL_TITLE5 = "List Arbor design system versions";
6106
+ var TOOL_DESCRIPTION5 = "List the version history of the Arbor design system (id, name, created date, read-only flag) from Supernova via the Arbor federation proxy. Read-only metadata. Does NOT return token values (use the compiled CSS / registry) and does NOT change anything.";
6107
+ var inputSchema5 = external_exports.object({}).strict();
6108
+ var outputSchema5 = external_exports.object({
6109
+ source: external_exports.enum(["remote", "unavailable"]).describe("'remote' = fetched; 'unavailable' = proxy fetch failed"),
6110
+ total: external_exports.number().describe("Number of versions returned"),
6111
+ versions: external_exports.array(external_exports.object({ id: external_exports.string(), name: external_exports.string(), createdAt: external_exports.string().nullable(), readOnly: external_exports.boolean().nullable() })).describe("Versions, in the order Supernova returned them"),
6112
+ detail: external_exports.string().nullable().describe("Degraded reason when source is unavailable")
6113
+ });
6114
+ var annotations5 = { readOnlyHint: true, idempotentHint: true, openWorldHint: true, destructiveHint: false };
6115
+ function extractList(data) {
6116
+ const wrapped = data?.result?.designSystemVersions;
6117
+ const list = Array.isArray(wrapped) ? wrapped : Array.isArray(data) ? data : null;
6118
+ if (list === null) return null;
6119
+ return list.filter((x) => !!x && typeof x === "object");
6120
+ }
6121
+ var str2 = (v) => typeof v === "string" && v.length > 0 ? v : null;
6122
+ async function handleListVersions(_args, deps = {}) {
6123
+ const query = deps.query ?? ((r) => querySupernova(r));
6124
+ const result = await query("versions");
6125
+ let out;
6126
+ const list = result.ok ? extractList(result.data) : null;
6127
+ if (!result.ok) {
6128
+ out = { source: "unavailable", total: 0, versions: [], detail: `could not load versions (${result.reason})` };
6129
+ } else if (list === null) {
6130
+ out = { source: "unavailable", total: 0, versions: [], detail: "proxy returned an unrecognized versions payload" };
6131
+ } else {
6132
+ const versions = list.map((v) => ({
6133
+ id: String(v.id ?? ""),
6134
+ name: str2(v.version) ?? str2(v.name) ?? "(unnamed)",
6135
+ createdAt: str2(v.createdAt),
6136
+ readOnly: typeof v.isReadonly === "boolean" ? v.isReadonly : null
6137
+ }));
6138
+ out = { source: "remote", total: versions.length, versions, detail: null };
6139
+ }
6140
+ const summary = out.source === "unavailable" ? `Could not load design system versions (${out.detail}). Treat as unavailable \u2014 do not assume there are none.` : `${out.total} design system version${out.total === 1 ? "" : "s"}: ${out.versions.map((v) => v.name).join(", ") || "(none)"}.`;
6141
+ return { content: [{ type: "text", text: summary }], structuredContent: out };
6142
+ }
6143
+ var listVersionsTool = {
6144
+ name: TOOL_NAME5,
6145
+ config: { title: TOOL_TITLE5, description: TOOL_DESCRIPTION5, inputSchema: inputSchema5, outputSchema: outputSchema5, annotations: annotations5 },
6146
+ handler: handleListVersions
6147
+ };
6148
+
5916
6149
  // src/tools/lookup-decision.ts
5917
6150
  var ARTIFACT2 = "decision-log.json";
5918
6151
  var CHARACTER_LIMIT = 25e3;
5919
6152
  var DEFAULT_LIMIT = 5;
5920
- var TOOL_NAME3 = "arbor_lookup_decision";
5921
- var TOOL_TITLE3 = "Look up an Arbor decision-log entry";
5922
- var TOOL_DESCRIPTION3 = 'Look up Arbor decision-log entries \u2014 the record of WHY design-system decisions were made \u2014 by id (e.g. "DL-067"), tag, or keyword. Keyword search spans the title, tags, and every section (context, decision, outcome, rationale, lessons, references, \u2026). Results are newest-first and paginated. Use this for "why did Arbor decide X?" / "what resolved <problem>?". Does NOT return component metadata or token values (those are separate); it is the decision/rationale record only.';
5923
- var inputSchema3 = external_exports.object({
6153
+ var TOOL_NAME6 = "arbor_lookup_decision";
6154
+ var TOOL_TITLE6 = "Look up an Arbor decision-log entry";
6155
+ var TOOL_DESCRIPTION6 = 'Look up Arbor decision-log entries \u2014 the record of WHY design-system decisions were made \u2014 by id (e.g. "DL-067"), tag, or keyword. Keyword search spans the title, tags, and every section (context, decision, outcome, rationale, lessons, references, \u2026). Results are newest-first and paginated. Use this for "why did Arbor decide X?" / "what resolved <problem>?". Does NOT return component metadata or token values (those are separate); it is the decision/rationale record only.';
6156
+ var inputSchema6 = external_exports.object({
5924
6157
  id: external_exports.string().optional().describe('A specific decision id, e.g. "DL-067" (a bare "67" also resolves)'),
5925
6158
  tag: external_exports.string().optional().describe('Filter to entries carrying this tag (case-insensitive), e.g. "tokens"'),
5926
6159
  query: external_exports.string().optional().describe("Keyword search across title, tags, and all sections (case-insensitive)"),
@@ -5938,7 +6171,7 @@ var decisionEntrySchema = external_exports.object({
5938
6171
  tags: external_exports.array(external_exports.string()),
5939
6172
  sections: external_exports.array(sectionSchema).describe("Ordered sections (Context, Decision, \u2026, plus any non-standard headings)")
5940
6173
  });
5941
- var outputSchema3 = external_exports.object({
6174
+ var outputSchema6 = external_exports.object({
5942
6175
  total: external_exports.number().describe("Total entries matching the filter (before pagination)"),
5943
6176
  count: external_exports.number().describe("Entries actually returned (after pagination + character budget)"),
5944
6177
  offset: external_exports.number(),
@@ -5949,7 +6182,7 @@ var outputSchema3 = external_exports.object({
5949
6182
  stale: external_exports.boolean().describe("True when the snapshot is older than the freshness threshold (or unavailable)"),
5950
6183
  entries: external_exports.array(decisionEntrySchema)
5951
6184
  });
5952
- var annotations3 = {
6185
+ var annotations6 = {
5953
6186
  readOnlyHint: true,
5954
6187
  idempotentHint: true,
5955
6188
  openWorldHint: true,
@@ -6065,13 +6298,13 @@ ${included.map(renderEntry).join("\n\n---\n\n")}${note}`;
6065
6298
  };
6066
6299
  }
6067
6300
  var lookupDecisionTool = {
6068
- name: TOOL_NAME3,
6301
+ name: TOOL_NAME6,
6069
6302
  config: {
6070
- title: TOOL_TITLE3,
6071
- description: TOOL_DESCRIPTION3,
6072
- inputSchema: inputSchema3,
6073
- outputSchema: outputSchema3,
6074
- annotations: annotations3
6303
+ title: TOOL_TITLE6,
6304
+ description: TOOL_DESCRIPTION6,
6305
+ inputSchema: inputSchema6,
6306
+ outputSchema: outputSchema6,
6307
+ annotations: annotations6
6075
6308
  },
6076
6309
  handler: handleLookupDecision
6077
6310
  };
@@ -7185,7 +7418,7 @@ var { hashCode } = require_hash_code();
7185
7418
  var MAX_SNIPPET_BYTES = 200 * 1024;
7186
7419
  var SLUG_PATTERN = /^[a-z]{3,11}-[a-z]{3,11}-[a-z]{3,11}-(?:[1-9]|[1-9][0-9])$/;
7187
7420
  var PLAYROOM_BASE_URL = "https://arbor.linktr.ee/playroom/";
7188
- var DEFAULT_TIMEOUT_MS2 = 5e3;
7421
+ var DEFAULT_TIMEOUT_MS3 = 5e3;
7189
7422
  function createInMemoryCache() {
7190
7423
  const store = /* @__PURE__ */ new Map();
7191
7424
  return {
@@ -7216,7 +7449,7 @@ function createSlugClient(opts) {
7216
7449
  );
7217
7450
  }
7218
7451
  const cache = opts.cache ?? createInMemoryCache();
7219
- const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS2;
7452
+ const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS3;
7220
7453
  const endpointUrl = opts.endpoint.toString();
7221
7454
  const inflight = /* @__PURE__ */ new Map();
7222
7455
  async function performMint(jsx, key, mintOpts) {
@@ -7304,14 +7537,14 @@ function extractSlug(payload) {
7304
7537
  }
7305
7538
 
7306
7539
  // src/tools/open-in-playroom.ts
7307
- var TOOL_NAME4 = "arbor_open_in_playroom";
7540
+ var TOOL_NAME7 = "arbor_open_in_playroom";
7308
7541
  var DEPRECATED_TOOL_ALIAS = "arbor.open_in_playroom";
7309
- var TOOL_TITLE4 = "Open in Playroom";
7310
- var TOOL_DESCRIPTION4 = `Given an Arbor component name (PascalCase), return the Playroom URL with the canonical snippet pre-loaded. coverage="mapped" resolves to a canonical snippet (use with high confidence); coverage="not_found" links to the Playroom homepage (surface this to the human rather than following it silently). Does NOT do fuzzy/Figma-name matching \u2014 that is the Figma plugin's selection-driven path.`;
7311
- var inputSchema4 = external_exports.object({
7542
+ var TOOL_TITLE7 = "Open in Playroom";
7543
+ var TOOL_DESCRIPTION7 = `Given an Arbor component name (PascalCase), return the Playroom URL with the canonical snippet pre-loaded. coverage="mapped" resolves to a canonical snippet (use with high confidence); coverage="not_found" links to the Playroom homepage (surface this to the human rather than following it silently). Does NOT do fuzzy/Figma-name matching \u2014 that is the Figma plugin's selection-driven path.`;
7544
+ var inputSchema7 = external_exports.object({
7312
7545
  componentName: external_exports.string().trim().min(1, "componentName is required and must be a non-empty string").describe('Arbor PascalCase component name, e.g. "Button" or "HeaderBar"')
7313
7546
  }).strict();
7314
- var outputSchema4 = external_exports.object({
7547
+ var outputSchema7 = external_exports.object({
7315
7548
  coverage: external_exports.enum(["mapped", "not_found"]).describe("mapped = resolved to a canonical snippet; not_found = no match, URL is the Playroom home"),
7316
7549
  url: external_exports.string().describe("Playroom URL \u2014 a ?slug= link, an lz-string #?code= fallback, or the homepage"),
7317
7550
  arborComponentName: external_exports.string().optional().describe("Resolved Arbor component name (mapped only)"),
@@ -7322,7 +7555,7 @@ var outputSchema4 = external_exports.object({
7322
7555
  degraded: external_exports.literal(true).optional().describe("True when slug minting failed and the URL fell back to lz-string"),
7323
7556
  reason: external_exports.enum(["timeout", "http_error", "network", "oversize", "malformed_response", "unconfigured"]).optional().describe("Why the result is degraded")
7324
7557
  });
7325
- var annotations4 = {
7558
+ var annotations7 = {
7326
7559
  readOnlyHint: false,
7327
7560
  idempotentHint: true,
7328
7561
  openWorldHint: true,
@@ -7423,14 +7656,14 @@ async function handleOpenInPlayroom(args) {
7423
7656
  };
7424
7657
  }
7425
7658
  var openInPlayroomTool = {
7426
- name: TOOL_NAME4,
7659
+ name: TOOL_NAME7,
7427
7660
  aliases: [DEPRECATED_TOOL_ALIAS],
7428
7661
  config: {
7429
- title: TOOL_TITLE4,
7430
- description: TOOL_DESCRIPTION4,
7431
- inputSchema: inputSchema4,
7432
- outputSchema: outputSchema4,
7433
- annotations: annotations4
7662
+ title: TOOL_TITLE7,
7663
+ description: TOOL_DESCRIPTION7,
7664
+ inputSchema: inputSchema7,
7665
+ outputSchema: outputSchema7,
7666
+ annotations: annotations7
7434
7667
  },
7435
7668
  handler: handleOpenInPlayroom
7436
7669
  };
@@ -7465,7 +7698,10 @@ var ARBOR_TOOLS = [
7465
7698
  openInPlayroomTool,
7466
7699
  checkSyncHealthTool,
7467
7700
  checkUxWritingTool,
7468
- lookupDecisionTool
7701
+ lookupDecisionTool,
7702
+ listVersionsTool,
7703
+ listComponentsTool,
7704
+ getComponentTool
7469
7705
  ];
7470
7706
  function createArborMcpServer() {
7471
7707
  const server = new McpServer({ name: SERVER_NAME, version: SERVER_VERSION });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@linktr.ee/arbor-mcp",
3
- "version": "0.2.0",
4
- "description": "Model Context Protocol server exposing Arbor design system tools: Playroom snippets, Figma→code sync health, UX-writing checks, and decision-log lookup.",
3
+ "version": "0.4.0-rc.ef036d0d6.7141",
4
+ "description": "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.",
5
5
  "keywords": [
6
6
  "arbor",
7
7
  "linktree",