@rfdtech/components 1.21.0 → 2.0.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 (263) hide show
  1. package/README.md +244 -74
  2. package/dist/adapters/context-adapter.d.ts +18 -0
  3. package/dist/adapters/context-adapter.d.ts.map +1 -0
  4. package/dist/adapters/next-adapter.d.ts +23 -0
  5. package/dist/adapters/next-adapter.d.ts.map +1 -0
  6. package/dist/adapters/react-router-adapter.d.ts +9 -0
  7. package/dist/adapters/react-router-adapter.d.ts.map +1 -0
  8. package/dist/adapters/registry.d.ts +19 -0
  9. package/dist/adapters/registry.d.ts.map +1 -0
  10. package/dist/components/app-header/AppHeader.d.ts +3 -2
  11. package/dist/components/app-header/AppHeader.d.ts.map +1 -1
  12. package/dist/components/app-header/AppHeaderNotificationItem.d.ts +3 -0
  13. package/dist/components/app-header/AppHeaderNotificationItem.d.ts.map +1 -0
  14. package/dist/components/app-header/AppHeaderProfile.d.ts +5 -0
  15. package/dist/components/app-header/AppHeaderProfile.d.ts.map +1 -1
  16. package/dist/components/app-header/index.d.ts +4 -2
  17. package/dist/components/app-header/index.d.ts.map +1 -1
  18. package/dist/components/app-layout/AppLayout.d.ts +6 -0
  19. package/dist/components/app-layout/AppLayout.d.ts.map +1 -1
  20. package/dist/components/app-layout/AppLayoutInner.d.ts +1 -0
  21. package/dist/components/app-layout/AppLayoutInner.d.ts.map +1 -1
  22. package/dist/components/app-switcher/AppIconTile.d.ts +10 -0
  23. package/dist/components/app-switcher/AppIconTile.d.ts.map +1 -0
  24. package/dist/components/app-switcher/AppSwitcher.d.ts +1 -1
  25. package/dist/components/app-switcher/AppSwitcher.d.ts.map +1 -1
  26. package/dist/components/app-switcher/AppSwitcherItem.d.ts.map +1 -1
  27. package/dist/components/app-switcher/IconPatterns.d.ts +8 -0
  28. package/dist/components/app-switcher/IconPatterns.d.ts.map +1 -0
  29. package/dist/components/app-switcher/SystemAppIcon.d.ts.map +1 -1
  30. package/dist/components/app-switcher/index.d.ts +2 -0
  31. package/dist/components/app-switcher/index.d.ts.map +1 -1
  32. package/dist/components/avatar/Avatar.d.ts.map +1 -1
  33. package/dist/components/bulk-import-modal/BulkImportModal.d.ts.map +1 -1
  34. package/dist/components/bulk-import-modal/steps/ValidateDataStep.d.ts.map +1 -1
  35. package/dist/components/button/Button.d.ts +1 -1
  36. package/dist/components/button/Button.d.ts.map +1 -1
  37. package/dist/components/button/Spinner.d.ts.map +1 -1
  38. package/dist/components/combobox/Combobox.d.ts +4 -0
  39. package/dist/components/combobox/Combobox.d.ts.map +1 -0
  40. package/dist/components/combobox/Combobox.test.d.ts +2 -0
  41. package/dist/components/combobox/Combobox.test.d.ts.map +1 -0
  42. package/dist/components/combobox/index.d.ts +3 -0
  43. package/dist/components/combobox/index.d.ts.map +1 -0
  44. package/dist/components/country-selector/CountrySelector.d.ts +7 -0
  45. package/dist/components/country-selector/CountrySelector.d.ts.map +1 -1
  46. package/dist/components/country-selector/index.d.ts +1 -0
  47. package/dist/components/country-selector/index.d.ts.map +1 -1
  48. package/dist/components/date-range-selector/DateRangeSelector.d.ts +2 -2
  49. package/dist/components/date-range-selector/DateRangeSelector.d.ts.map +1 -1
  50. package/dist/components/date-range-selector/index.d.ts +1 -1
  51. package/dist/components/date-range-selector/index.d.ts.map +1 -1
  52. package/dist/components/date-selector/DateSelector.d.ts.map +1 -1
  53. package/dist/components/dropdown/Dropdown.d.ts +1 -1
  54. package/dist/components/dropdown/Dropdown.d.ts.map +1 -1
  55. package/dist/components/export-button/ExportButton.d.ts +3 -0
  56. package/dist/components/export-button/ExportButton.d.ts.map +1 -0
  57. package/dist/components/export-button/ExportButton.test.d.ts +2 -0
  58. package/dist/components/export-button/ExportButton.test.d.ts.map +1 -0
  59. package/dist/components/export-button/index.d.ts +3 -0
  60. package/dist/components/export-button/index.d.ts.map +1 -0
  61. package/dist/components/launchpad/Launchpad.d.ts +4 -0
  62. package/dist/components/launchpad/Launchpad.d.ts.map +1 -0
  63. package/dist/components/launchpad/Launchpad.test.d.ts +2 -0
  64. package/dist/components/launchpad/Launchpad.test.d.ts.map +1 -0
  65. package/dist/components/launchpad/LaunchpadGridIcon.d.ts +2 -0
  66. package/dist/components/launchpad/LaunchpadGridIcon.d.ts.map +1 -0
  67. package/dist/components/launchpad/LaunchpadIconTile.d.ts +10 -0
  68. package/dist/components/launchpad/LaunchpadIconTile.d.ts.map +1 -0
  69. package/dist/components/launchpad/LaunchpadItem.d.ts +8 -0
  70. package/dist/components/launchpad/LaunchpadItem.d.ts.map +1 -0
  71. package/dist/components/launchpad/LaunchpadOverlays.d.ts +3 -0
  72. package/dist/components/launchpad/LaunchpadOverlays.d.ts.map +1 -0
  73. package/dist/components/launchpad/SystemLaunchpadIcon.d.ts +5 -0
  74. package/dist/components/launchpad/SystemLaunchpadIcon.d.ts.map +1 -0
  75. package/dist/components/launchpad/hooks/useLaunchpad.d.ts +3 -0
  76. package/dist/components/launchpad/hooks/useLaunchpad.d.ts.map +1 -0
  77. package/dist/components/launchpad/index.d.ts +10 -0
  78. package/dist/components/launchpad/index.d.ts.map +1 -0
  79. package/dist/components/metric-card/MetricCard.d.ts.map +1 -1
  80. package/dist/components/metric-card/index.d.ts +1 -1
  81. package/dist/components/metric-card/index.d.ts.map +1 -1
  82. package/dist/components/network-operator/NetworkOperator.d.ts +4 -0
  83. package/dist/components/network-operator/NetworkOperator.d.ts.map +1 -1
  84. package/dist/components/network-operator/index.d.ts +1 -0
  85. package/dist/components/network-operator/index.d.ts.map +1 -1
  86. package/dist/components/notice/Notice.d.ts +4 -0
  87. package/dist/components/notice/Notice.d.ts.map +1 -0
  88. package/dist/components/notice/Notice.test.d.ts +2 -0
  89. package/dist/components/notice/Notice.test.d.ts.map +1 -0
  90. package/dist/components/notice/index.d.ts +3 -0
  91. package/dist/components/notice/index.d.ts.map +1 -0
  92. package/dist/components/phone-number-input/PhoneNumberInput.d.ts.map +1 -1
  93. package/dist/components/popup/Popup.d.ts +15 -0
  94. package/dist/components/popup/Popup.d.ts.map +1 -0
  95. package/dist/components/popup/Popup.test.d.ts +2 -0
  96. package/dist/components/popup/Popup.test.d.ts.map +1 -0
  97. package/dist/components/popup/index.d.ts +3 -0
  98. package/dist/components/popup/index.d.ts.map +1 -0
  99. package/dist/components/profile-popover/ProfilePopover.d.ts +4 -0
  100. package/dist/components/profile-popover/ProfilePopover.d.ts.map +1 -0
  101. package/dist/components/profile-popover/ProfilePopover.test.d.ts +2 -0
  102. package/dist/components/profile-popover/ProfilePopover.test.d.ts.map +1 -0
  103. package/dist/components/profile-popover/index.d.ts +3 -0
  104. package/dist/components/profile-popover/index.d.ts.map +1 -0
  105. package/dist/components/role-select/RoleSelect.d.ts +4 -0
  106. package/dist/components/role-select/RoleSelect.d.ts.map +1 -0
  107. package/dist/components/role-select/RoleSelect.test.d.ts +2 -0
  108. package/dist/components/role-select/RoleSelect.test.d.ts.map +1 -0
  109. package/dist/components/role-select/index.d.ts +3 -0
  110. package/dist/components/role-select/index.d.ts.map +1 -0
  111. package/dist/components/section-header/SectionHeader.d.ts +7 -0
  112. package/dist/components/section-header/SectionHeader.d.ts.map +1 -0
  113. package/dist/components/section-header/SectionHeader.test.d.ts +2 -0
  114. package/dist/components/section-header/SectionHeader.test.d.ts.map +1 -0
  115. package/dist/components/section-header/index.d.ts +3 -0
  116. package/dist/components/section-header/index.d.ts.map +1 -0
  117. package/dist/components/sidebar/Sidebar.d.ts +3 -3
  118. package/dist/components/sidebar/Sidebar.d.ts.map +1 -1
  119. package/dist/components/sidebar/SidebarContext.d.ts +6 -0
  120. package/dist/components/sidebar/SidebarContext.d.ts.map +1 -1
  121. package/dist/components/sidebar/index.d.ts +1 -1
  122. package/dist/components/sidebar/index.d.ts.map +1 -1
  123. package/dist/components/stepper/Stepper.d.ts +12 -0
  124. package/dist/components/stepper/Stepper.d.ts.map +1 -0
  125. package/dist/components/stepper/Stepper.test.d.ts +2 -0
  126. package/dist/components/stepper/Stepper.test.d.ts.map +1 -0
  127. package/dist/components/stepper/StepperContext.d.ts +4 -0
  128. package/dist/components/stepper/StepperContext.d.ts.map +1 -0
  129. package/dist/components/stepper/index.d.ts +3 -0
  130. package/dist/components/stepper/index.d.ts.map +1 -0
  131. package/dist/components/switch/Switch.d.ts +4 -0
  132. package/dist/components/switch/Switch.d.ts.map +1 -0
  133. package/dist/components/switch/Switch.test.d.ts +2 -0
  134. package/dist/components/switch/Switch.test.d.ts.map +1 -0
  135. package/dist/components/switch/index.d.ts +3 -0
  136. package/dist/components/switch/index.d.ts.map +1 -0
  137. package/dist/components/table/Table.d.ts +3 -6
  138. package/dist/components/table/Table.d.ts.map +1 -1
  139. package/dist/components/table/TableHeader.d.ts +3 -10
  140. package/dist/components/table/TableHeader.d.ts.map +1 -1
  141. package/dist/components/table/TablePagination.d.ts.map +1 -1
  142. package/dist/components/table/index.d.ts +1 -1
  143. package/dist/components/table/index.d.ts.map +1 -1
  144. package/dist/components/tabs/Tabs.d.ts.map +1 -1
  145. package/dist/components/tabs/hooks/useTabsLineIndicator.d.ts.map +1 -1
  146. package/dist/components/theme/ThemeProvider.d.ts.map +1 -1
  147. package/dist/components/theme/cletTheme.d.ts +11 -0
  148. package/dist/components/theme/cletTheme.d.ts.map +1 -0
  149. package/dist/components/theme/cletTheme.test-d.d.ts +2 -0
  150. package/dist/components/theme/cletTheme.test-d.d.ts.map +1 -0
  151. package/dist/components/theme/cletTheme.test.d.ts +2 -0
  152. package/dist/components/theme/cletTheme.test.d.ts.map +1 -0
  153. package/dist/components/theme/index.d.ts +3 -1
  154. package/dist/components/theme/index.d.ts.map +1 -1
  155. package/dist/components/timeline/Timeline.d.ts +8 -0
  156. package/dist/components/timeline/Timeline.d.ts.map +1 -0
  157. package/dist/components/timeline/Timeline.test.d.ts +2 -0
  158. package/dist/components/timeline/Timeline.test.d.ts.map +1 -0
  159. package/dist/components/timeline/index.d.ts +3 -0
  160. package/dist/components/timeline/index.d.ts.map +1 -0
  161. package/dist/components/upload-field/UploadField.d.ts +5 -0
  162. package/dist/components/upload-field/UploadField.d.ts.map +1 -1
  163. package/dist/components/upload-field/index.d.ts +2 -2
  164. package/dist/components/upload-field/index.d.ts.map +1 -1
  165. package/dist/contexts/router-adapter-context.d.ts +27 -0
  166. package/dist/contexts/router-adapter-context.d.ts.map +1 -0
  167. package/dist/generated/components.theme.d.ts +313 -0
  168. package/dist/generated/components.theme.d.ts.map +1 -0
  169. package/dist/hooks/index.d.ts +1 -0
  170. package/dist/hooks/index.d.ts.map +1 -1
  171. package/dist/hooks/useHasMounted.d.ts +10 -0
  172. package/dist/hooks/useHasMounted.d.ts.map +1 -0
  173. package/dist/hooks/useTableFilter.d.ts.map +1 -1
  174. package/dist/hooks/useTablePagination.d.ts.map +1 -1
  175. package/dist/hooks/useTableState.d.ts +1 -1
  176. package/dist/hooks/useTableState.d.ts.map +1 -1
  177. package/dist/index.cjs +85 -55
  178. package/dist/index.css +1 -1
  179. package/dist/index.d.ts +15 -1
  180. package/dist/index.d.ts.map +1 -1
  181. package/dist/index.js +19423 -31378
  182. package/dist/mcp/cli.js +75 -0
  183. package/dist/mcp/docs.js +122 -0
  184. package/dist/mcp/generated/components.json +3593 -0
  185. package/dist/mcp/generated/examples.json +1233 -0
  186. package/dist/mcp/generated/index.meta.json +252 -0
  187. package/dist/mcp/generated/rules.json +629 -0
  188. package/dist/mcp/generated/search.json +1618 -0
  189. package/dist/mcp/generated/tokens.json +1022 -0
  190. package/dist/mcp/index.js +337 -0
  191. package/dist/mcp/indexer.js +370 -0
  192. package/dist/mcp/installers/claude.js +37 -0
  193. package/dist/mcp/installers/codex.js +24 -0
  194. package/dist/mcp/installers/cursor.js +33 -0
  195. package/dist/mcp/installers/doctor.js +36 -0
  196. package/dist/mcp/installers/opencode.js +36 -0
  197. package/dist/mcp/installers/setup.js +34 -0
  198. package/dist/mcp/installers/util.js +41 -0
  199. package/dist/mcp/paths.js +63 -0
  200. package/dist/mcp/skill/SKILL.md +36 -0
  201. package/dist/mcp/skill/image-to-components.md +49 -0
  202. package/dist/next-index.d.ts +44 -0
  203. package/dist/next-index.d.ts.map +1 -0
  204. package/dist/next.cjs +282 -0
  205. package/dist/next.css +1 -0
  206. package/dist/next.d.ts +29 -0
  207. package/dist/next.js +37774 -0
  208. package/dist/types/app-header.d.ts +70 -10
  209. package/dist/types/app-header.d.ts.map +1 -1
  210. package/dist/types/app-switcher.d.ts +10 -1
  211. package/dist/types/app-switcher.d.ts.map +1 -1
  212. package/dist/types/avatar.d.ts +9 -0
  213. package/dist/types/avatar.d.ts.map +1 -1
  214. package/dist/types/combobox.d.ts +43 -0
  215. package/dist/types/combobox.d.ts.map +1 -0
  216. package/dist/types/country-selector.d.ts +4 -0
  217. package/dist/types/country-selector.d.ts.map +1 -1
  218. package/dist/types/date-range-selector.d.ts +11 -0
  219. package/dist/types/date-range-selector.d.ts.map +1 -1
  220. package/dist/types/dropdown.d.ts +8 -0
  221. package/dist/types/dropdown.d.ts.map +1 -1
  222. package/dist/types/export-button.d.ts +24 -0
  223. package/dist/types/export-button.d.ts.map +1 -0
  224. package/dist/types/launchpad.d.ts +79 -0
  225. package/dist/types/launchpad.d.ts.map +1 -0
  226. package/dist/types/metric-card.d.ts +7 -0
  227. package/dist/types/metric-card.d.ts.map +1 -1
  228. package/dist/types/network-operator.d.ts +8 -0
  229. package/dist/types/network-operator.d.ts.map +1 -1
  230. package/dist/types/notice.d.ts +24 -0
  231. package/dist/types/notice.d.ts.map +1 -0
  232. package/dist/types/popup.d.ts +43 -0
  233. package/dist/types/popup.d.ts.map +1 -0
  234. package/dist/types/profile-popover.d.ts +97 -0
  235. package/dist/types/profile-popover.d.ts.map +1 -0
  236. package/dist/types/role-select.d.ts +28 -0
  237. package/dist/types/role-select.d.ts.map +1 -0
  238. package/dist/types/router-adapter.d.ts +46 -0
  239. package/dist/types/router-adapter.d.ts.map +1 -0
  240. package/dist/types/section-header.d.ts +26 -0
  241. package/dist/types/section-header.d.ts.map +1 -0
  242. package/dist/types/sidebar.d.ts +16 -0
  243. package/dist/types/sidebar.d.ts.map +1 -1
  244. package/dist/types/stepper.d.ts +45 -0
  245. package/dist/types/stepper.d.ts.map +1 -0
  246. package/dist/types/switch.d.ts +24 -0
  247. package/dist/types/switch.d.ts.map +1 -0
  248. package/dist/types/table.d.ts +90 -1
  249. package/dist/types/table.d.ts.map +1 -1
  250. package/dist/types/tabs.d.ts +1 -1
  251. package/dist/types/tabs.d.ts.map +1 -1
  252. package/dist/types/theme.d.ts +28 -8
  253. package/dist/types/theme.d.ts.map +1 -1
  254. package/dist/types/timeline.d.ts +52 -0
  255. package/dist/types/timeline.d.ts.map +1 -0
  256. package/dist/types/upload-field.d.ts +23 -0
  257. package/dist/types/upload-field.d.ts.map +1 -1
  258. package/dist/utils/export.d.ts +15 -0
  259. package/dist/utils/export.d.ts.map +1 -0
  260. package/dist/utils/stringToColor.d.ts +25 -0
  261. package/dist/utils/stringToColor.d.ts.map +1 -1
  262. package/package.json +43 -7
  263. package/scripts/postinstall.mjs +40 -0
