pigment-mcp 0.2.1 → 0.4.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.
Files changed (3) hide show
  1. package/README.md +7 -7
  2. package/cli.js +150 -102
  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
 
@@ -33,7 +32,7 @@ Prefer nothing to install? The same server runs remotely: `claude mcp add --tran
33
32
 
34
33
  Start a new session and just ask:
35
34
 
36
- > Apply the Pigment design system margin-notes@1.0.0 to this project.
35
+ > Apply the Pigment design system margin-notes to this project.
37
36
 
38
37
  Or run the `apply` prompt (Claude Code: `/mcp__pigment__apply`), which adds the working agreement: audit first,
39
38
  propose a plan, restyle one sample screen, then report build and visual checks.
@@ -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"));
34758
34764
  }
34759
- function getTokenFile(id2, version3, format) {
34760
- return readArtifact(id2, version3, join("dist", `tokens.${format}`));
34765
+ function getTokenFile(id2, version2, format) {
34766
+ return readArtifact(id2, version2, join("dist", `tokens.${format}`));
34761
34767
  }
34762
- function resolveVersion(id2, version3) {
34763
- return getManifest(id2, version3).version;
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) }));
34793
+ }
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,16 @@ 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`;
34855
+ function specRef(id2, delivery) {
34856
+ if (delivery === "url") return `Spec: ${SITE_URL}/s/${id2}/agent.md`;
34824
34857
  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`;
34858
+ return `Spec: dist/agent.md in the ${id2} package, unzipped under ./design-packages/`;
34826
34859
  }
34827
- function buildPrompt(id2, version3, mode2, delivery) {
34828
- const ref = `${id2}@${version3}`;
34829
- const spec = specRef(id2, version3, delivery);
34860
+ function buildPrompt(id2, mode2, delivery) {
34861
+ const spec = specRef(id2, delivery);
34830
34862
  if (mode2 === "new") {
34831
34863
  return [
34832
- `Set up design system ${ref} in this codebase.`,
34864
+ `Set up design system ${id2} in this codebase.`,
34833
34865
  spec,
34834
34866
  "First inspect the framework, styling approach and component structure, then propose an installation plan and wait for my approval.",
34835
34867
  "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 +34870,7 @@ function buildPrompt(id2, version3, mode2, delivery) {
34838
34870
  }
34839
34871
  if (mode2 === "existing") {
34840
34872
  return [
34841
- `Apply design system ${ref} to this project.`,
34873
+ `Apply design system ${id2} to this project.`,
34842
34874
  spec,
34843
34875
  "Preserve features, routes, business logic, API contracts, tests and accessibility.",
34844
34876
  "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 +34878,7 @@ function buildPrompt(id2, version3, mode2, delivery) {
34846
34878
  ].join("\n");
34847
34879
  }
34848
34880
  return [
34849
- `Build this with the installed design system ${ref}.`,
34881
+ `Build this with the installed design system ${id2}.`,
34850
34882
  "Use its semantic tokens and component conventions from design-system/. Don't hard-code new style values.",
34851
34883
  "If the spec doesn't cover something, ask me with options before deciding."
34852
34884
  ].join("\n");
@@ -34866,7 +34898,7 @@ var init_package = __esm({
34866
34898
  "package.json"() {
34867
34899
  package_default = {
34868
34900
  name: "@pigment/mcp",
34869
- version: "0.2.1",
34901
+ version: "0.4.0",
34870
34902
  private: true,
34871
34903
  type: "module",
34872
34904
  description: "MCP server for Pigment design systems: tokens, rules, component specs and installation guides for your coding agent.",
@@ -34908,9 +34940,15 @@ function guard(fn) {
34908
34940
  throw e;
34909
34941
  }
34910
34942
  }
34911
- function specForMcp(systemId, pinned) {
34912
- const md = getAgentMd(systemId, pinned);
34913
- const index = getComponents(systemId, pinned).components;
34943
+ function updated(systemId) {
34944
+ const t = listTemplates().find((x) => x.id === systemId);
34945
+ if (!t) throw new ContentError("unknown_template", `No published system "${systemId}". Call list_design_systems.`);
34946
+ return t.updated;
34947
+ }
34948
+ function specForMcp(systemId) {
34949
+ const date5 = updated(systemId);
34950
+ const md = getAgentMd(systemId, CURRENT);
34951
+ const index = getComponents(systemId, CURRENT).components;
34914
34952
  const start = md.indexOf("\n## Components");
34915
34953
  const end = start < 0 ? -1 : md.indexOf("\n## ", start + 4);
34916
34954
  const specified = index.filter((c) => c.status !== "unsupported").map((c) => c.name);
@@ -34918,22 +34956,23 @@ function specForMcp(systemId, pinned) {
34918
34956
  const chapter = [
34919
34957
  "\n## Components\n",
34920
34958
  `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.` : ""
34959
+ unsupported.length ? `Not specified in this system: ${unsupported.join(", ")} \u2014 compose them from tokens and say so in your report.` : ""
34922
34960
  ].join("\n");
34923
34961
  const body = start < 0 ? md : md.slice(0, start) + chapter + (end < 0 ? "" : md.slice(end));
34924
34962
  return [
34925
- `> Delivered over MCP as ${systemId}@${pinned}. Where this document says tokens.css or tokens.json, call get_tokens and copy the file verbatim.`,
34963
+ `> Pigment system ${systemId}, updated ${date5}. Write \`${systemId}, updated ${date5}\` in design-system/VERSION.`,
34964
+ "> Where this document says tokens.css or tokens.json, call get_tokens and copy the file verbatim.",
34926
34965
  "",
34927
34966
  body
34928
34967
  ].join("\n");
