pigment-mcp 0.3.0 → 0.4.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.
Files changed (3) hide show
  1. package/README.md +6 -6
  2. package/cli.js +147 -108
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -3,11 +3,10 @@
3
3
  # pigment-mcp
4
4
 
5
5
  MCP server for [Pigment](https://pigmentui.com) design systems. Your coding agent (Claude Code, Codex,
6
- Cursor or any MCP client) reads tokens, rules, component specs and installation guides for a pinned
7
- version while it works.
6
+ Cursor or any MCP client) reads tokens, rules, component specs and installation guides while it works.
8
7
 
9
- Every published design system ships inside this package, so it works offline and a version never changes
10
- under you. New systems arrive with new package versions.
8
+ Every design system ships inside this package, so it works offline. New systems and updates arrive with new
9
+ package releases.
11
10
 
12
11
  ## Connect
13
12
 
@@ -42,11 +41,12 @@ propose a plan, restyle one sample screen, then report build and visual checks.
42
41
 
43
42
  | Tool | Returns |
44
43
  | --- | --- |
45
- | `list_design_systems` | Published systems with tags and versions (`tone?`, `appearance?`) |
46
- | `get_design_system` | The full spec of one pinned version: rules, token tables, setup, verification |
44
+ | `list_design_systems` | Systems with tags and when each was last updated (`tone?`, `appearance?`) |
45
+ | `get_design_system` | The full spec of a system: rules, token tables, setup, verification |
47
46
  | `get_tokens` | `tokens.css` or `tokens.json`, to copy verbatim |
48
47
  | `get_component_spec` | One component: anatomy, variants, states, accessibility, token mapping |
49
48
  | `get_installation_guide` | Steps and approval points for `new`, `existing` or `ongoing` |
49
+ | `get_changelog` | What changed, by date — to update a project that already has the system |
50
50
  | `get_references` | Screenshots of four reference apps restyled with the system |
51
51
 
52
52
  Requires Node 22+.
package/cli.js CHANGED
@@ -1536,10 +1536,10 @@ var init_regexes = __esm({
1536
1536
  duration = /^P(?:(\d+W)|(?!.*W)(?=\d|T\d)(\d+Y)?(\d+M)?(\d+D)?(T(?=\d)(\d+H)?(\d+M)?(\d+([.,]\d+)?S)?)?)$/;
1537
1537
  extendedDuration = /^[-+]?P(?!$)(?:(?:[-+]?\d+Y)|(?:[-+]?\d+[.,]\d+Y$))?(?:(?:[-+]?\d+M)|(?:[-+]?\d+[.,]\d+M$))?(?:(?:[-+]?\d+W)|(?:[-+]?\d+[.,]\d+W$))?(?:(?:[-+]?\d+D)|(?:[-+]?\d+[.,]\d+D$))?(?:T(?=[\d+-])(?:(?:[-+]?\d+H)|(?:[-+]?\d+[.,]\d+H$))?(?:(?:[-+]?\d+M)|(?:[-+]?\d+[.,]\d+M$))?(?:[-+]?\d+(?:[.,]\d+)?S)?)??$/;
1538
1538
  guid = /^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})$/;
