@teamix-evo/mcp 0.3.0 → 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 (29) hide show
  1. package/README.md +1 -1
  2. package/dist/cli.js +161 -162
  3. package/dist/cli.js.map +1 -1
  4. package/dist/data/adr/0001-three-layer-alignment.md +54 -0
  5. package/dist/data/adr/0002-package-naming.md +50 -0
  6. package/dist/data/adr/0003-update-strategy-tri-state.md +62 -0
  7. package/dist/data/adr/0004-cli-command-structure.md +61 -0
  8. package/dist/data/adr/0005-ui-no-variant.md +67 -0
  9. package/dist/data/adr/0006-ui-upgrade-no-baseline.md +67 -0
  10. package/dist/data/adr/0007-governance-docs-at-root.md +62 -0
  11. package/dist/data/adr/0008-eslint-visual-rules-warn-baseline.md +110 -0
  12. package/dist/data/adr/0009-registry-mcp-protocol-layer.md +87 -0
  13. package/dist/data/adr/0010-design-default-and-variants.md +319 -0
  14. package/dist/data/adr/0011-mcp-single-package-multi-group.md +169 -0
  15. package/dist/data/adr/0012-lint-shared-core.md +215 -0
  16. package/dist/data/adr/0013-skills-source-mirror.md +154 -0
  17. package/dist/data/adr/0014-ui-biz-ui-templates-tier.md +274 -0
  18. package/dist/data/adr/0015-skill-description-trigger-contract.md +122 -0
  19. package/dist/data/adr/0016-design-md-brand-charter.md +93 -0
  20. package/dist/data/adr/0017-mcp-design-group.md +107 -0
  21. package/dist/data/adr/0018-ai-context-routing.md +112 -0
  22. package/dist/data/adr/0019-project-upgrade-flow.md +156 -0
  23. package/dist/data/adr/0020-design-to-tokens-skill-fusion.md +139 -0
  24. package/dist/data/adr/README.md +70 -0
  25. package/dist/data/adr/_template.md +36 -0
  26. package/dist/index.d.ts +64 -59
  27. package/dist/index.js +168 -170
  28. package/dist/index.js.map +1 -1
  29. package/package.json +1 -1
package/README.md CHANGED
@@ -18,7 +18,7 @@
18
18
  | Group | Status | Tools | Naming |
19
19
  | --- | --- | --- | --- |
20
20
  | **registry** | ✅ shipping | `list_components` / `get_component_meta` / `find_components` | unprefixed (historical exception, see ADR 0011 §5) |
21
- | **design** | ✅ shipping | `design_list_principles` / `design_get_tokens` / `design_list_patterns` / `design_get_pattern` / `design_get_brand` | `<group>_<verb>_<noun>` |
21
+ | **tokens** | ✅ shipping | `tokens_get` / `tokens_list` / `tokens_search` | `<group>_<verb>_<noun>` |
22
22
  | **skills** | ✅ shipping | `skills_list` / `skills_get` / `skills_find` | `<group>_<verb>_<noun>` |
23
23
  | **adr** | ✅ shipping | `adr_list` / `adr_get` / `adr_find` | `<group>_<verb>_<noun>` |
24
24
  | **scenario** | ⏸ v0.8 | `scenario_list` / `scenario_get_pack` | `<group>_<verb>_<noun>` |
package/dist/cli.js CHANGED
@@ -40,10 +40,15 @@ function resolveManifestPath(startDir = process.cwd()) {
40
40
  }