@@ -0,0 +1,337 @@
1
+ #!/usr/bin/env node
2
+ import { existsSync } from "node:fs";
3
+ import { readdir, readFile, stat } from "node:fs/promises";
4
+ import path from "node:path";
5
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
6
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
7
+ import { z } from "zod";
8
+ import { getComponent, getRules, listSlugs, loadIndex, searchComponents, searchDocs, searchRules, } from "./docs.js";
9
+ import { buildIndex } from "./indexer.js";
10
+ import { GENERATED_DIR, SOURCE, hasRepoSource } from "./paths.js";
11
+ /**
12
+ * The generated JSON knowledge base is a build artifact, not a second source
13
+ * of truth: it's gitignored and only ever produced by re-running the indexer
14
+ * against the live MDX/examples/types/rules/tokens. In dev (running inside
15
+ * the repo checkout) we rebuild it here whenever it's missing or any source
16
+ * file is newer than what it was last built from, so the MCP always reflects
17
+ * the current docs with zero manual "npm run index" step required. In a
18
+ * published install there is no source to rebuild from — hasRepoSource is
19
+ * false there — so we only ever read the snapshot bundled at publish time.
20
+ */
21
+ async function ensureFreshIndex() {
22
+ if (!hasRepoSource)
23
+ return;
24
+ const metaPath = path.join(GENERATED_DIR, "index.meta.json");
25
+ let stale = !existsSync(metaPath);
26
+ if (!stale) {
27
+ const meta = JSON.parse(await readFile(metaPath, "utf8"));
28
+ const builtFrom = meta.builtFrom ?? {};
29
+ for (const [filePath, mtime] of Object.entries(builtFrom)) {
30
+ if (!existsSync(filePath)) {
31
+ stale = true;
32
+ break;
33
+ }
34
+ const info = await stat(filePath);
35
+ if (info.mtimeMs > mtime) {
36
+ stale = true;
37
+ break;
38
+ }
39
+ }
40
+ if (!stale) {
41
+ for (const dir of [SOURCE.pages, SOURCE.examples, SOURCE.types, SOURCE.rules, SOURCE.themeDir]) {
42
+ if (!existsSync(dir))
43
+ continue;
44
+ for (const file of await readdir(dir)) {
45
+ const filePath = path.join(dir, file);
46
+ const info = await stat(filePath);
47
+ if (info.isFile() && !(filePath in builtFrom)) {
48
+ stale = true;
49
+ break;
50
+ }
51
+ }
52
+ if (stale)
53
+ break;
54
+ }
55
+ }
56
+ // A brand-new component (new styles/*.css files) or a token registry
57
+ // regenerated for the first time — same "unseen file" check as above,
58
+ // just for the two additional sources buildTokens() depends on.
59
+ if (!stale && existsSync(SOURCE.generatedTheme) && !(SOURCE.generatedTheme in builtFrom)) {
60
+ stale = true;
61
+ }
62
+ if (!stale && existsSync(SOURCE.componentsDir)) {
63
+ for (const entry of await readdir(SOURCE.componentsDir)) {
64
+ const stylesDir = path.join(SOURCE.componentsDir, entry, "styles");
65
+ if (!existsSync(stylesDir))
66
+ continue;
67
+ for (const file of await readdir(stylesDir)) {
68
+ if (!file.endsWith(".css"))
69
+ continue;
70
+ if (!(path.join(stylesDir, file) in builtFrom)) {
71
+ stale = true;
72
+ break;
73
+ }
74
+ }
75
+ if (stale)
76
+ break;
77
+ }
78
+ }
79
+ }
80
+ if (stale) {
81
+ console.error("[gsl-components-mcp] index missing or stale — rebuilding from source...");
82
+ const result = await buildIndex(GENERATED_DIR);
83
+ console.error(`[gsl-components-mcp] index rebuilt: ${JSON.stringify(result)}`);
84
+ }
85
+ }
86
+ // ───────────────────────── formatting ─────────────────────────
87
+ function formatExample(ex) {
88
+ const ownership = ex.importName
89
+ ? `imported as \`${ex.importName}\``
90
+ : "not referenced by any MDX \\`?raw\\` import — included by filename match";
91
+ return `### \`${ex.file}\` (${ownership})\n\n\`\`\`tsx\n${ex.source.trimEnd()}\n\`\`\``;
92
+ }
93
+ function formatRuleLine(rule) {
94
+ return `- **[${rule.severity.toUpperCase()}] ${rule.title}** — ${rule.body.replace(/\s+/g, " ").trim()}`;
95
+ }
96
+ function formatTokenValues(t) {
97
+ const values = [
98
+ t.base ? `base=${t.base}` : null,
99
+ t.light ? `light=${t.light}` : null,
100
+ t.dark ? `dark=${t.dark}` : null,
101
+ ].filter(Boolean);
102
+ return values.length ? ` ${values.join(", ")}` : "";
103
+ }
104
+ function formatComponent(idx, entry) {
105
+ const { meta } = entry;
106
+ const parts = [];
107
+ parts.push(`# ${meta.title}`);
108
+ if (meta.description)
109
+ parts.push(meta.description);
110
+ const facts = [
111
+ meta.category ? `**Category:** ${meta.category}` : null,
112
+ meta.since ? `**Since:** ${meta.since}` : null,
113
+ meta.status ? `**Status:** ${meta.status}` : null,
114
+ meta.import ? `**Import:** \`${meta.import}\`` : null,
115
+ ].filter(Boolean);
116
+ if (facts.length)
117
+ parts.push(facts.join(" · "));
118
+ if (meta.keywords.length)
119
+ parts.push(`**Keywords:** ${meta.keywords.join(", ")}`);
120
+ if (meta.related.length)
121
+ parts.push(`**Related:** ${meta.related.join(", ")}`);
122
+ parts.push("---");
123
+ parts.push(entry.docs);
124
+ if (entry.examples.length) {
125
+ parts.push("---");
126
+ parts.push(`## All examples (${entry.examples.length})\n\n` + entry.examples.map(formatExample).join("\n\n"));
127
+ }
128
+ if (entry.types) {
129
+ parts.push("---");
130
+ parts.push(`## Authoritative type source (\`src/types/${meta.slug}.ts\`)\n\n` +
131
+ "This is the real TypeScript source — if it ever disagrees with a prop table above, trust this.\n\n" +
132
+ `\`\`\`ts\n${entry.types.trimEnd()}\n\`\`\``);
133
+ }
134
+ const rules = entry.ruleIds.map((id) => idx.rulesById[id]).filter((r) => Boolean(r));
135
+ if (rules.length) {
136
+ parts.push("---");
137
+ parts.push(`## Usage rules\n\n${rules.map(formatRuleLine).join("\n")}`);
138
+ }
139
+ return parts.join("\n\n");
140
+ }
141
+ function formatRulesByCategory(byCategory) {
142
+ const categories = Object.keys(byCategory).sort();
143
+ if (categories.length === 0)
144
+ return "No rules found.";
145
+ return categories
146
+ .map((cat) => `## ${cat}\n\n${byCategory[cat].map(formatRuleLine).join("\n")}`)
147
+ .join("\n\n");
148
+ }
149
+ function notFound(slug, idx) {
150
+ return {
151
+ content: [
152
+ {
153
+ type: "text",
154
+ text: `No component doc found for slug "${slug}". Available slugs:\n${listSlugs(idx)
155
+ .map((s) => `- ${s}`)
156
+ .join("\n")}`,
157
+ },
158
+ ],
159
+ isError: true,
160
+ };
161
+ }
162
+ // ───────────────────────── server ─────────────────────────
163
+ async function main() {
164
+ await ensureFreshIndex();
165
+ const idx = await loadIndex();
166
+ const server = new McpServer({
167
+ name: "gsl-components-docs",
168
+ version: "2.0.0",
169
+ });
170
+ server.registerTool("list_components", {
171
+ title: "List components",
172
+ description: "Lists every GSL component/guide doc page — slug, title, category, and description. Start here.",
173
+ }, async () => {
174
+ const lines = listSlugs(idx).map((slug) => {
175
+ const { meta } = idx.components[slug];
176
+ return `- ${slug}: ${meta.title}${meta.category ? ` [${meta.category}]` : ""}${meta.description ? ` — ${meta.description}` : ""}`;
177
+ });
178
+ return { content: [{ type: "text", text: lines.join("\n") }] };
179
+ });
180
+ server.registerTool("search_components", {
181
+ title: "Search components",
182
+ description: "Lexical search over component name, keywords, description, and doc headings. Use this before " +
183
+ "inventing a UI element — try phrases like \"table pagination\", \"confirmation dialog\", \"date range\".",
184
+ inputSchema: {
185
+ query: z.string().describe('Free-text search, e.g. "wizard steps" or "otp verification".'),
186
+ },
187
+ }, async ({ query }) => {
188
+ const results = searchComponents(idx, query);
189
+ if (results.length === 0) {
190
+ return { content: [{ type: "text", text: `No components matched "${query}".` }] };
191
+ }
192
+ const text = results
193
+ .map((r) => `- ${r.slug}: ${r.name}${r.description ? ` — ${r.description}` : ""} (score ${r.score})`)
194
+ .join("\n");
195
+ return { content: [{ type: "text", text }] };
196
+ });
197
+ server.registerTool("get_component", {
198
+ title: "Get component",
199
+ description: "The full picture for one component: description, props/types reference (authoritative source, " +
200
+ "not just the prose table), design tokens context, EVERY known example (not just the ones the MDX " +
201
+ "page shows), and any usage rules (do/don't). Use this before writing code against a component.",
202
+ inputSchema: {
203
+ slug: z
204
+ .string()
205
+ .describe('Doc page slug, e.g. "button", "table", "date-range-selector".'),
206
+ },
207
+ }, async ({ slug }) => {
208
+ const entry = getComponent(idx, slug);
209
+ if (!entry)
210
+ return notFound(slug, idx);
211
+ return { content: [{ type: "text", text: formatComponent(idx, entry) }] };
212
+ });
213
+ server.registerTool("get_component_examples", {
214
+ title: "Get component examples",
215
+ description: "Just the example source files for a component — every one known (explicit MDX examples plus any " +
216
+ "matching orphan example files), without the full prose doc.",
217
+ inputSchema: {
218
+ slug: z.string().describe('Doc page slug, e.g. "table", "form".'),
219
+ },
220
+ }, async ({ slug }) => {
221
+ const entry = getComponent(idx, slug);
222
+ if (!entry)
223
+ return notFound(slug, idx);
224
+ if (entry.examples.length === 0) {
225
+ return { content: [{ type: "text", text: `No examples found for "${slug}".` }] };
226
+ }
227
+ const text = `# ${entry.meta.title} — examples (${entry.examples.length})\n\n${entry.examples
228
+ .map(formatExample)
229
+ .join("\n\n")}`;
230
+ return { content: [{ type: "text", text }] };
231
+ });
232
+ server.registerTool("get_component_types", {
233
+ title: "Get component types",
234
+ description: "The authoritative TypeScript prop/type source for a component, straight from src/types/<slug>.ts — " +
235
+ "trust this over any hand-written MDX prop table if they ever disagree.",
236
+ inputSchema: {
237
+ slug: z.string().describe('Doc page slug, e.g. "button", "date-range-selector".'),
238
+ },
239
+ }, async ({ slug }) => {
240
+ const entry = getComponent(idx, slug);
241
+ if (!entry)
242
+ return notFound(slug, idx);
243
+ if (!entry.types) {
244
+ return {
245
+ content: [
246
+ { type: "text", text: `No dedicated src/types/${entry.meta.slug}.ts file for "${slug}".` },
247
+ ],
248
+ };
249
+ }
250
+ return {
251
+ content: [
252
+ { type: "text", text: `\`\`\`ts\n// src/types/${entry.meta.slug}.ts\n${entry.types.trimEnd()}\n\`\`\`` },
253
+ ],
254
+ };
255
+ });
256
+ server.registerTool("get_rules", {
257
+ title: "Get rules",
258
+ description: "Design/usage rules (do/don't) — either the whole corpus, one category (components, forms, " +
259
+ "theming, tokens, layout, accessibility), or every rule tagged to one component slug.",
260
+ inputSchema: {
261
+ topic: z
262
+ .string()
263
+ .optional()
264
+ .describe('Optional: a rules category (e.g. "forms") or a component slug (e.g. "dialog"). Omit for everything.'),
265
+ },
266
+ }, async ({ topic }) => {
267
+ const byCategory = getRules(idx, topic);
268
+ return { content: [{ type: "text", text: formatRulesByCategory(byCategory) }] };
269
+ });
270
+ server.registerTool("search_rules", {
271
+ title: "Search rules",
272
+ description: "Free-text search over every rule's title, body, and tagged components.",
273
+ inputSchema: {
274
+ query: z.string().describe('Free-text search, e.g. "required fields" or "modal padding".'),
275
+ },
276
+ }, async ({ query }) => {
277
+ const results = searchRules(idx, query);
278
+ if (results.length === 0)
279
+ return { content: [{ type: "text", text: `No rules matched "${query}".` }] };
280
+ return { content: [{ type: "text", text: results.map(formatRuleLine).join("\n") }] };
281
+ });
282
+ server.registerTool("get_tokens", {
283
+ title: "Get design tokens",
284
+ description: "Every --gsl-* design token gslTheme() actually accepts — global tokens (color/radius/shadow/" +
285
+ "z-index/font) plus every component-scoped token (e.g. Card's `padding`, AppHeader's `bg`), with " +
286
+ "base/light/dark values where known. To override any of these at runtime, use the type-safe " +
287
+ "`gslTheme()` helper (see get_component(\"theme\")) rather than hand-writing CSS.",
288
+ inputSchema: {
289
+ component: z
290
+ .string()
291
+ .optional()
292
+ .describe('Optional: a component slug (e.g. "card") to list only that component\'s own tokens. Omit for everything, global tokens first.'),
293
+ },
294
+ }, async ({ component }) => {
295
+ const wanted = component?.trim().toLowerCase();
296
+ const entries = Object.entries(idx.tokens).filter(([, t]) => !wanted || t.component === wanted);
297
+ if (entries.length === 0) {
298
+ return { content: [{ type: "text", text: `No tokens found${wanted ? ` for component "${wanted}"` : ""}.` }] };
299
+ }
300
+ const globalLines = entries
301
+ .filter(([, t]) => t.scope === "global")
302
+ .sort(([a], [b]) => a.localeCompare(b))
303
+ .map(([name, t]) => `- \`${name}\` [${t.group}]${formatTokenValues(t)}`);
304
+ const componentLines = entries
305
+ .filter(([, t]) => t.scope === "component")
306
+ .sort(([a, ta], [b, tb]) => (ta.component ?? "").localeCompare(tb.component ?? "") || a.localeCompare(b))
307
+ .map(([name, t]) => `- \`${name}\` [${t.component}, ${t.group}]${formatTokenValues(t)}`);
308
+ const parts = [];
309
+ if (globalLines.length)
310
+ parts.push(`## Global\n\n${globalLines.join("\n")}`);
311
+ if (componentLines.length)
312
+ parts.push(`## Component-scoped\n\n${componentLines.join("\n")}`);
313
+ return { content: [{ type: "text", text: parts.join("\n\n") }] };
314
+ });
315
+ server.registerTool("search_docs", {
316
+ title: "Search docs",
317
+ description: "Full-text search across every component's doc prose and headings — broader than search_components, " +
318
+ "useful when you're not sure which component owns a concept (e.g. \"virtual scrolling\", \"URL state\").",
319
+ inputSchema: {
320
+ query: z.string().describe('Free-text search, e.g. "URL driven pagination".'),
321
+ },
322
+ }, async ({ query }) => {
323
+ const results = searchDocs(idx, query);
324
+ if (results.length === 0)
325
+ return { content: [{ type: "text", text: `No docs matched "${query}".` }] };
326
+ const text = results
327
+ .map((r) => `- ${r.slug}: ${r.name}${r.matchedHeadings.length ? ` — matched: ${r.matchedHeadings.join(", ")}` : ""}`)
328
+ .join("\n");
329
+ return { content: [{ type: "text", text }] };
330
+ });
331
+ const transport = new StdioServerTransport();
332
+ await server.connect(transport);
333
+ }
334
+ main().catch((err) => {
335
+ console.error(err);
336
+ process.exit(1);
337
+ });