@bridge-ds/mcp 0.7.0 → 0.7.1

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.
@@ -4916,6 +4916,7 @@ function figmaDraftBrief(d, ctx) {
4916
4916
  ...d.reasons?.length ? ["Why this needs code:", ...d.reasons.map((r) => `- ${r}`), ""] : [],
4917
4917
  `Spec: ${ctx.specPath} ${suggested ? "in the suggestion" : "on the branch"} has the bound variables of every variant (token paths), the layers of every variant and what code gave Figma before.`,
4918
4918
  ...d.figma?.url ? [`Design: ${d.figma.url}`] : [],
4919
+ ...suggested && d.figma?.fileKey && d.figma.nodeId ? [`Read the design straight from Figma: Figma MCP get_design_context { fileKey: "${d.figma.fileKey}", nodeId: "${d.figma.nodeId}" }${d.figma.before?.nodeId ? ` (before: node ${d.figma.before.nodeId})` : ""}, or the REST API GET https://api.figma.com/v1/files/${d.figma.fileKey}/nodes?ids=${encodeURIComponent(d.figma.nodeId)}.`] : [],
4919
4920
  ...d.images?.length ? ["Screenshot:", ...d.images.map((u) => `- ${u}`)] : [],
4920
4921
  "",
4921
4922
  "Steps:",
@@ -5224,6 +5225,7 @@ var SUGGESTION_STATUS_LABEL = {
5224
5225
  declined: "Declined",
5225
5226
  outdated: "Outdated"
5226
5227
  };
5228
+ var figmaNodeUrl = (fileKey, nodeId) => `https://www.figma.com/design/${fileKey}/?node-id=${encodeURIComponent(nodeId.replace(/:/g, "-"))}`;
5227
5229
  var SUGGESTION_LIMITS = {
5228
5230
  body: 65536,
5229
5231
  /** The readable part of the body (tables are cut with "and N more"). */
@@ -5530,6 +5532,19 @@ function linksSection(p) {
5530
5532
  const out = [];
5531
5533
  if (l.neededBy) out.push(`- Needed by ${l.neededBy.productName}: [#${l.neededBy.pr} ${cell3(l.neededBy.title)}](${l.neededBy.url})`);
5532
5534
  if (l.figma?.url) out.push(`- Design: [Open in Figma](${l.figma.url})`);
5535
+ const key = l.figma?.fileKey;
5536
+ const nodes = (l.figma?.nodes ?? []).filter((n) => n && typeof n.nodeId === "string");
5537
+ for (const n of nodes) {
5538
+ const url = n.url ?? (key ? figmaNodeUrl(key, n.nodeId) : void 0);
5539
+ const what = `${n.state === "before" ? "Before" : n.state === "after" ? "After" : "Node"}${n.name ? ` (${cell3(n.name)})` : ""}`;
5540
+ out.push(`- ${what}: ${url ? `[node ${n.nodeId}](${url})` : `node ${n.nodeId}`}`);
5541
+ }
5542
+ if (key && (nodes.length || l.figma?.nodeId)) {
5543
+ const ids = nodes.length ? nodes.map((n) => n.nodeId) : [l.figma.nodeId];
5544
+ out.push(
5545
+ `- Claude Code reads this design straight from Figma: the Figma MCP server (\`get_design_context\` with file key \`${key}\` and node ${ids.map((i) => `\`${i}\``).join(", ")}) or the REST API (\`GET https://api.figma.com/v1/files/${key}/nodes?ids=${ids.map(encodeURIComponent).join(",")}\`). No image hosting is needed.`
5546
+ );
5547
+ }
5533
5548
  if (l.exploration) out.push(`- Exploration: \`${l.exploration}\``);
5534
5549
  if (l.supersedes) out.push(`- Replaces suggestion #${l.supersedes}`);
5535
5550
  for (const v of p.previews ?? []) out.push(`- ${cell3(v.label)}: ${v.url}`);
@@ -5768,6 +5783,8 @@ function suggestionShapeProblems(raw) {
5768
5783
  if (p.docs !== void 0 && !(Array.isArray(p.docs) && p.docs.every((d) => d && typeof d.component === "string" && typeof d.path === "string"))) out.push('"docs" must be a list of { "component", "path", "after" }.');
5769
5784
  const spec = p.componentSpec;
5770
5785
  if (spec !== void 0 && (!spec || typeof spec.component !== "string" || typeof spec.specPath !== "string" || typeof spec.brief !== "string")) out.push('"componentSpec" must be { "component", "specPath", "spec", "brief" }.');
5786
+ const nodes = p.links?.figma?.nodes;
5787
+ if (nodes !== void 0 && !(Array.isArray(nodes) && nodes.every((n) => n && typeof n.nodeId === "string"))) out.push('"links.figma.nodes" must be a list of { "nodeId", "url"?, "name"?, "state"? }.');
5771
5788
  if (p.files !== void 0) {
5772
5789
  if (!Array.isArray(p.files)) out.push('"files" must be a list.');
5773
5790
  else {
@@ -6564,7 +6581,8 @@ function componentSpecSuggestionPayload(d, plan, ctx) {
6564
6581
  const spec = JSON.parse(formatFigmaSpec(d, plan));
6565
6582
  const brief = figmaDraftBrief({ ...d, tokenEdits: plan.edits, reasons: plan.reasons }, { suggestion: true, specPath, componentsDir: ctx.componentsDir });
6566
6583
  const by = (ctx.suggestedBy ?? d.requestedBy)?.replace(/^@/, "");
6567
- const figma = d.figma?.fileKey || d.figma?.nodeId || d.figma?.url ? { ...d.figma.fileKey ? { fileKey: d.figma.fileKey } : {}, ...d.figma.nodeId ? { nodeId: d.figma.nodeId } : {}, ...d.figma.url ? { url: d.figma.url } : {} } : void 0;
6584
+ const figma = d.figma?.fileKey || d.figma?.nodeId || d.figma?.url ? { ...d.figma.fileKey ? { fileKey: d.figma.fileKey } : {}, ...d.figma.nodeId ? { nodeId: d.figma.nodeId } : {}, ...d.figma.url ? { url: d.figma.url } : {}, ...figmaNodes(d) } : void 0;
6585
+ const previews = (d.images ?? []).map((url, i) => ({ label: `${d.component} in Figma${(d.images?.length ?? 0) > 1 ? ` ${i + 1}` : ""}`, url }));
6568
6586
  return {
6569
6587
  schema: SUGGESTION_SCHEMA,
6570
6588
  kind: "component-spec",
@@ -6577,9 +6595,19 @@ function componentSpecSuggestionPayload(d, plan, ctx) {
6577
6595
  tier: specTier(d),
6578
6596
  componentSpec: { component: d.component, specPath, spec, brief },
6579
6597
  checks: { newErrors: 0, contrast: [], blocked: null, impact: 0 },
6580
- ...figma ? { links: { figma } } : {}
6598
+ ...figma ? { links: { figma } } : {},
6599
+ ...previews.length ? { previews } : {}
6581
6600
  };
6582
6601
  }
6602
+ function figmaNodes(d) {
6603
+ const f = d.figma;
6604
+ if (!f) return {};
6605
+ const url = (id, given) => given ?? (f.fileKey ? figmaNodeUrl(f.fileKey, id) : void 0);
6606
+ const nodes = [];
6607
+ if (f.before?.nodeId) nodes.push({ nodeId: f.before.nodeId, state: "before", ...url(f.before.nodeId, f.before.url) ? { url: url(f.before.nodeId, f.before.url) } : {}, ...f.before.nodeName ? { name: f.before.nodeName } : {} });
6608
+ if (f.nodeId) nodes.push({ nodeId: f.nodeId, state: "after", ...url(f.nodeId, f.url) ? { url: url(f.nodeId, f.url) } : {}, ...f.nodeName ? { name: f.nodeName } : {} });
6609
+ return nodes.length ? { nodes } : {};
6610
+ }
6583
6611
  async function openComponentPushOnGitHub(gh, input) {
6584
6612
  const d = input.diff;
6585
6613
  if (!input.plan && !input.tokens) throw new Error("Route the change first: pass the token facts or the plan.");
@@ -6600,7 +6628,7 @@ async function openComponentPushOnGitHub(gh, input) {
6600
6628
  };
6601
6629
  if (plan.route === "token-change") {
6602
6630
  if (!input.writeTokens) throw new GitHubError(422, "This change is a token change: send it where the token files are read (the DS Bridge plugin, bridge_push_figma_component or `bridge figma push`).");
6603
- const figma = { ...origin.fileKey ? { fileKey: origin.fileKey } : {}, ...origin.nodeId ? { nodeId: origin.nodeId } : {}, ...origin.url ? { url: origin.url } : {} };
6631
+ const figma = { ...origin.fileKey ? { fileKey: origin.fileKey } : {}, ...origin.nodeId ? { nodeId: origin.nodeId } : {}, ...origin.url ? { url: origin.url } : {}, ...figmaNodes(d) };
6604
6632
  const r = await input.writeTokens({ edits: plan.edits, title: figmaBranchTitle(d), summary: d.summary.trim() || d.title, bodyExtra: [figmaOriginSection(origin)], mode, figma });
6605
6633
  await gh.addLabels(r.number, [FROM_FIGMA_LABEL]);
6606
6634
  if (r.status === "suggested") return { status: "suggested", plan, link: null, suggestion: { route: "token-change", number: r.number, url: r.url } };
@@ -12783,7 +12811,7 @@ async function pushFigmaComponentOp(ctx, a) {
12783
12811
  }
12784
12812
 
12785
12813
  // src/server.ts
12786
- var VERSION = true ? "0.7.0" : "dev";
12814
+ var VERSION = true ? "0.7.1" : "dev";
12787
12815
  var ok = (data) => ({ content: [{ type: "text", text: typeof data === "string" ? data : JSON.stringify(data, null, 2) }] });
12788
12816
  var fail2 = (e) => ({ content: [{ type: "text", text: `Error: ${e.message ?? String(e)}` }], isError: true });
12789
12817
  var run = (fn) => async (a) => {
package/dist/index.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  VERSION,
4
4
  optionsFromArgv,
5
5
  serveStdio
6
- } from "./chunk-MFV5WHB5.js";
6
+ } from "./chunk-YCKNSKPW.js";
7
7
  import "./chunk-BZ3KXXMM.js";
8
8
 
9
9
  // src/index.ts
package/dist/lib.js CHANGED
@@ -28,7 +28,7 @@ import {
28
28
  serveStdio,
29
29
  studio_exports,
30
30
  suggestions_exports
31
- } from "./chunk-MFV5WHB5.js";
31
+ } from "./chunk-YCKNSKPW.js";
32
32
  import {
33
33
  CONFIG_FILE,
34
34
  loadConfig,
@@ -154,6 +154,7 @@ export declare function getSuggestionOp(ctx: BridgeContext, a: {
154
154
  fileKey?: string | undefined;
155
155
  nodeId?: string | undefined;
156
156
  url?: string | undefined;
157
+ nodes?: import("@bridge-ds/core").SuggestionFigmaNode[] | undefined;
157
158
  } | undefined;
158
159
  exploration?: string | undefined;
159
160
  supersedes?: number | undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bridge-ds/mcp",
3
- "version": "0.7.0",
3
+ "version": "0.7.1",
4
4
  "type": "module",
5
5
  "description": "Bridge MCP server: lets Claude (Claude Code, Claude Desktop or any MCP client) read, check and propose changes to a DTCG design system repo through pull requests.",
6
6
  "license": "MIT",
@@ -54,8 +54,8 @@
54
54
  "build:package": "node ../../scripts/build-packages.mjs mcp"
55
55
  },
56
56
  "dependencies": {
57
- "@bridge-ds/core": "^0.10.0",
58
- "@bridge-ds/visual": "^0.3.4",
57
+ "@bridge-ds/core": "^0.11.0",
58
+ "@bridge-ds/visual": "^0.3.5",
59
59
  "@modelcontextprotocol/sdk": "1.31.0",
60
60
  "typescript": "5.9.3",
61
61
  "zod": "3.25.76"