34929
34968
  }
34930
34969
  function createServer() {
34931
- const server = new McpServer({ name: "pigment", title: "Pigment design systems", version: SERVER_VERSION });
34970
+ const server = new McpServer({ name: "pigment", title: "Pigment design systems", version: SERVER_VERSION }, { instructions: INSTRUCTIONS });
34932
34971
  server.registerTool(
34933
34972
  "list_design_systems",
34934
34973
  {
34935
34974
  title: "List design systems",
34936
- 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.",
34975
+ description: "Browse the Pigment design systems with their tone, appearance, keywords and when each was last updated. Use it to pick a system.",
34937
34976
  inputSchema: external_exports.object({
34938
34977
  tone: external_exports.string().optional().describe("Filter by tone: minimal, natural, soft, editorial, brutalist, technical, playful, luxury"),
34939
34978
  appearance: external_exports.enum(["light", "dark"]).optional().describe("Only systems that support this appearance")
@@ -34941,67 +34980,77 @@ function createServer() {
34941
34980
  annotations: { readOnlyHint: true }
34942
34981
  },
34943
34982
  async ({ tone, appearance }) => guard(() => {
34944
- 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}` }));
34945
- return items.length ? JSON.stringify(items, null, 2) : "No published system matches that filter.";
34983
+ 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}` }));
34984
+ return items.length ? JSON.stringify(items, null, 2) : "No system matches that filter.";
34946
34985
  })
34947
34986
  );
34948
34987
  server.registerTool(
34949
34988
  "get_design_system",
34950
34989
  {
34951
34990
  title: "Get design system",
34952
- description: "The full specification of one pinned version: character, the defining rules, color, type, spacing and motion rules, semantic and primitive token tables, setup and verification. Read it before changing any UI.",
34953
- inputSchema: external_exports.object({ id, version: version2 }),
34991
+ 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.",
34992
+ inputSchema: external_exports.object({ id }),
34954
34993
  annotations: { readOnlyHint: true }
34955
34994
  },
34956
- async (args) => guard(() => specForMcp(args.id, resolveVersion(args.id, args.version)))
34995
+ async (args) => guard(() => specForMcp(args.id))
34957
34996
  );
34958
34997
  server.registerTool(
34959
34998
  "get_tokens",
34960
34999
  {
34961
35000
  title: "Get token file",
34962
35001
  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.",
34963
- inputSchema: external_exports.object({ id, version: version2, format: external_exports.enum(["css", "json"]).default("css") }),
35002
+ inputSchema: external_exports.object({ id, format: external_exports.enum(["css", "json"]).default("css") }),
34964
35003
  annotations: { readOnlyHint: true }
34965
35004
  },
34966
- async (args) => guard(() => getTokenFile(args.id, args.version, args.format))
35005
+ async (args) => guard(() => getTokenFile(args.id, CURRENT, args.format))
34967
35006
  );
34968
35007
  server.registerTool(
34969
35008
  "get_component_spec",
34970
35009
  {
34971
35010
  title: "Get component spec",
34972
35011
  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.",
34973
- inputSchema: external_exports.object({ id, version: version2, component: external_exports.string().describe("Component name from the Components list in get_design_system") }),
35012
+ inputSchema: external_exports.object({ id, component: external_exports.string().describe("Component name from the Components list in get_design_system") }),
34974
35013
  annotations: { readOnlyHint: true }
34975
35014
  },
34976
- async (args) => guard(() => getComponentSpec(args.id, args.version, args.component).markdown)
35015
+ async (args) => guard(() => getComponentSpec(args.id, CURRENT, args.component).markdown)
34977
35016
  );
34978
35017
  server.registerTool(
34979
35018
  "get_installation_guide",
34980
35019
  {
34981
35020
  title: "Get installation guide",
34982
35021
  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).",
34983
- inputSchema: external_exports.object({ id, version: version2, mode }),
35022
+ inputSchema: external_exports.object({ id, mode }),
34984
35023
  annotations: { readOnlyHint: true }
34985
35024
  },