41
41
  throw new Error(
42
42
  [
43
- "Could not locate @teamix-evo/ui/manifest.json.",
44
- `Tried env TEAMIX_EVO_UI_MANIFEST + walking up from ${startDir}.`,
45
- "Either install @teamix-evo/ui in the consumer project, or set TEAMIX_EVO_UI_MANIFEST to an absolute path."
46
- ].join(" ")
43
+ "Could not locate @teamix-evo/ui/manifest.json \u2014 the UI registry is not installed in this project.",
44
+ "",
45
+ "Fix one of:",
46
+ ` 1. Install the package: pnpm add @teamix-evo/ui (or npm/yarn equivalent)`,
47
+ ` 2. Point env at an existing manifest: export TEAMIX_EVO_UI_MANIFEST=/abs/path/to/manifest.json`,
48
+ "",
49
+ `Searched: $TEAMIX_EVO_UI_MANIFEST, then walked up from ${startDir} looking for node_modules/@teamix-evo/ui/manifest.json (16 levels).`,
50
+ "Note: MCP currently has no online fallback \u2014 it does not fetch from the npm registry / unpkg."
51
+ ].join("\n")
47
52
  );
48
53
  }
49
54
  function loadManifest(path) {
@@ -238,11 +243,18 @@ import { z as z2 } from "zod";
238
243
  // src/adr-loader.ts
239
244
  import { existsSync as existsSync2, readFileSync as readFileSync2, readdirSync } from "fs";
240
245
  import { dirname as dirname2, join as join2, resolve as resolve2 } from "path";
246
+ import { fileURLToPath } from "url";
241
247
  function resolveAdrRoot(startDir = process.cwd()) {
242
248
  const envPath = process.env.TEAMIX_EVO_ADR_ROOT;
243
249
  if (envPath && existsSync2(envPath)) {
244
250
  return envPath;
245
251
  }
252
+ try {
253
+ const here = dirname2(fileURLToPath(import.meta.url));
254
+ const bundled = join2(here, "data", "adr");
255
+ if (existsSync2(bundled)) return bundled;
256
+ } catch {
257
+ }
246
258
  let dir = resolve2(startDir);
247
259
  for (let i = 0; i < 16; i++) {
248
260
  const candidates = [
@@ -259,8 +271,8 @@ function resolveAdrRoot(startDir = process.cwd()) {
259
271
  throw new Error(
260
272
  [
261
273
  "Could not locate ADR directory.",
262
- `Tried env TEAMIX_EVO_ADR_ROOT + walking up from ${startDir} for docs/adr/ or .teamix-evo/adr/.`,
263
- "Either run from a teamix-evo monorepo, or set TEAMIX_EVO_ADR_ROOT to an absolute path."
274
+ `Tried env TEAMIX_EVO_ADR_ROOT, mcp-bundled snapshot, and walked up from ${startDir} for docs/adr/ or .teamix-evo/adr/.`,
275
+ "Either install @teamix-evo/mcp (carries ADR snapshot) or set TEAMIX_EVO_ADR_ROOT to an absolute path."
264
276
  ].join(" ")
265
277
  );
266
278
  }
@@ -544,8 +556,8 @@ function resolveSkillsRoot(startDir = process.cwd()) {
544
556
  throw new Error(
545
557
  [
546
558
  "Could not locate @teamix-evo/skills root.",
547
- `Tried env TEAMIX_EVO_SKILLS_ROOT + walking up from ${startDir}.`,
548
- "Either install @teamix-evo/skills or run from a monorepo, or set TEAMIX_EVO_SKILLS_ROOT to an absolute path."
559
+ `Tried env TEAMIX_EVO_SKILLS_ROOT and walked up from ${startDir} for packages/skills/ or node_modules/@teamix-evo/skills/.`,
560
+ "Install @teamix-evo/skills as a devDependency in your project (the scaffold adds it automatically)."
549
561
  ].join(" ")
550
562
  );
551
563
  }
@@ -795,20 +807,20 @@ function createSkillsGroup(opts = {}) {
795
807
  };
796
808
  }
797
809
 
798
- // src/groups/design.ts
810
+ // src/groups/tokens.ts
799
811
  import { z as z4 } from "zod";
800
812
 
801
- // src/design-loader.ts
802
- import { existsSync as existsSync4, readFileSync as readFileSync4, readdirSync as readdirSync3, statSync as statSync2 } from "fs";
813
+ // src/tokens-loader.ts
814
+ import { existsSync as existsSync4, readFileSync as readFileSync4 } from "fs";
803
815
  import { dirname as dirname4, join as join4, resolve as resolve4 } from "path";
804
- function resolveDesignRoot(startDir = process.cwd()) {
805
- const envPath = process.env.TEAMIX_EVO_DESIGN_ROOT;
816
+ function resolveTokensRoot(startDir = process.cwd()) {
817
+ const envPath = process.env.TEAMIX_EVO_TOKENS_ROOT;
806
818
  if (envPath && existsSync4(envPath)) {
807
819
  return envPath;
808
820
  }
809
821
  let dir = resolve4(startDir);
810
822
  for (let i = 0; i < 16; i++) {
811
- const candidate = join4(dir, ".teamix-evo", "design");
823
+ const candidate = join4(dir, ".teamix-evo");
812
824
  if (existsSync4(candidate)) return candidate;
813
825
  const parent = dirname4(dir);
814
826
  if (parent === dir) break;
@@ -816,11 +828,11 @@ function resolveDesignRoot(startDir = process.cwd()) {
816
828
  }
817
829
  return null;
818
830
  }
819
- function loadDesign(rootDir) {
820
- const root = rootDir ?? resolveDesignRoot();
831
+ function loadTokens(rootDir) {
832
+ const root = rootDir ?? resolveTokensRoot();
821
833
  if (!root) return null;
822
834
  let variant = null;
823
- const lockPath = join4(root, "pack.lock.json");
835
+ const lockPath = join4(root, "tokens-lock.json");
824
836
  if (existsSync4(lockPath)) {
825
837
  try {
826
838
  const raw = JSON.parse(readFileSync4(lockPath, "utf-8"));
@@ -831,47 +843,17 @@ function loadDesign(rootDir) {
831
843
  }
832
844
  return { rootDir: root, variant };
833
845
  }
834
- var PRINCIPLE_HEADING_RE = /^#{2,3}\s+(P\d+)\s*[·:]\s*(.+?)\s*$/;
835
- function readPrinciples(loaded) {
836
- const filePath = join4(loaded.rootDir, "philosophy", "principles.md");
837
- if (!existsSync4(filePath)) {
838
- return {
839
- principles: [],
840
- sourcePath: null,
841
- note: "philosophy/principles.md not found in this project. Run `teamix-evo design init <variant>` to install it."
842
- };
843
- }
844
- const text = readFileSync4(filePath, "utf-8");
845
- const lines = text.split(/\r?\n/);
846
- const out = [];
847
- for (let i = 0; i < lines.length; i++) {
848
- const m = lines[i].match(PRINCIPLE_HEADING_RE);
849
- if (!m) continue;
850
- let def = "";
851
- for (let j = i + 1; j < lines.length && j < i + 8; j++) {
852
- const candidate = lines[j].trim();
853
- if (!candidate) continue;
854
- if (/^#{1,6}\s/.test(candidate)) break;
855
- def = candidate.replace(/^[*_>\s-]+/, "").trim();
856
- break;
857
- }
858
- out.push({ id: m[1], name: m[2].trim(), oneLineDef: def });
859
- }
860
- return { principles: out, sourcePath: filePath };
861
- }
862
846
  function resolveTokensDir(loaded) {
863
847
  const projectRoot = dirname4(loaded.rootDir);
864
- const lifted = join4(projectRoot, "tokens");
865
- if (existsSync4(lifted)) return lifted;
866
- const legacy = join4(loaded.rootDir, "foundations", "tokens");
867
- if (existsSync4(legacy)) return legacy;
848
+ const rootTokens = join4(projectRoot, "tokens");
849
+ if (existsSync4(rootTokens)) return rootTokens;
868
850
  return null;
869
851
  }
870
852
  function readTokens(loaded) {
871
853
  const tokensDir = resolveTokensDir(loaded);
872
854
  const out = { sources: [] };
873
855
  if (!tokensDir) {
874
- out.note = ".teamix-evo/tokens/ not found in this project (and no legacy foundations/tokens/).";
856
+ out.note = "tokens/ not found at project root.";
875
857
  return out;
876
858
  }
877
859
  const tryJson = (rel) => {
@@ -891,169 +873,186 @@ function readTokens(loaded) {
891
873
  return readFileSync4(fp, "utf-8");
892
874
  };
893
875
  out.base = tryJson("base.tokens.json");
894
- out.semantic = tryJson("semantic.tokens.json");
895
876
  out.themeCss = tryText("tokens.theme.css");
896
877
  out.overridesCss = tryText("tokens.overrides.css");
897
878
  return out;
898
879
  }
899
- var VARIANT_PREFIX_RE = /^(cloud|opentrek|uni-manager|enterprise)-/i;
900
- function readPatternIndex(loaded) {
901
- const patternsDir = join4(loaded.rootDir, "patterns");
902
- if (!existsSync4(patternsDir)) return [];
880
+ var W3C_KEYS = /* @__PURE__ */ new Set(["$value", "$type", "$description", "$extensions"]);
881
+ function isPlainObject(v) {
882
+ return typeof v === "object" && v !== null && !Array.isArray(v);
883
+ }
884
+ function flattenTokens(base) {
885
+ if (!isPlainObject(base)) return [];
903
886
  const out = [];
904
- for (const name of readdirSync3(patternsDir)) {
905
- if (!name.endsWith(".md")) continue;
906
- const fp = join4(patternsDir, name);
907
- try {
908
- if (!statSync2(fp).isFile()) continue;
909
- } catch {
910
- continue;
887
+ for (const [category, group] of Object.entries(base)) {
888
+ if (category.startsWith("$")) continue;
889
+ if (!isPlainObject(group)) continue;
890
+ for (const [name, leaf] of Object.entries(group)) {
891
+ if (name.startsWith("$")) continue;
892
+ if (!isPlainObject(leaf)) continue;
893
+ const type = typeof leaf.$type === "string" ? leaf.$type : void 0;
894
+ const values = {};
895
+ let description = typeof leaf.$description === "string" ? leaf.$description : void 0;
896
+ if (typeof leaf.$value === "string") {
897
+ values.default = leaf.$value;
898
+ }
899
+ for (const [modeKey, modeVal] of Object.entries(leaf)) {
900
+ if (W3C_KEYS.has(modeKey)) continue;
901
+ if (!isPlainObject(modeVal)) continue;
902
+ if (typeof modeVal.$value === "string") {
903
+ values[modeKey] = modeVal.$value;
904
+ }
905
+ if (!description && typeof modeVal.$description === "string") {
906
+ description = modeVal.$description;
907
+ }
908
+ }
909
+ if (Object.keys(values).length === 0) continue;
910
+ out.push({
911
+ category,
912
+ name,
913
+ ...type ? { type } : {},
914
+ values,
915
+ ...description ? { description } : {}
916
+ });
911
917
  }
912
- const stem = name.replace(/\.md$/, "");
913
- const text = readFileSync4(fp, "utf-8");
914
- const titleMatch = text.match(/^#\s+(.+?)\s*$/m);
915
- out.push({
916
- id: stem,
917
- title: titleMatch ? titleMatch[1] : stem,
918
- sourcePath: fp,
919
- variantSpecific: VARIANT_PREFIX_RE.test(stem)
920
- });
921
918
  }
922
- return out.sort((a, b) => a.id.localeCompare(b.id));
919
+ return out;
923
920
  }
924
- function readPatternContent(loaded, id) {
925
- const stem = id.replace(/\.md$/, "");
926
- const fp = join4(loaded.rootDir, "patterns", `${stem}.md`);
927
- if (!existsSync4(fp)) return null;
921
+ function listTokens(loaded) {
922
+ const snapshot = readTokens(loaded);
923
+ if (!snapshot.base) {
924
+ return {
925
+ variant: loaded.variant,
926
+ tokens: [],
927
+ sources: snapshot.sources,
928
+ note: snapshot.note ?? "base.tokens.json not found."
929
+ };
930
+ }
928
931
  return {
929
- id: stem,
930
- sourcePath: fp,
931
- content: readFileSync4(fp, "utf-8")
932
+ variant: loaded.variant,
933
+ tokens: flattenTokens(snapshot.base),
934
+ sources: snapshot.sources
932
935
  };
933
936
  }
934
- function readBrand(loaded) {
935
- const brandDir = join4(loaded.rootDir, "brand");
936
- const out = {};
937
- if (!existsSync4(brandDir)) {
938
- out.note = "brand/ not present \u2014 this design variant does not ship brand-specific tone / voice / examples.";
939
- return out;
937
+ function searchTokens(loaded, query, limit = 20) {
938
+ const list = listTokens(loaded);
939
+ const q = query.trim().toLowerCase();
940
+ if (!q) {
941
+ return {
942
+ variant: list.variant,
943
+ query,
944
+ matches: [],
945
+ sources: list.sources,
946
+ note: "query is empty."
947
+ };
940
948
  }
941
- for (const key of ["tone", "voice", "examples"]) {
942
- const fp = join4(brandDir, `${key}.md`);
943
- if (existsSync4(fp)) {
944
- out[key] = { sourcePath: fp, content: readFileSync4(fp, "utf-8") };
945
- }
949
+ if (list.note && list.tokens.length === 0) {
950
+ return {
951
+ variant: list.variant,
952
+ query,
953
+ matches: [],
954
+ sources: list.sources,
955
+ note: list.note
956
+ };
946
957
  }
947
- return out;
958
+ const hits = list.tokens.filter((t) => {
959
+ if (t.category.toLowerCase().includes(q)) return true;
960
+ if (t.name.toLowerCase().includes(q)) return true;
961
+ if (t.description?.toLowerCase().includes(q)) return true;
962
+ return Object.values(t.values).some((v) => v.toLowerCase().includes(q));
963
+ });
964
+ hits.sort((a, b) => {
965
+ const c = a.category.localeCompare(b.category);
966
+ return c !== 0 ? c : a.name.localeCompare(b.name);
967
+ });
968
+ return {
969
+ variant: list.variant,
970
+ query,
971
+ matches: hits.slice(0, limit),
972
+ sources: list.sources
973
+ };
948
974
  }
949
975
 
950
- // src/groups/design.ts
976
+ // src/groups/tokens.ts
951
977
  var EmptyInput = z4.object({});
952
- var GetPatternInput = z4.object({
953
- id: z4.string().min(1)
978
+ var SearchInput = z4.object({
979
+ query: z4.string().min(1),
980
+ limit: z4.number().int().min(1).max(100).optional()
954
981
  });
955
982
  var TOOLS4 = [
956
983
  {
957
- name: "design_list_principles",
958
- description: 'List the design principles defined by the consumer project\'s installed design variant. Reads `.teamix-evo/design/philosophy/principles.md` and parses headings of the form `## P1 \xB7 Name`. Returns id, display name, and a one-line definition for each. Use this when the user asks about "the four principles", "design philosophy", or to ground a design review in the variant\'s stated values. Returns `{ principles: [], note: "..." }` if the project hasn\'t installed a design pack.',
959
- inputSchema: { type: "object", properties: {} }
960
- },
961
- {
962
- name: "design_get_tokens",
963
- description: "Fetch the consumer project's design tokens \u2014 parsed JSON from `.teamix-evo/tokens/base.tokens.json` and `semantic.tokens.json`, plus raw text of `tokens.theme.css` (variant theme) and `tokens.overrides.css` (user-owned overrides) when present. Use when AI needs to know what semantic colors / spacing / radii are available before writing component styles. JSON is for introspection; CSS is for copy-paste. Each tool result lists `sources` so you can cite paths.",
984
+ name: "tokens_get",
985
+ description: "Fetch the consumer project's design tokens \u2014 parsed JSON from `tokens/base.tokens.json`, plus raw text of `tokens.theme.css` (variant theme) and `tokens.overrides.css` (user-owned overrides) when present. Use when AI needs to know what semantic colors / spacing / radii are available before writing component styles. JSON is for introspection; CSS is for copy-paste. Each result lists `sources` so you can cite paths.",
964
986
  inputSchema: { type: "object", properties: {} }
965
987
  },
966
988
  {
967
- name: "design_list_patterns",
968
- description: "Index every pattern in `.teamix-evo/design/patterns/*.md` (page types, journeys, flows, plus variant-specific markers like `cloud-*`, `opentrek-*`, `uni-manager-*`). Returns `{ id, title, sourcePath, variantSpecific }` for each. Use this before `design_get_pattern` to discover which patterns exist. When both a baseline (`page-types`) and a variant-specific (`cloud-page-types`) entry are available, prefer the variant-specific one for projects that match.",
989
+ name: "tokens_list",
990
+ description: "List ALL design tokens as a flat array of entries `{ category, name, type, values, description }`. Categories come from top-level keys of base.tokens.json (e.g. `color`, `radius`, `spacing`). Each entry exposes resolved values keyed by mode (e.g. `light` / `dark` / `default`). Use this BEFORE writing styles to discover what semantic names are available \u2014 much cheaper than parsing the whole CSS file. Use `tokens_search` instead when you have a keyword.",
969
991
  inputSchema: { type: "object", properties: {} }
970
992
  },
971
993
  {
972
- name: "design_get_pattern",
973
- description: "Fetch the full markdown body of one pattern by id (filename stem, e.g. `page-types` or `cloud-page-types`). Use after `design_list_patterns` discovered the id. Returns the raw markdown plus `sourcePath`. Returns isError if the id does not exist \u2014 call `design_list_patterns` first to discover available ids.",
994
+ name: "tokens_search",
995
+ description: 'Substring match across token category / name / description / values (case-insensitive). Use when you remember a phrase ("primary" / "destructive" / "card-gap") but not the exact path. Returns up to `limit` matches (default 20). Combine with `tokens_get` if you need raw CSS text after locating an entry.',
974
996
  inputSchema: {
975
997
  type: "object",
976
998
  properties: {
977
- id: {
978
- type: "string",
979
- description: 'Pattern id \u2014 filename stem under `patterns/`, e.g. "page-types", "cloud-page-types".'
999
+ query: { type: "string", description: "Free-text query." },
1000
+ limit: {
1001
+ type: "integer",
1002
+ minimum: 1,
1003
+ maximum: 100,
1004
+ description: "Max matches to return (default 20)."
980
1005
  }
981
1006
  },
982
- required: ["id"]
1007
+ required: ["query"]
983
1008
  }
984
- },
985
- {
986
- name: "design_get_brand",
987
- description: "Fetch the consumer project's variant-specific brand voice \u2014 `brand/tone.md`, `brand/voice.md`, `brand/examples.md`. Each is optional; the response contains only the files that exist. Some variants (like `_template` or `default`) ship no brand content \u2014 that's not an error, the response simply lists no entries. Use this when generating or reviewing user-facing copy, error messages, empty states, or onboarding text where brand voice matters.",
988
- inputSchema: { type: "object", properties: {} }
989
1009
  }
990
1010
  ];
991
1011
  var TOOL_NAMES4 = new Set(TOOLS4.map((t) => t.name));
992
- var NO_DESIGN_NOTE = ".teamix-evo/design/ not found in this project. Run `npx teamix-evo design init <variant>` to install a design pack.";
993
- function createDesignGroup(opts = {}) {
1012
+ var NO_TOKENS_NOTE = "tokens/ directory not found in this project. Run `npx teamix-evo tokens init <variant>` to install design tokens.";
1013
+ function createTokensGroup(opts = {}) {
994
1014
  let cache = opts.loaded === void 0 ? void 0 : opts.loaded;
995
- function getDesign() {
996
- if (cache === void 0) cache = loadDesign(opts.rootDir);
1015
+ function getLoaded() {
1016
+ if (cache === void 0) cache = loadTokens(opts.rootDir);
997
1017
  return cache;
998
1018
  }
999
1019
  return {
1000
- name: "design",
1020
+ name: "tokens",
1001
1021
  tools: TOOLS4,
1002
1022
  async handle(name, args) {
1003
1023
  if (!TOOL_NAMES4.has(name)) return void 0;
1004
- const loaded = getDesign();
1005
- if (name === "design_list_principles") {
1006
- EmptyInput.parse(args ?? {});
1007
- if (!loaded) {
1008
- return jsonResult({ principles: [], sourcePath: null, note: NO_DESIGN_NOTE });
1009
- }
1010
- return jsonResult(readPrinciples(loaded));
1011
- }
1012
- if (name === "design_get_tokens") {
1024
+ const loaded = getLoaded();
1025
+ if (name === "tokens_get") {
1013
1026
  EmptyInput.parse(args ?? {});
1014
1027
  if (!loaded) {
1015
- return jsonResult({ sources: [], note: NO_DESIGN_NOTE });
1028
+ return jsonResult({ sources: [], note: NO_TOKENS_NOTE });
1016
1029
  }
1017
1030
  return jsonResult(readTokens(loaded));
1018
1031
  }
1019
- if (name === "design_list_patterns") {
1032
+ if (name === "tokens_list") {
1020
1033
  EmptyInput.parse(args ?? {});
1021
1034
  if (!loaded) {
1022
- return jsonResult({ patterns: [], note: NO_DESIGN_NOTE });
1023
- }
1024
- return jsonResult({
1025
- variant: loaded.variant,
1026
- patterns: readPatternIndex(loaded)
1027
- });
1028
- }
1029
- if (name === "design_get_pattern") {
1030
- const input = GetPatternInput.parse(args);
1031
- if (!loaded) {
1032
- return {
1033
- content: [{ type: "text", text: NO_DESIGN_NOTE }],
1034
- isError: true
1035
- };
1036
- }
1037
- const result = readPatternContent(loaded, input.id);
1038
- if (!result) {
1039
- return {
1040
- content: [
1041
- {
1042
- type: "text",
1043
- text: `Pattern not found: ${input.id}. Use design_list_patterns to discover available ids.`
1044
- }
1045
- ],
1046
- isError: true
1047
- };
1035
+ return jsonResult({
1036
+ variant: null,
1037
+ tokens: [],
1038
+ sources: [],
1039
+ note: NO_TOKENS_NOTE
1040
+ });
1048
1041
  }
1049
- return jsonResult(result);
1042
+ return jsonResult(listTokens(loaded));
1050
1043
  }
1051
- if (name === "design_get_brand") {
1052
- EmptyInput.parse(args ?? {});
1044
+ if (name === "tokens_search") {
1045
+ const parsed = SearchInput.parse(args ?? {});
1053
1046
  if (!loaded) {
1054
- return jsonResult({ note: NO_DESIGN_NOTE });
1047
+ return jsonResult({
1048
+ variant: null,
1049
+ query: parsed.query,
1050
+ matches: [],
1051
+ sources: [],
1052
+ note: NO_TOKENS_NOTE
1053
+ });
1055
1054
  }
1056
- return jsonResult(readBrand(loaded));
1055
+ return jsonResult(searchTokens(loaded, parsed.query, parsed.limit));
1057
1056
  }
1058
1057
  return void 0;
1059
1058
  }
@@ -1071,7 +1070,7 @@ function createServer(opts = {}) {
1071
1070
  createRegistryGroup(opts.registry),
1072
1071
  createAdrGroup(opts.adr),
1073
1072
  createSkillsGroup(opts.skills),
1074
- createDesignGroup(opts.design)
1073
+ createTokensGroup(opts.tokens)
1075
1074
  // Future: createScenarioGroup(opts.scenario), // ADR 0011, v0.8
1076
1075
  ];
1077
1076
  const seen = /* @__PURE__ */ new Set();