1539
- uuid = (version3) => {
1540
- if (!version3)
1539
+ uuid = (version2) => {
1540
+ if (!version2)
1541
1541
  return /^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$/;
1542
- return new RegExp(`^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-${version3}[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$`);
1542
+ return new RegExp(`^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-${version2}[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$`);
1543
1543
  };
1544
1544
  uuid4 = /* @__PURE__ */ uuid(4);
1545
1545
  uuid6 = /* @__PURE__ */ uuid(6);
@@ -19777,10 +19777,10 @@ function fromJSONSchema(schema, params) {
19777
19777
  } catch {
19778
19778
  throw new Error("fromJSONSchema input is not valid JSON (possibly cyclic); use $defs/$ref for recursive schemas");
19779
19779
  }
19780
- const version3 = detectVersion(normalized, params?.defaultTarget);
19780
+ const version2 = detectVersion(normalized, params?.defaultTarget);
19781
19781
  const defs = normalized.$defs || normalized.definitions || {};
19782
19782
  const ctx = {
19783
- version: version3,
19783
+ version: version2,
19784
19784
  defs,
19785
19785
  refs: /* @__PURE__ */ new Map(),
19786
19786
  processing: /* @__PURE__ */ new Set(),
@@ -21466,14 +21466,14 @@ function missingClientCapabilities(required2, declared) {
21466
21466
  }
21467
21467
  return Object.keys(missing).length > 0 ? missing : void 0;
21468
21468
  }
21469
- function isModernProtocolVersion(version3) {
21470
- return version3 >= FIRST_MODERN_PROTOCOL_VERSION;
21469
+ function isModernProtocolVersion(version2) {
21470
+ return version2 >= FIRST_MODERN_PROTOCOL_VERSION;
21471
21471
  }
21472
21472
  function legacyProtocolVersions(versions) {
21473
- return versions.filter((version3) => !isModernProtocolVersion(version3));
21473
+ return versions.filter((version2) => !isModernProtocolVersion(version2));
21474
21474
  }
21475
21475
  function modernProtocolVersions(versions) {
21476
- return versions.filter((version3) => isModernProtocolVersion(version3));
21476
+ return versions.filter((version2) => isModernProtocolVersion(version2));
21477
21477
  }
21478
21478
  function appendTextFallbackForNonObject(result) {
21479
21479
  const sc = result.structuredContent;
@@ -23588,8 +23588,8 @@ function getWireResultSchemas() {
23588
23588
  };
23589
23589
  return wireResultSchemasMemo;
23590
23590
  }
23591
- function codecForVersion(version3) {
23592
- return version3 !== void 0 && isModernProtocolVersion(version3) ? rev2026Codec : rev2025Codec;
23591
+ function codecForVersion(version2) {
23592
+ return version2 !== void 0 && isModernProtocolVersion(version2) ? rev2026Codec : rev2025Codec;
23593
23593
  }
23594
23594
  function classifiedWireEra(classification) {
23595
23595
  if (classification.revision !== void 0) return codecForVersion(classification.revision).era;
@@ -25202,8 +25202,8 @@ var init_src_Cqbh3MYc = __esm({
25202
25202
  */
25203
25203
  _negotiatedProtocolVersion;
25204
25204
  static {
25205
- writeNegotiatedProtocolVersion = (instance, version3) => {
25206
- instance._negotiatedProtocolVersion = version3;
25205
+ writeNegotiatedProtocolVersion = (instance, version2) => {
25206
+ instance._negotiatedProtocolVersion = version2;
25207
25207
  };
25208
25208
  }
25209
25209
  _supportedProtocolVersions;
@@ -33268,7 +33268,7 @@ var init_mcp_DIH4cS6P = __esm({
33268
33268
  if (identity.clientInfo !== void 0) server._clientVersion = identity.clientInfo;
33269
33269
  };
33270
33270
  installDiscoverHandler = (server, servedModernVersions) => {
33271
- const missing = servedModernVersions.filter((version3) => !server._supportedProtocolVersions.includes(version3));
33271
+ const missing = servedModernVersions.filter((version2) => !server._supportedProtocolVersions.includes(version2));
33272
33272
  if (missing.length > 0) server._supportedProtocolVersions = [...server._supportedProtocolVersions, ...missing];
33273
33273
  server.setRequestHandler("server/discover", () => server._ondiscover());
33274
33274
  };
@@ -34680,7 +34680,7 @@ var init_src = __esm({
34680
34680
 
34681
34681
  // ../content/src/index.ts
34682
34682
  import { existsSync, readdirSync, readFileSync } from "node:fs";
34683
- import { dirname, join, resolve } from "node:path";
34683
+ import { basename, dirname, join, resolve } from "node:path";
34684
34684
  function findContentRoot() {
34685
34685
  if (process.env.PIGMENT_CONTENT_DIR) return resolve(process.env.PIGMENT_CONTENT_DIR);
34686
34686
  let dir = process.cwd();
@@ -34705,25 +34705,31 @@ function publishedVersions(id2) {
34705
34705
  if (!entry) throw new ContentError("unknown_template", `No published template "${id2}"`);
34706
34706
  return entry.versions.filter((v) => v.status === "published").map((v) => v.version);
34707
34707
  }
34708
- function versionDir(id2, version3) {
34708
+ function versionDir(id2, version2) {
34709
34709
  const versions = publishedVersions(id2);
34710
- const resolvedVersion = version3 === "latest" ? registry2().templates.find((t) => t.id === id2)?.latest : version3;
34710
+ const resolvedVersion = version2 === "latest" ? registry2().templates.find((t) => t.id === id2)?.latest : version2;
34711
34711
  if (!resolvedVersion || !versions.includes(resolvedVersion)) {
34712
- throw new ContentError("unknown_version", `"${id2}" has no published version "${version3}". Available: ${versions.join(", ") || "none"}`);
34712
+ throw new ContentError("unknown_version", `"${id2}" has no published version "${version2}". Available: ${versions.join(", ") || "none"}`);
34713
34713
  }
34714
34714
  return join(contentRoot(), "templates", id2, resolvedVersion);
34715
34715
  }
34716
- function getManifest(id2, version3) {
34717
- return Manifest.parse(readJson(join(versionDir(id2, version3), "manifest.json")));
34716
+ function getManifest(id2, version2) {
34717
+ return Manifest.parse(readJson(join(versionDir(id2, version2), "manifest.json")));
34718
34718
  }
34719
- function readArtifact(id2, version3, rel) {
34720
- const file2 = join(versionDir(id2, version3), rel);
34721
- if (!existsSync(file2)) throw new ContentError("unavailable_artifact", `${id2}@${version3} has no ${rel}`);
34722
- return readFileSync(file2, "utf8");
34719
+ function readArtifact(id2, version2, rel) {
34720
+ const dir = versionDir(id2, version2);
34721
+ const file2 = join(dir, rel);
34722
+ if (!existsSync(file2)) throw new ContentError("unavailable_artifact", `${id2}@${version2} has no ${rel}`);
34723
+ return unversioned(id2, basename(dir), readFileSync(file2, "utf8"));
34723
34724
  }
34724
- function getTokens(id2, version3) {
34725
- const manifest = getManifest(id2, version3);
34726
- const dir = versionDir(id2, version3);
34725
+ function unversioned(id2, version2, text2) {
34726
+ const name = getManifest(id2, version2).name;
34727
+ const date5 = (registry2().templates.find((t) => t.id === id2)?.versions.find((v) => v.version === version2)?.publishedAt ?? "").slice(0, 10);
34728
+ return text2.replaceAll(`${name} ${version2}`, name).replaceAll(`${id2}@${version2}`, date5 ? `${id2}, updated ${date5}` : id2).replaceAll(`"version": "${version2}",`, `"updated": "${date5}",`);
34729
+ }
34730
+ function getTokens(id2, version2) {
34731
+ const manifest = getManifest(id2, version2);
34732
+ const dir = versionDir(id2, version2);
34727
34733
  const primitives = readJson(join(dir, manifest.artifacts.tokens.primitives));
34728
34734
  const out = {};
34729
34735
  for (const mode2 of ["light", "dark"]) {
@@ -34734,41 +34740,67 @@ function getTokens(id2, version3) {
34734
34740
  }
34735
34741
  return out;
34736
34742
  }
34737
- function getComponents(id2, version3) {
34738
- const manifest = getManifest(id2, version3);
34739
- return ComponentIndex.parse(JSON.parse(readArtifact(id2, version3, manifest.artifacts.components)));
34743
+ function getComponents(id2, version2) {
34744
+ const manifest = getManifest(id2, version2);
34745
+ return ComponentIndex.parse(JSON.parse(readArtifact(id2, version2, manifest.artifacts.components)));
34740
34746
  }
34741
- function getComponentSpec(id2, version3, name) {
34742
- const entry = getComponents(id2, version3).components.find((c) => c.name === name);
34743
- if (!entry) throw new ContentError("invalid_component", `${id2}@${version3} has no component "${name}"`);
34744
- if (entry.status === "unsupported" || !entry.file) return { entry, markdown: `${name} is not specified in this version.` };
34745
- return { entry, markdown: readArtifact(id2, version3, join("components", entry.file)) };
34747
+ function getComponentSpec(id2, version2, name) {
34748
+ const entry = getComponents(id2, version2).components.find((c) => c.name === name);
34749
+ if (!entry) throw new ContentError("invalid_component", `${id2}@${version2} has no component "${name}"`);
34750
+ if (entry.status === "unsupported" || !entry.file) return { entry, markdown: `${name} is not specified in this system.` };
34751
+ return { entry, markdown: readArtifact(id2, version2, join("components", entry.file)) };
34746
34752
  }
34747
- function getGuide(id2, version3, mode2) {
34753
+ function getGuide(id2, version2, mode2) {
34748
34754
  if (!(mode2 in GUIDE_SECTIONS)) throw new ContentError("invalid_mode", `mode must be one of ${Object.keys(GUIDE_SECTIONS).join(", ")}`);
34749
- const text2 = readArtifact(id2, version3, getManifest(id2, version3).artifacts.instructions);
34755
+ const text2 = readArtifact(id2, version2, getManifest(id2, version2).artifacts.instructions);
34750
34756
  const start = text2.indexOf(GUIDE_SECTIONS[mode2]);
34751
34757
  const rest = text2.slice(start);
34752
34758
  const next = rest.indexOf("\n## ", 3);
34753
34759
  const verification = text2.slice(text2.indexOf("## Verification"));
34754
34760
  return (next > 0 ? rest.slice(0, next) : rest).trim() + "\n\n" + verification.trim();
34755
34761
  }
34756
- function getAgentMd(id2, version3) {
34757
- return readArtifact(id2, version3, join("dist", "agent.md"));
34762
+ function getAgentMd(id2, version2) {
34763
+ return readArtifact(id2, version2, join("dist", "agent.md"));
34764
+ }
34765
+ function getTokenFile(id2, version2, format) {
34766
+ return readArtifact(id2, version2, join("dist", `tokens.${format}`));
34758
34767
  }
34759
- function getTokenFile(id2, version3, format) {
34760
- return readArtifact(id2, version3, join("dist", `tokens.${format}`));
34768
+ function changelogEntry(id2, version2) {
34769
+ let md;
34770
+ try {
34771
+ md = readArtifact(id2, version2, "CHANGELOG.md").replace(/\r\n/g, "\n");
34772
+ } catch {
34773
+ return { summary: "", changes: [] };
34774
+ }
34775
+ const start = md.indexOf(`
34776
+ ## ${version2}
34777
+ `);
34778
+ if (start < 0) return { summary: "", changes: [] };
34779
+ const rest = md.slice(start + version2.length + 5);
34780
+ const section = rest.slice(0, rest.search(/\n## |$/)).trim();
34781
+ const summary = (section.split(/\n\s*\n/)[0] ?? "").replace(/\s*\n\s*/g, " ").trim();
34782
+ const changes = [];
34783
+ for (const line of section.split("\n")) {
34784
+ if (line.startsWith("- ")) changes.push(line.slice(2).trim());
34785
+ else if (/^\s+\S/.test(line) && changes.length) changes[changes.length - 1] += ` ${line.trim()}`;
34786
+ }
34787
+ return { summary, changes };
34788
+ }
34789
+ function getHistory(id2) {
34790
+ const entry = registry2().templates.find((t) => t.id === id2);
34791
+ if (!entry) throw new ContentError("unknown_template", `No published template "${id2}"`);
34792
+ return entry.versions.filter((v) => v.status === "published").reverse().map((v) => ({ date: (v.publishedAt ?? "").slice(0, 10), ...changelogEntry(id2, v.version) }));
34761
34793
  }
34762
- function resolveVersion(id2, version3) {
34763
- return getManifest(id2, version3).version;
34794
+ function resolveVersion(id2, version2) {
34795
+ return getManifest(id2, version2).version;
34764
34796
  }
34765
- function getShots(id2, version3) {
34766
- return shotsFor(id2, resolveVersion(id2, version3));
34797
+ function getShots(id2, version2) {
34798
+ return shotsFor(id2, resolveVersion(id2, version2));
34767
34799
  }
34768
- function shotsFor(id2, version3) {
34800
+ function shotsFor(id2, version2) {
34769
34801
  const index = join(contentRoot(), "shots.json");
34770
- if (existsSync(index)) return readJson(index)[`${id2}@${version3}`] ?? {};
34771
- const dir = join(contentRoot(), "..", "apps", "web", "public", "showcase", id2, version3);
34802
+ if (existsSync(index)) return readJson(index)[`${id2}@${version2}`] ?? {};
34803
+ const dir = join(contentRoot(), "..", "apps", "web", "public", "showcase", id2, version2);
34772
34804
  const shots = {};
34773
34805
  if (!existsSync(dir)) return shots;
34774
34806
  for (const app of readdirSync(dir)) {
@@ -34777,27 +34809,28 @@ function shotsFor(id2, version3) {
34777
34809
  const files = readdirSync(join(dir, app)).filter((f) => f.endsWith(".png"));
34778
34810
  for (const [key, re] of Object.entries({ desktopLight: /desktop-light/, desktopDark: /desktop-dark/, mobileLight: /mobile-light/, mobileDark: /mobile-dark/ })) {
34779
34811
  const f = cover[key] ?? files.find((x) => re.test(x));
34780
- if (f) shots[`${app}.${key}`] = `/showcase/${id2}/${version3}/${app}/${f}`;
34812
+ if (f) shots[`${app}.${key}`] = `/showcase/${id2}/${version2}/${app}/${f}`;
34781
34813
  }
34782
34814
  }
34783
34815
  return shots;
34784
34816
  }
34785
34817
  function listTemplates() {
34786
34818
  return registry2().templates.filter((t) => t.latest).map((t) => {
34787
- const version3 = t.latest;
34788
- const manifest = getManifest(t.id, version3);
34789
- const tokens = getTokens(t.id, version3);
34819
+ const version2 = t.latest;
34820
+ const manifest = getManifest(t.id, version2);
34821
+ const tokens = getTokens(t.id, version2);
34790
34822
  const light = tokens.light ?? tokens.dark ?? {};
34791
34823
  const swatch = (k) => typeof light[k] === "string" ? light[k] : "#000000";
34792
34824
  return {
34793
34825
  id: t.id,
34794
- version: version3,
34826
+ version: version2,
34795
34827
  name: manifest.name,
34796
34828
  description: manifest.description,
34797
34829
  tags: manifest.tags,
34798
34830
  versions: t.versions.filter((v) => v.status === "published").map((v) => v.version),
34831
+ updated: (t.versions.find((v) => v.version === version2)?.publishedAt ?? "").slice(0, 10),
34799
34832
  swatches: ["color.surface.default", "color.surface.subtle", "color.text.primary", "color.action.primary", "color.action.primaryHover"].map(swatch),
34800
- shots: shotsFor(t.id, version3)
34833
+ shots: shotsFor(t.id, version2)
34801
34834
  };
34802
34835
  });
34803
34836
  }
@@ -34819,17 +34852,15 @@ var init_src2 = __esm({
34819
34852
  });
34820
34853
 
34821
34854
  // ../content/src/prompts.ts
34822
- function specRef(id2, version3, delivery) {
34823
- if (delivery === "url") return `Spec: ${SITE_URL}/s/${id2}/${version3}/agent.md`;
34824
- if (delivery === "mcp") return "Read the spec with the Pigment MCP tools (get_design_system, get_component_spec).";
34825
- return `Spec: ./design-packages/${id2}-${version3}/dist/agent.md`;
34826
- }
34827
- function buildPrompt(id2, version3, mode2, delivery) {
34828
- const ref = `${id2}@${version3}`;
34829
- const spec = specRef(id2, version3, delivery);
34855
+ function specRef(id2, delivery) {
34856
+ if (delivery === "url") return `Spec: ${SITE_URL}/s/${id2}/agent.md`;
34857
+ return "Read the spec with the Pigment MCP tools (get_design_system, get_component_spec).";
34858
+ }
34859
+ function buildPrompt(id2, mode2, delivery) {
34860
+ const spec = specRef(id2, delivery);
34830
34861
  if (mode2 === "new") {
34831
34862
  return [
34832
- `Set up design system ${ref} in this codebase.`,
34863
+ `Set up design system ${id2} in this codebase.`,
34833
34864
  spec,
34834
34865
  "First inspect the framework, styling approach and component structure, then propose an installation plan and wait for my approval.",
34835
34866
  "Don't add libraries that don't fit the existing stack. Leave an AGENTS.md / CLAUDE.md pointer so future screens reuse the system.",
@@ -34838,7 +34869,7 @@ function buildPrompt(id2, version3, mode2, delivery) {
34838
34869
  }
34839
34870
  if (mode2 === "existing") {
34840
34871
  return [
34841
- `Apply design system ${ref} to this project.`,
34872
+ `Apply design system ${id2} to this project.`,
34842
34873
  spec,
34843
34874
  "Preserve features, routes, business logic, API contracts, tests and accessibility.",
34844
34875
  "Audit styles and components first, then propose a migration plan and wait for my approval. Restyle one screen as a sample and wait again.",
@@ -34846,7 +34877,7 @@ function buildPrompt(id2, version3, mode2, delivery) {
34846
34877
  ].join("\n");
34847
34878
  }
34848
34879
  return [
34849
- `Build this with the installed design system ${ref}.`,
34880
+ `Build this with the installed design system ${id2}.`,
34850
34881
  "Use its semantic tokens and component conventions from design-system/. Don't hard-code new style values.",
34851
34882
  "If the spec doesn't cover something, ask me with options before deciding."
34852
34883
  ].join("\n");
@@ -34866,7 +34897,7 @@ var init_package = __esm({
34866
34897
  "package.json"() {
34867
34898
  package_default = {
34868
34899
  name: "@pigment/mcp",
34869
- version: "0.3.0",
34900
+ version: "0.4.1",
34870
34901
  private: true,
34871
34902
  type: "module",
34872
34903
  description: "MCP server for Pigment design systems: tokens, rules, component specs and installation guides for your coding agent.",
@@ -34908,9 +34939,15 @@ function guard(fn) {
34908
34939
  throw e;
34909
34940
  }
34910
34941
  }
34911
- function specForMcp(systemId, pinned) {
34912
- const md = getAgentMd(systemId, pinned);
34913
- const index = getComponents(systemId, pinned).components;
34942
+ function updated(systemId) {
34943
+ const t = listTemplates().find((x) => x.id === systemId);
34944
+ if (!t) throw new ContentError("unknown_template", `No published system "${systemId}". Call list_design_systems.`);
34945
+ return t.updated;
34946
+ }
34947
+ function specForMcp(systemId) {
34948
+ const date5 = updated(systemId);
34949
+ const md = getAgentMd(systemId, CURRENT);
34950
+ const index = getComponents(systemId, CURRENT).components;
34914
34951
  const start = md.indexOf("\n## Components");
34915
34952
  const end = start < 0 ? -1 : md.indexOf("\n## ", start + 4);
34916
34953
  const specified = index.filter((c) => c.status !== "unsupported").map((c) => c.name);
@@ -34918,11 +34955,11 @@ function specForMcp(systemId, pinned) {
34918
34955
  const chapter = [
34919
34956
  "\n## Components\n",
34920
34957
  `Specified: ${specified.join(", ")}. Read each with get_component_spec before building it.`,
34921
- unsupported.length ? `Not specified in this version: ${unsupported.join(", ")} \u2014 compose them from tokens and say so in your report.` : ""
34958
+ unsupported.length ? `Not specified in this system: ${unsupported.join(", ")} \u2014 compose them from tokens and say so in your report.` : ""
34922
34959
  ].join("\n");
34923
34960
  const body = start < 0 ? md : md.slice(0, start) + chapter + (end < 0 ? "" : md.slice(end));
34924
34961
  return [
34925
- `> Delivered over MCP as ${systemId}@${pinned}. Record exactly \`${systemId}@${pinned}\` in design-system/VERSION and pass version "${pinned}" in every later call.`,
34962
+ `> Pigment system ${systemId}, updated ${date5}. Write \`${systemId}, updated ${date5}\` in design-system/VERSION.`,
34926
34963
  "> Where this document says tokens.css or tokens.json, call get_tokens and copy the file verbatim.",
34927
34964
  "",
34928
34965
  body
@@ -34934,7 +34971,7 @@ function createServer() {
34934
34971
  "list_design_systems",
34935
34972
  {
34936
34973
  title: "List design systems",
34937
- description: "Browse the published Pigment design systems with their tone, appearance, keywords and latest stable version. Use it to pick a system or check which versions exist.",
34974
+ description: "Browse the Pigment design systems with their tone, appearance, keywords and when each was last updated. Use it to pick a system.",
34938
34975
  inputSchema: external_exports.object({
34939
34976
  tone: external_exports.string().optional().describe("Filter by tone: minimal, natural, soft, editorial, brutalist, technical, playful, luxury"),
34940
34977
  appearance: external_exports.enum(["light", "dark"]).optional().describe("Only systems that support this appearance")
@@ -34942,67 +34979,76 @@ function createServer() {
34942
34979
  annotations: { readOnlyHint: true }
34943
34980
  },
34944
34981
  async ({ tone, appearance }) => guard(() => {
34945
- const items = listTemplates().filter((t) => !tone || t.tags.tone === tone).filter((t) => !appearance || t.tags.appearance.includes(appearance)).map((t) => ({ id: t.id, name: t.name, latest: t.version, versions: t.versions, description: t.description, tags: t.tags, page: `${SITE_URL}/s/${t.id}` }));
34946
- return items.length ? JSON.stringify(items, null, 2) : "No published system matches that filter.";
34982
+ const items = listTemplates().filter((t) => !tone || t.tags.tone === tone).filter((t) => !appearance || t.tags.appearance.includes(appearance)).map((t) => ({ id: t.id, name: t.name, updated: t.updated, description: t.description, tags: t.tags, page: `${SITE_URL}/s/${t.id}` }));
34983
+ return items.length ? JSON.stringify(items, null, 2) : "No system matches that filter.";
34947
34984
  })
34948
34985
  );
34949
34986
  server.registerTool(
34950
34987
  "get_design_system",
34951
34988
  {
34952
34989
  title: "Get design system",
34953
- description: "The full specification of one version (the latest unless the project pins one): character, the defining rules, color, type, spacing and motion rules, semantic and primitive token tables, setup and verification. Read it before changing any UI.",
34954
- inputSchema: external_exports.object({ id, version: version2 }),
34990
+ description: "The full specification of a system: character, the defining rules, color, type, spacing and motion rules, semantic and primitive token tables, setup and verification. Read it before changing any UI.",
34991
+ inputSchema: external_exports.object({ id }),
34955
34992
  annotations: { readOnlyHint: true }
34956
34993
  },
34957
- async (args) => guard(() => specForMcp(args.id, resolveVersion(args.id, args.version)))
34994
+ async (args) => guard(() => specForMcp(args.id))
34958
34995
  );
34959
34996
  server.registerTool(
34960
34997
  "get_tokens",
34961
34998
  {
34962
34999
  title: "Get token file",
34963
35000
  description: "The exact token values, light and dark, as tokens.css (CSS custom properties) or tokens.json. Copy the file into the project unchanged; never retype values.",
34964
- inputSchema: external_exports.object({ id, version: version2, format: external_exports.enum(["css", "json"]).default("css") }),
35001
+ inputSchema: external_exports.object({ id, format: external_exports.enum(["css", "json"]).default("css") }),
34965
35002
  annotations: { readOnlyHint: true }
34966
35003
  },
34967
- async (args) => guard(() => getTokenFile(args.id, args.version, args.format))
35004
+ async (args) => guard(() => getTokenFile(args.id, CURRENT, args.format))
34968
35005
  );
34969
35006
  server.registerTool(
34970
35007
  "get_component_spec",
34971
35008
  {
34972
35009
  title: "Get component spec",
34973
35010
  description: "Anatomy, variants, every state, accessibility and token mapping for one component (e.g. button, table, dialog). Read it before building or restyling that component.",
34974
- inputSchema: external_exports.object({ id, version: version2, component: external_exports.string().describe("Component name from the Components list in get_design_system") }),
35011
+ inputSchema: external_exports.object({ id, component: external_exports.string().describe("Component name from the Components list in get_design_system") }),
34975
35012
  annotations: { readOnlyHint: true }
34976
35013
  },
34977
- async (args) => guard(() => getComponentSpec(args.id, args.version, args.component).markdown)
35014
+ async (args) => guard(() => getComponentSpec(args.id, CURRENT, args.component).markdown)
34978
35015
  );
34979
35016
  server.registerTool(
34980
35017
  "get_installation_guide",
34981
35018
  {
34982
35019
  title: "Get installation guide",
34983
35020
  description: "Steps, approval points and checks for one scenario: new (set up a fresh project), existing (restyle a working app without changing behavior) or ongoing (build new screens in a project that already uses the system).",
34984
- inputSchema: external_exports.object({ id, version: version2, mode }),
35021
+ inputSchema: external_exports.object({ id, mode }),
34985
35022
  annotations: { readOnlyHint: true }
34986
35023
  },
34987
- async (args) => guard(() => getGuide(args.id, args.version, args.mode))
35024
+ async (args) => guard(() => getGuide(args.id, CURRENT, args.mode))
35025
+ );
35026
+ server.registerTool(
35027
+ "get_changelog",
35028
+ {
35029
+ title: "Get changelog",
35030
+ description: "What changed in a system, newest first, by date. Use it to update a project: apply only the entries dated after the date in the project's design-system/VERSION.",
35031
+ inputSchema: external_exports.object({ id }),
35032
+ annotations: { readOnlyHint: true }
35033
+ },
35034
+ async (args) => guard(() => JSON.stringify(getHistory(args.id), null, 2))
34988
35035
  );
34989
35036
  server.registerTool(
34990
35037
  "get_references",
34991
35038
  {
34992
35039
  title: "Get references",
34993
- description: "Screenshots of four reference apps (dashboard, store, blog, settings) restyled with this version by an agent \u2014 desktop and mobile, light and dark \u2014 plus links to the system page, spec and offline package.",
34994
- inputSchema: external_exports.object({ id, version: version2, app: external_exports.string().optional().describe("Only one app: dashboard, commerce, blog or settings") }),
35040
+ description: "Screenshots of four reference apps (dashboard, store, blog, settings) restyled with this system by an agent \u2014 desktop and mobile, light and dark \u2014 plus links to the system page and the spec.",
35041
+ inputSchema: external_exports.object({ id, app: external_exports.string().optional().describe("Only one app: dashboard, commerce, blog or settings") }),
34995
35042
  annotations: { readOnlyHint: true }
34996
35043
  },
34997
35044
  async (args) => guard(() => {
34998
- const pinned = resolveVersion(args.id, args.version);
34999
- const shots = Object.entries(getShots(args.id, pinned)).filter(([key]) => !args.app || key.startsWith(`${args.app}.`)).map(([key, path]) => ({ view: key, url: SITE_URL + path }));
35045
+ const shots = Object.entries(getShots(args.id, CURRENT)).filter(([key]) => !args.app || key.startsWith(`${args.app}.`)).map(([key, path]) => ({ view: key, url: SITE_URL + path }));
35000
35046
  return JSON.stringify(
35001
35047
  {
35002
- system: `${args.id}@${pinned}`,
35003
- page: `${SITE_URL}/s/${args.id}/${pinned}`,
35004
- spec: `${SITE_URL}/s/${args.id}/${pinned}/agent.md`,
35005
- package: `${SITE_URL}/download/${args.id}/${pinned}`,
35048
+ system: args.id,
35049
+ updated: updated(args.id),
35050
+ page: `${SITE_URL}/s/${args.id}`,
35051
+ spec: `${SITE_URL}/s/${args.id}/agent.md`,
35006
35052
  screenshots: shots
35007
35053
  },
35008
35054
  null,
@@ -35014,29 +35060,22 @@ function createServer() {
35014
35060
  "apply",
35015
35061
  {
35016
35062
  title: "Apply a design system",
35017
- description: "Install or apply a pinned Pigment design system with a plan, an approval step and a verification report.",
35063
+ description: "Install or apply a Pigment design system with a plan, an approval step and a verification report.",
35018
35064
  argsSchema: external_exports.object({
35019
35065
  id: external_exports.string().describe('Design system id, e.g. "margin-notes"'),
35020
- version: external_exports.string().optional().describe("Version to pin; defaults to the latest stable version"),
35021
35066
  mode: external_exports.string().optional().describe("new, existing (default) or ongoing")
35022
35067
  })
35023
35068
  },
35024
- ({ id: systemId, version: wanted, mode: scenario }) => {
35025
- const pinned = getManifest(systemId, wanted ?? "latest").version;
35069
+ ({ id: systemId, mode: scenario }) => {
35026
35070
  const m = mode.safeParse(scenario ?? "existing");
35027
35071
  return {
35028
- messages: [
35029
- {
35030
- role: "user",
35031
- content: { type: "text", text: buildPrompt(systemId, pinned, m.success ? m.data : "existing", "mcp") }
35032
- }
35033
- ]
35072
+ messages: [{ role: "user", content: { type: "text", text: buildPrompt(systemId, m.success ? m.data : "existing", "mcp") } }]
35034
35073
  };
35035
35074
  }
35036
35075
  );
35037
35076
  return server;
35038
35077
  }
35039
- var SERVER_VERSION, id, version2, INSTRUCTIONS, mode;
35078
+ var SERVER_VERSION, CURRENT, id, mode, INSTRUCTIONS;
35040
35079
  var init_server = __esm({
35041
35080
  "src/server.ts"() {
35042
35081
  "use strict";
@@ -35046,15 +35085,15 @@ var init_server = __esm({
35046
35085
  init_zod();
35047
35086
  init_package();
35048
35087
  SERVER_VERSION = package_default.version;
35088
+ CURRENT = "latest";
35049
35089
  id = external_exports.string().describe('Design system id, e.g. "margin-notes" (see list_design_systems)');
35050
- version2 = external_exports.string().default("latest").describe('Leave out (or "latest") when adding a system to a project. If the project has design-system/VERSION (e.g. margin-notes@1.0.0), pass that exact version.');
35090
+ mode = external_exports.enum(["new", "existing", "ongoing"]);
35051
35091
  INSTRUCTIONS = [
35052
- "Pigment serves versioned design systems. Published versions never change.",
35053
- "Before UI work, check the project for design-system/VERSION. If it exists, use exactly that id and version in every call.",
35054
- "If it does not, the user is adding a system: omit the version (latest), then record the exact id@version that get_design_system reports in design-system/VERSION.",
35055
- "Only change the recorded version when the user asks to upgrade."
35092
+ "Pigment serves design systems for coding agents. Every tool returns the current system; there is nothing to choose or pin.",
35093
+ "Applying a system copies it into the project's design-system/ folder (tokens, component notes and a VERSION file naming the system and its update date).",
35094
+ "From then on design-system/ is the project's source of truth: build new screens from it and don't re-fetch tokens unless the user asks to update.",
35095
+ "To update, call get_changelog, apply only the entries dated after the date in design-system/VERSION, and then write the new date."
35056
35096
  ].join("\n");
35057
- mode = external_exports.enum(["new", "existing", "ongoing"]);
35058
35097
  }
35059
35098
  });
35060
35099
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pigment-mcp",
3
- "version": "0.3.0",
3
+ "version": "0.4.1",
4
4
  "description": "MCP server for Pigment design systems: tokens, rules, component specs and installation guides for your coding agent.",
5
5
  "type": "module",
6
6
  "bin": {