figma-relai 0.7.3 → 0.7.5
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/dist/{chunk-BEGACSIP.js → chunk-BRUWHN2A.js} +49 -11
- package/dist/chunk-BRUWHN2A.js.map +1 -0
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/{manifest-2MDQRETR.js → manifest-UX6MDMVV.js} +2 -2
- package/package.json +1 -1
- package/dist/chunk-BEGACSIP.js.map +0 -1
- /package/dist/{manifest-2MDQRETR.js.map → manifest-UX6MDMVV.js.map} +0 -0
|
@@ -10,13 +10,16 @@ import {
|
|
|
10
10
|
__export
|
|
11
11
|
} from "./chunk-2H7UOFLK.js";
|
|
12
12
|
|
|
13
|
+
// src/version.ts
|
|
14
|
+
var VERSION = "0.7.5";
|
|
15
|
+
|
|
13
16
|
// src/server.ts
|
|
14
17
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
15
18
|
function createServer() {
|
|
16
19
|
return new McpServer(
|
|
17
20
|
{
|
|
18
21
|
name: "Relai",
|
|
19
|
-
version:
|
|
22
|
+
version: VERSION
|
|
20
23
|
},
|
|
21
24
|
{
|
|
22
25
|
instructions: `
|
|
@@ -866,10 +869,12 @@ var FileIdentityError = class extends Error {
|
|
|
866
869
|
};
|
|
867
870
|
async function resolveFileIdentity(sendCommand) {
|
|
868
871
|
const probe = await sendCommand("get_file_identity", {});
|
|
869
|
-
|
|
872
|
+
const cached = probe?.identity?.fileKey ? probe.identity : null;
|
|
873
|
+
if (cached && cached.by === VERSION) return cached;
|
|
870
874
|
const rootName = probe?.rootName ?? "";
|
|
871
875
|
const candidates = probe?.candidates ?? [];
|
|
872
876
|
if (candidates.length === 0) {
|
|
877
|
+
if (cached) return cached;
|
|
873
878
|
throw new FileIdentityError(
|
|
874
879
|
"This file publishes no components, so its key cannot be resolved from the plugin (figma.fileKey is private-plugin-only)."
|
|
875
880
|
);
|
|
@@ -904,7 +909,8 @@ async function resolveFileIdentity(sendCommand) {
|
|
|
904
909
|
const file = await figmaApi(token, `/files/${main.fileKey}?branch_data=true&depth=1`);
|
|
905
910
|
const identity = {
|
|
906
911
|
fileKey: main.fileKey,
|
|
907
|
-
fileName: typeof file.json.name === "string" ? file.json.name : rootName
|
|
912
|
+
fileName: typeof file.json.name === "string" ? file.json.name : rootName,
|
|
913
|
+
by: VERSION
|
|
908
914
|
};
|
|
909
915
|
if (rootName && rootName !== identity.fileName) {
|
|
910
916
|
const branches = file.json.branches ?? [];
|
|
@@ -1885,7 +1891,7 @@ import { z as z12 } from "zod";
|
|
|
1885
1891
|
function register11(server, sendCommand) {
|
|
1886
1892
|
server.tool(
|
|
1887
1893
|
"import_from_library",
|
|
1888
|
-
"Import a component, style, or variable from an enabled team library by its key.
|
|
1894
|
+
"Import a component, style, or variable from an enabled team library by its key. Keys come from get_design_system's libraryCatalog, which resolves each library this file draws from and lists everything it publishes \u2014 not just what the file already uses. If the library you want is not in that catalog, it is listed under librariesNotCatalogued with what to do about it. Imported components can then be instantiated with manage_components.",
|
|
1889
1895
|
{
|
|
1890
1896
|
kind: z12.enum(["component", "style", "variable"]),
|
|
1891
1897
|
key: z12.string().describe("Library key")
|
|
@@ -2195,13 +2201,15 @@ function register15(server, sendCommand) {
|
|
|
2195
2201
|
var design_system_exports = {};
|
|
2196
2202
|
__export(design_system_exports, {
|
|
2197
2203
|
discoverLibraryFiles: () => discoverLibraryFiles,
|
|
2204
|
+
librariesNotCatalogued: () => librariesNotCatalogued,
|
|
2205
|
+
probeFailureNote: () => probeFailureNote,
|
|
2198
2206
|
register: () => register16
|
|
2199
2207
|
});
|
|
2200
2208
|
import { z as z17 } from "zod";
|
|
2201
2209
|
function register16(server, sendCommand) {
|
|
2202
2210
|
server.tool(
|
|
2203
2211
|
"get_design_system",
|
|
2204
|
-
"Inventory the design system available to this file \u2014 call this BEFORE building UI, then prefer instantiating existing components (manage_components action:instantiate takes local and library keys) and binding existing variables over drawing raw shapes. Reports: local components/styles/variable collections with usage counts, remote components/styles the file already uses, enabled libraries' variable collections, and \u2014 with a token stored (`npx figma-relai login`) \u2014 the FULL published catalog of every library this file consumes, resolved automatically. What the file already uses is never the whole library: a component the file has not placed yet is invisible until this catalog is read. Results are cached per session \u2014 pass refresh:true after big library changes.",
|
|
2212
|
+
"Inventory the design system available to this file \u2014 call this BEFORE building UI, then prefer instantiating existing components (manage_components action:instantiate takes local and library keys) and binding existing variables over drawing raw shapes. Reports: local components/styles/variable collections with usage counts, remote components/styles the file already uses, enabled libraries' variable collections, and \u2014 with a token stored (`npx figma-relai login`) \u2014 the FULL published catalog of every library this file consumes, resolved automatically. What the file already uses is never the whole library: a component the file has not placed yet is invisible until this catalog is read. A library the file has drawn from NOTHING at all cannot be resolved either \u2014 those are named under librariesNotCatalogued, with the one step that fixes it. Results are cached per session \u2014 pass refresh:true after big library changes.",
|
|
2205
2213
|
{
|
|
2206
2214
|
refresh: z17.boolean().optional().describe("Rescan instead of using the session cache"),
|
|
2207
2215
|
libraryFileUrl: z17.string().optional().describe(
|
|
@@ -2217,6 +2225,10 @@ function register16(server, sendCommand) {
|
|
|
2217
2225
|
12e4
|
|
2218
2226
|
);
|
|
2219
2227
|
data.libraryCatalog = await fetchLibraryCatalogs(libraryFileUrl, data);
|
|
2228
|
+
if (!libraryFileUrl) {
|
|
2229
|
+
const missed = librariesNotCatalogued(data);
|
|
2230
|
+
if (missed.length) data.librariesNotCatalogued = missedNote(missed);
|
|
2231
|
+
}
|
|
2220
2232
|
const truncNotes = [];
|
|
2221
2233
|
const components = data.components;
|
|
2222
2234
|
for (const [group, entry] of Object.entries(components ?? {})) {
|
|
@@ -2308,9 +2320,10 @@ function apiFor(token) {
|
|
|
2308
2320
|
return { ok: res.ok, status: res.status, json };
|
|
2309
2321
|
};
|
|
2310
2322
|
}
|
|
2311
|
-
function
|
|
2312
|
-
|
|
2313
|
-
|
|
2323
|
+
function probeFailureNote(statuses) {
|
|
2324
|
+
const denied = statuses.filter((s) => s === 401 || s === 403).length;
|
|
2325
|
+
if (denied === 0) return null;
|
|
2326
|
+
return denied === statuses.length ? "Figma rejected the token on every probe (401/403) \u2014 it is expired or lacks the file content read scope. Re-run `npx -y figma-relai@latest login`, then retry." : `Figma refused ${denied} of ${statuses.length} probes with 401/403 \u2014 the token works, but it cannot read the libraries this file draws from (they belong to a team or organisation it has no access to). Ask for access to that library file, or pass libraryFileUrl for one you can read.`;
|
|
2314
2327
|
}
|
|
2315
2328
|
var metaOf = (r) => r.json.meta ?? {};
|
|
2316
2329
|
async function discoverLibraryFiles(api, scan) {
|
|
@@ -2334,7 +2347,7 @@ async function discoverLibraryFiles(api, scan) {
|
|
|
2334
2347
|
}
|
|
2335
2348
|
}
|
|
2336
2349
|
if (found.size === 0) {
|
|
2337
|
-
const denied =
|
|
2350
|
+
const denied = probeFailureNote(statuses);
|
|
2338
2351
|
if (denied) throw new Error(denied);
|
|
2339
2352
|
}
|
|
2340
2353
|
return [...found].map(([fileKey, via]) => ({ fileKey, via }));
|
|
@@ -2376,6 +2389,29 @@ async function catalogOne(api, fileKey) {
|
|
|
2376
2389
|
}))
|
|
2377
2390
|
};
|
|
2378
2391
|
}
|
|
2392
|
+
function librariesNotCatalogued(data) {
|
|
2393
|
+
const catalog = data.libraryCatalog;
|
|
2394
|
+
if (!catalog?.libraries) return [];
|
|
2395
|
+
const norm = (s) => s.trim().toLowerCase();
|
|
2396
|
+
const seen = new Set(catalog.libraries.map((l) => norm(l.name ?? "")));
|
|
2397
|
+
const enabled = data.variables?.libraryCollections;
|
|
2398
|
+
const names = /* @__PURE__ */ new Set();
|
|
2399
|
+
for (const c of enabled ?? []) {
|
|
2400
|
+
const name = c.libraryName?.trim();
|
|
2401
|
+
if (name && !seen.has(norm(name))) names.add(name);
|
|
2402
|
+
}
|
|
2403
|
+
return [...names];
|
|
2404
|
+
}
|
|
2405
|
+
function missedNote(names) {
|
|
2406
|
+
return {
|
|
2407
|
+
libraries: names,
|
|
2408
|
+
why: "Enabled, but absent from the catalog above \u2014 usually because this file has not placed a single component or style from them, so there is no key to resolve their file from.",
|
|
2409
|
+
// The remedy is the designer's, not the agent's: one instance is enough,
|
|
2410
|
+
// and only while the scan runs — the keys it yields stay valid after.
|
|
2411
|
+
how: "Ask the designer to drag any one component from that library onto the canvas (any page), then call this tool again with refresh:true. The instance can be deleted once the catalog is read \u2014 the keys remain valid. Or pass libraryFileUrl with the library file's figma.com URL.",
|
|
2412
|
+
ignoreIf: "A library that publishes only variables always lands here; its contents are already in variables.libraryCollections."
|
|
2413
|
+
};
|
|
2414
|
+
}
|
|
2379
2415
|
async function fetchLibraryCatalogs(libraryFileUrl, scan) {
|
|
2380
2416
|
const token = loadToken();
|
|
2381
2417
|
if (!token) {
|
|
@@ -2396,8 +2432,9 @@ async function fetchLibraryCatalogs(libraryFileUrl, scan) {
|
|
|
2396
2432
|
return { note: err.message };
|
|
2397
2433
|
}
|
|
2398
2434
|
if (!targets.length) {
|
|
2435
|
+
const remote = (scan.components?.remoteUsed?.items?.length ?? 0) + (scan.styles?.remoteUsed?.items?.length ?? 0);
|
|
2399
2436
|
return {
|
|
2400
|
-
note: "No library file could be resolved from the keys this file holds \u2014 every candidate 404'd (unpublished, or from a library this token cannot read). Pass libraryFileUrl explicitly."
|
|
2437
|
+
note: remote ? "No library file could be resolved from the keys this file holds \u2014 every candidate 404'd (unpublished, or from a library this token cannot read). Pass libraryFileUrl explicitly." : "This file uses no remote components or styles, so there is no key to resolve a library from. Either it draws from none, or it publishes its own \u2014 check components.local. Pass libraryFileUrl to catalog a specific library anyway."
|
|
2401
2438
|
};
|
|
2402
2439
|
}
|
|
2403
2440
|
}
|
|
@@ -4433,6 +4470,7 @@ function registerPrompts(server) {
|
|
|
4433
4470
|
}
|
|
4434
4471
|
|
|
4435
4472
|
export {
|
|
4473
|
+
VERSION,
|
|
4436
4474
|
createServer,
|
|
4437
4475
|
FIGMA_COMMANDS,
|
|
4438
4476
|
RelayCore,
|
|
@@ -4444,4 +4482,4 @@ export {
|
|
|
4444
4482
|
listToolCatalog,
|
|
4445
4483
|
registerPrompts
|
|
4446
4484
|
};
|
|
4447
|
-
//# sourceMappingURL=chunk-
|
|
4485
|
+
//# sourceMappingURL=chunk-BRUWHN2A.js.map
|