34986
- async (args) => guard(() => getGuide(args.id, args.version, args.mode))
35025
+ async (args) => guard(() => getGuide(args.id, CURRENT, args.mode))
35026
+ );
35027
+ server.registerTool(
35028
+ "get_changelog",
35029
+ {
35030
+ title: "Get changelog",
35031
+ 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.",
35032
+ inputSchema: external_exports.object({ id }),
35033
+ annotations: { readOnlyHint: true }
35034
+ },
35035
+ async (args) => guard(() => JSON.stringify(getHistory(args.id), null, 2))
34987
35036
  );
34988
35037
  server.registerTool(
34989
35038
  "get_references",
34990
35039
  {
34991
35040
  title: "Get references",
34992
- 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.",
34993
- inputSchema: external_exports.object({ id, version: version2, app: external_exports.string().optional().describe("Only one app: dashboard, commerce, blog or settings") }),
35041
+ 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, spec and offline package.",
35042
+ inputSchema: external_exports.object({ id, app: external_exports.string().optional().describe("Only one app: dashboard, commerce, blog or settings") }),
34994
35043
  annotations: { readOnlyHint: true }
34995
35044
  },
34996
35045
  async (args) => guard(() => {
34997
- const pinned = resolveVersion(args.id, args.version);
34998
- 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 }));
35046
+ 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 }));
34999
35047
  return JSON.stringify(
35000
35048
  {
35001
- system: `${args.id}@${pinned}`,
35002
- page: `${SITE_URL}/s/${args.id}/${pinned}`,
35003
- spec: `${SITE_URL}/s/${args.id}/${pinned}/agent.md`,
35004
- package: `${SITE_URL}/download/${args.id}/${pinned}`,
35049
+ system: args.id,
35050
+ updated: updated(args.id),
35051
+ page: `${SITE_URL}/s/${args.id}`,
35052
+ spec: `${SITE_URL}/s/${args.id}/agent.md`,
35053
+ package: `${SITE_URL}/download/${args.id}`,
35005
35054
  screenshots: shots
35006
35055
  },
35007
35056
  null,
@@ -35013,29 +35062,22 @@ function createServer() {
35013
35062
  "apply",
35014
35063
  {
35015
35064
  title: "Apply a design system",
35016
- description: "Install or apply a pinned Pigment design system with a plan, an approval step and a verification report.",
35065
+ description: "Install or apply a Pigment design system with a plan, an approval step and a verification report.",
35017
35066
  argsSchema: external_exports.object({
35018
35067
  id: external_exports.string().describe('Design system id, e.g. "margin-notes"'),
35019
- version: external_exports.string().optional().describe("Version to pin; defaults to the latest stable version"),
35020
35068
  mode: external_exports.string().optional().describe("new, existing (default) or ongoing")
35021
35069
  })
35022
35070
  },
35023
- ({ id: systemId, version: wanted, mode: scenario }) => {
35024
- const pinned = getManifest(systemId, wanted ?? "latest").version;
35071
+ ({ id: systemId, mode: scenario }) => {
35025
35072
  const m = mode.safeParse(scenario ?? "existing");
35026
35073
  return {
35027
- messages: [
35028
- {
35029
- role: "user",
35030
- content: { type: "text", text: buildPrompt(systemId, pinned, m.success ? m.data : "existing", "mcp") }
35031
- }
35032
- ]
35074
+ messages: [{ role: "user", content: { type: "text", text: buildPrompt(systemId, m.success ? m.data : "existing", "mcp") } }]
35033
35075
  };
35034
35076
  }
35035
35077
  );
35036
35078
  return server;
35037
35079
  }
35038
- var SERVER_VERSION, id, version2, mode;
35080
+ var SERVER_VERSION, CURRENT, id, mode, INSTRUCTIONS;
35039
35081
  var init_server = __esm({
35040
35082
  "src/server.ts"() {
35041
35083
  "use strict";
@@ -35045,9 +35087,15 @@ var init_server = __esm({
35045
35087
  init_zod();
35046
35088
  init_package();
35047
35089
  SERVER_VERSION = package_default.version;
35090
+ CURRENT = "latest";
35048
35091
  id = external_exports.string().describe('Design system id, e.g. "margin-notes" (see list_design_systems)');
35049
- version2 = external_exports.string().describe('Exact version such as "1.0.0". Pin the version a project installed; "latest" only when starting fresh.');
35050
35092
  mode = external_exports.enum(["new", "existing", "ongoing"]);
35093
+ INSTRUCTIONS = [
35094
+ "Pigment serves design systems for coding agents. Every tool returns the current system; there is nothing to choose or pin.",
35095
+ "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).",
35096
+ "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.",
35097
+ "To update, call get_changelog, apply only the entries dated after the date in design-system/VERSION, and then write the new date."
35098
+ ].join("\n");
35051
35099
  }
35052
35100
  });
35053
35101
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pigment-mcp",
3
- "version": "0.2.1",
3
+ "version": "0.4.0",
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": {