@urbint/cl 1.0.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 (206) hide show
  1. package/.cursor/rules +313 -0
  2. package/.rnstorybook/index.ts +11 -0
  3. package/.rnstorybook/main.ts +8 -0
  4. package/.rnstorybook/preview.tsx +14 -0
  5. package/.rnstorybook/storybook.requires.ts +49 -0
  6. package/.storybook/main.ts +16 -0
  7. package/.storybook/preview.ts +32 -0
  8. package/.storybook/vitest.setup.ts +7 -0
  9. package/App.tsx +422 -0
  10. package/README.md +229 -0
  11. package/app.json +33 -0
  12. package/assets/adaptive-icon.png +0 -0
  13. package/assets/favicon.png +0 -0
  14. package/assets/icon.png +0 -0
  15. package/assets/splash-icon.png +0 -0
  16. package/babel.config.js +16 -0
  17. package/docs/components/CodeBlock.tsx +80 -0
  18. package/docs/components/PropTable.tsx +93 -0
  19. package/docs/components/Sidebar.tsx +199 -0
  20. package/docs/components/index.ts +8 -0
  21. package/docs/data/colorTokens.ts +70 -0
  22. package/docs/data/componentData.tsx +1685 -0
  23. package/docs/data/index.ts +7 -0
  24. package/docs/index.ts +19 -0
  25. package/docs/navigation.ts +94 -0
  26. package/docs/pages/ColorsPage.tsx +226 -0
  27. package/docs/pages/ComponentPage.tsx +235 -0
  28. package/docs/pages/InstallationPage.tsx +232 -0
  29. package/docs/pages/IntroductionPage.tsx +163 -0
  30. package/docs/pages/ThemingPage.tsx +251 -0
  31. package/docs/pages/index.ts +10 -0
  32. package/docs/theme.ts +64 -0
  33. package/docs/types.ts +54 -0
  34. package/index.ts +8 -0
  35. package/llms.txt +1893 -0
  36. package/mcp-config.example.json +10 -0
  37. package/mcp-server/README.md +192 -0
  38. package/mcp-server/package-lock.json +1707 -0
  39. package/mcp-server/package.json +38 -0
  40. package/mcp-server/src/index.ts +1136 -0
  41. package/mcp-server/src/registry/components.ts +1446 -0
  42. package/mcp-server/src/registry/index.ts +3 -0
  43. package/mcp-server/src/registry/tokens.ts +256 -0
  44. package/mcp-server/tsconfig.json +19 -0
  45. package/package.json +92 -0
  46. package/src/components/Accordion/Accordion.stories.tsx +226 -0
  47. package/src/components/Accordion/Accordion.tsx +255 -0
  48. package/src/components/Accordion/index.ts +12 -0
  49. package/src/components/ActionSheet/ActionSheet.stories.tsx +393 -0
  50. package/src/components/ActionSheet/ActionSheet.tsx +258 -0
  51. package/src/components/ActionSheet/index.ts +2 -0
  52. package/src/components/Alert/Alert.stories.tsx +165 -0
  53. package/src/components/Alert/Alert.tsx +164 -0
  54. package/src/components/Alert/index.ts +2 -0
  55. package/src/components/AlertDialog/AlertDialog.stories.tsx +330 -0
  56. package/src/components/AlertDialog/AlertDialog.tsx +234 -0
  57. package/src/components/AlertDialog/index.ts +2 -0
  58. package/src/components/Avatar/Avatar.stories.tsx +154 -0
  59. package/src/components/Avatar/Avatar.tsx +219 -0
  60. package/src/components/Avatar/index.ts +2 -0
  61. package/src/components/Badge/Badge.stories.tsx +146 -0
  62. package/src/components/Badge/Badge.tsx +125 -0
  63. package/src/components/Badge/index.ts +2 -0
  64. package/src/components/Box/Box.stories.tsx +192 -0
  65. package/src/components/Box/Box.tsx +184 -0
  66. package/src/components/Box/index.ts +2 -0
  67. package/src/components/Button/Button.stories.tsx +157 -0
  68. package/src/components/Button/Button.tsx +180 -0
  69. package/src/components/Button/index.ts +2 -0
  70. package/src/components/Card/Card.stories.tsx +145 -0
  71. package/src/components/Card/Card.tsx +169 -0
  72. package/src/components/Card/index.ts +11 -0
  73. package/src/components/Center/Center.stories.tsx +215 -0
  74. package/src/components/Center/Center.tsx +29 -0
  75. package/src/components/Center/index.ts +2 -0
  76. package/src/components/Checkbox/Checkbox.stories.tsx +94 -0
  77. package/src/components/Checkbox/Checkbox.tsx +242 -0
  78. package/src/components/Checkbox/index.ts +2 -0
  79. package/src/components/DatePicker/DatePicker.stories.tsx +623 -0
  80. package/src/components/DatePicker/DatePicker.tsx +1228 -0
  81. package/src/components/DatePicker/index.ts +8 -0
  82. package/src/components/Divider/Divider.stories.tsx +224 -0
  83. package/src/components/Divider/Divider.tsx +73 -0
  84. package/src/components/Divider/index.ts +2 -0
  85. package/src/components/Drawer/Drawer.stories.tsx +414 -0
  86. package/src/components/Drawer/Drawer.tsx +342 -0
  87. package/src/components/Drawer/index.ts +11 -0
  88. package/src/components/Fab/Fab.stories.tsx +360 -0
  89. package/src/components/Fab/Fab.tsx +185 -0
  90. package/src/components/Fab/index.ts +2 -0
  91. package/src/components/FormControl/FormControl.stories.tsx +276 -0
  92. package/src/components/FormControl/FormControl.tsx +185 -0
  93. package/src/components/FormControl/index.ts +12 -0
  94. package/src/components/Grid/Grid.stories.tsx +244 -0
  95. package/src/components/Grid/Grid.tsx +93 -0
  96. package/src/components/Grid/index.ts +2 -0
  97. package/src/components/HStack/HStack.stories.tsx +230 -0
  98. package/src/components/HStack/HStack.tsx +80 -0
  99. package/src/components/HStack/index.ts +2 -0
  100. package/src/components/Heading/Heading.stories.tsx +111 -0
  101. package/src/components/Heading/Heading.tsx +85 -0
  102. package/src/components/Heading/index.ts +2 -0
  103. package/src/components/Icon/Icon.stories.tsx +320 -0
  104. package/src/components/Icon/Icon.tsx +117 -0
  105. package/src/components/Icon/index.ts +2 -0
  106. package/src/components/Image/Image.stories.tsx +357 -0
  107. package/src/components/Image/Image.tsx +168 -0
  108. package/src/components/Image/index.ts +2 -0
  109. package/src/components/Input/Input.stories.tsx +164 -0
  110. package/src/components/Input/Input.tsx +274 -0
  111. package/src/components/Input/index.ts +2 -0
  112. package/src/components/Link/Link.stories.tsx +187 -0
  113. package/src/components/Link/Link.tsx +104 -0
  114. package/src/components/Link/index.ts +2 -0
  115. package/src/components/Menu/Menu.stories.tsx +363 -0
  116. package/src/components/Menu/Menu.tsx +238 -0
  117. package/src/components/Menu/index.ts +2 -0
  118. package/src/components/Modal/Modal.stories.tsx +156 -0
  119. package/src/components/Modal/Modal.tsx +280 -0
  120. package/src/components/Modal/index.ts +11 -0
  121. package/src/components/Popover/Popover.stories.tsx +330 -0
  122. package/src/components/Popover/Popover.tsx +315 -0
  123. package/src/components/Popover/index.ts +11 -0
  124. package/src/components/Portal/Portal.stories.tsx +376 -0
  125. package/src/components/Portal/Portal.tsx +100 -0
  126. package/src/components/Portal/index.ts +2 -0
  127. package/src/components/Pressable/Pressable.stories.tsx +338 -0
  128. package/src/components/Pressable/Pressable.tsx +71 -0
  129. package/src/components/Pressable/index.ts +2 -0
  130. package/src/components/Progress/Progress.stories.tsx +131 -0
  131. package/src/components/Progress/Progress.tsx +219 -0
  132. package/src/components/Progress/index.ts +2 -0
  133. package/src/components/Radio/Radio.stories.tsx +101 -0
  134. package/src/components/Radio/Radio.tsx +234 -0
  135. package/src/components/Radio/index.ts +2 -0
  136. package/src/components/Select/Select.stories.tsx +908 -0
  137. package/src/components/Select/Select.tsx +659 -0
  138. package/src/components/Select/index.ts +8 -0
  139. package/src/components/Skeleton/Skeleton.stories.tsx +154 -0
  140. package/src/components/Skeleton/Skeleton.tsx +192 -0
  141. package/src/components/Skeleton/index.ts +8 -0
  142. package/src/components/Slider/Slider.stories.tsx +363 -0
  143. package/src/components/Slider/Slider.tsx +209 -0
  144. package/src/components/Slider/index.ts +2 -0
  145. package/src/components/Spinner/Spinner.stories.tsx +108 -0
  146. package/src/components/Spinner/Spinner.tsx +121 -0
  147. package/src/components/Spinner/index.ts +2 -0
  148. package/src/components/Switch/Switch.stories.tsx +116 -0
  149. package/src/components/Switch/Switch.tsx +172 -0
  150. package/src/components/Switch/index.ts +2 -0
  151. package/src/components/Table/Table.stories.tsx +417 -0
  152. package/src/components/Table/Table.tsx +233 -0
  153. package/src/components/Table/index.ts +2 -0
  154. package/src/components/Text/Text.stories.tsx +93 -0
  155. package/src/components/Text/Text.tsx +119 -0
  156. package/src/components/Text/index.ts +2 -0
  157. package/src/components/Textarea/Textarea.stories.tsx +280 -0
  158. package/src/components/Textarea/Textarea.tsx +212 -0
  159. package/src/components/Textarea/index.ts +2 -0
  160. package/src/components/Toast/Toast.stories.tsx +446 -0
  161. package/src/components/Toast/Toast.tsx +221 -0
  162. package/src/components/Toast/index.ts +2 -0
  163. package/src/components/Tooltip/Tooltip.stories.tsx +354 -0
  164. package/src/components/Tooltip/Tooltip.tsx +261 -0
  165. package/src/components/Tooltip/index.ts +2 -0
  166. package/src/components/VStack/VStack.stories.tsx +183 -0
  167. package/src/components/VStack/VStack.tsx +76 -0
  168. package/src/components/VStack/index.ts +2 -0
  169. package/src/components/index.ts +62 -0
  170. package/src/hooks/index.ts +7 -0
  171. package/src/hooks/useControllableState.ts +41 -0
  172. package/src/hooks/useDisclosure.ts +51 -0
  173. package/src/index.ts +22 -0
  174. package/src/stories/Button.stories.tsx +53 -0
  175. package/src/stories/Button.tsx +101 -0
  176. package/src/stories/Configure.mdx +364 -0
  177. package/src/stories/Header.stories.tsx +33 -0
  178. package/src/stories/Header.tsx +75 -0
  179. package/src/stories/Page.stories.tsx +25 -0
  180. package/src/stories/Page.tsx +154 -0
  181. package/src/stories/assets/accessibility.png +0 -0
  182. package/src/stories/assets/accessibility.svg +1 -0
  183. package/src/stories/assets/addon-library.png +0 -0
  184. package/src/stories/assets/assets.png +0 -0
  185. package/src/stories/assets/avif-test-image.avif +0 -0
  186. package/src/stories/assets/context.png +0 -0
  187. package/src/stories/assets/discord.svg +1 -0
  188. package/src/stories/assets/docs.png +0 -0
  189. package/src/stories/assets/figma-plugin.png +0 -0
  190. package/src/stories/assets/github.svg +1 -0
  191. package/src/stories/assets/share.png +0 -0
  192. package/src/stories/assets/styling.png +0 -0
  193. package/src/stories/assets/testing.png +0 -0
  194. package/src/stories/assets/theming.png +0 -0
  195. package/src/stories/assets/tutorials.svg +1 -0
  196. package/src/stories/assets/youtube.svg +1 -0
  197. package/src/styles/index.ts +7 -0
  198. package/src/styles/tokens.ts +318 -0
  199. package/src/styles/unistyles.ts +254 -0
  200. package/src/utils/createContext.tsx +25 -0
  201. package/src/utils/index.ts +7 -0
  202. package/src/utils/mergeRefs.ts +21 -0
  203. package/tsconfig.json +26 -0
  204. package/urbint-cl-1.0.0.tgz +0 -0
  205. package/vitest.config.ts +37 -0
  206. package/vitest.shims.d.ts +1 -0
@@ -0,0 +1,1136 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Urbint Component Library - MCP Server
4
+ *
5
+ * This MCP (Model Context Protocol) server enables AI assistants to:
6
+ * - Discover and explore all components in the design system
7
+ * - Get detailed component documentation, props, and examples
8
+ * - Access design tokens (colors, spacing, typography, etc.)
9
+ * - Generate code snippets for common patterns
10
+ * - Search components by functionality or category
11
+ */
12
+
13
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
14
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
15
+ import {
16
+ CallToolRequestSchema,
17
+ ListResourcesRequestSchema,
18
+ ListToolsRequestSchema,
19
+ ReadResourceRequestSchema,
20
+ } from '@modelcontextprotocol/sdk/types.js';
21
+
22
+ import {
23
+ componentRegistry,
24
+ getComponentByName,
25
+ getComponentsByCategory,
26
+ searchComponents,
27
+ getAllCategories,
28
+ type ComponentMetadata,
29
+ } from './registry/components.js';
30
+
31
+ import {
32
+ colorTokens,
33
+ spacingTokens,
34
+ typographyTokens,
35
+ borderRadiusTokens,
36
+ elevationTokens,
37
+ formatTokensAsMarkdown,
38
+ searchTokens,
39
+ } from './registry/tokens.js';
40
+
41
+ // Create the MCP server
42
+ const server = new Server(
43
+ {
44
+ name: 'urbint-cl',
45
+ version: '1.0.0',
46
+ },
47
+ {
48
+ capabilities: {
49
+ tools: {},
50
+ resources: {},
51
+ },
52
+ }
53
+ );
54
+
55
+ // ============================================================================
56
+ // TOOLS
57
+ // ============================================================================
58
+
59
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
60
+ return {
61
+ tools: [
62
+ {
63
+ name: 'list_components',
64
+ description: 'List all available components in the Urbint Component Library. Can filter by category.',
65
+ inputSchema: {
66
+ type: 'object',
67
+ properties: {
68
+ category: {
69
+ type: 'string',
70
+ description: 'Filter by category: layout, typography, form, feedback, data-display, overlay, action, utility',
71
+ enum: ['layout', 'typography', 'form', 'feedback', 'data-display', 'overlay', 'action', 'utility'],
72
+ },
73
+ },
74
+ },
75
+ },
76
+ {
77
+ name: 'get_component',
78
+ description: 'Get detailed documentation for a specific component including props, examples, and related components.',
79
+ inputSchema: {
80
+ type: 'object',
81
+ properties: {
82
+ name: {
83
+ type: 'string',
84
+ description: 'Component name (e.g., Button, Input, Card, Modal)',
85
+ },
86
+ },
87
+ required: ['name'],
88
+ },
89
+ },
90
+ {
91
+ name: 'search_components',
92
+ description: 'Search for components by name, description, or functionality tags.',
93
+ inputSchema: {
94
+ type: 'object',
95
+ properties: {
96
+ query: {
97
+ type: 'string',
98
+ description: 'Search query (e.g., "button", "form", "modal", "loading")',
99
+ },
100
+ },
101
+ required: ['query'],
102
+ },
103
+ },
104
+ {
105
+ name: 'get_design_tokens',
106
+ description: 'Get design tokens (colors, spacing, typography, border radius, elevation) for the design system.',
107
+ inputSchema: {
108
+ type: 'object',
109
+ properties: {
110
+ category: {
111
+ type: 'string',
112
+ description: 'Token category to retrieve',
113
+ enum: ['colors', 'spacing', 'typography', 'borderRadius', 'elevation', 'all'],
114
+ },
115
+ },
116
+ },
117
+ },
118
+ {
119
+ name: 'search_tokens',
120
+ description: 'Search for specific design tokens by name or description.',
121
+ inputSchema: {
122
+ type: 'object',
123
+ properties: {
124
+ query: {
125
+ type: 'string',
126
+ description: 'Search query (e.g., "blue", "padding", "heading")',
127
+ },
128
+ },
129
+ required: ['query'],
130
+ },
131
+ },
132
+ {
133
+ name: 'generate_component_code',
134
+ description: 'Generate code snippet for a component with specific configuration.',
135
+ inputSchema: {
136
+ type: 'object',
137
+ properties: {
138
+ component: {
139
+ type: 'string',
140
+ description: 'Component name',
141
+ },
142
+ variant: {
143
+ type: 'string',
144
+ description: 'Component variant (if applicable)',
145
+ },
146
+ withState: {
147
+ type: 'boolean',
148
+ description: 'Include useState hook for controlled components',
149
+ },
150
+ props: {
151
+ type: 'object',
152
+ description: 'Additional props to include',
153
+ },
154
+ },
155
+ required: ['component'],
156
+ },
157
+ },
158
+ {
159
+ name: 'get_form_pattern',
160
+ description: 'Get a complete form pattern code snippet with validation and styling.',
161
+ inputSchema: {
162
+ type: 'object',
163
+ properties: {
164
+ fields: {
165
+ type: 'array',
166
+ items: {
167
+ type: 'object',
168
+ properties: {
169
+ name: { type: 'string' },
170
+ type: { type: 'string', enum: ['text', 'email', 'password', 'select', 'checkbox', 'textarea', 'date'] },
171
+ label: { type: 'string' },
172
+ required: { type: 'boolean' },
173
+ },
174
+ },
175
+ description: 'Form fields configuration',
176
+ },
177
+ includeValidation: {
178
+ type: 'boolean',
179
+ description: 'Include validation logic',
180
+ },
181
+ },
182
+ required: ['fields'],
183
+ },
184
+ },
185
+ {
186
+ name: 'get_layout_pattern',
187
+ description: 'Get a layout pattern code snippet (card grid, sidebar, header, etc.)',
188
+ inputSchema: {
189
+ type: 'object',
190
+ properties: {
191
+ pattern: {
192
+ type: 'string',
193
+ description: 'Layout pattern type',
194
+ enum: ['card-grid', 'sidebar-layout', 'header-content', 'list-detail', 'dashboard', 'form-layout'],
195
+ },
196
+ },
197
+ required: ['pattern'],
198
+ },
199
+ },
200
+ ],
201
+ };
202
+ });
203
+
204
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
205
+ const { name, arguments: args } = request.params;
206
+
207
+ switch (name) {
208
+ case 'list_components': {
209
+ const category = args?.category as ComponentMetadata['category'] | undefined;
210
+ const components = category
211
+ ? getComponentsByCategory(category)
212
+ : componentRegistry;
213
+
214
+ const result = components.map((c) => ({
215
+ name: c.name,
216
+ displayName: c.displayName,
217
+ description: c.description,
218
+ category: c.category,
219
+ tags: c.tags,
220
+ }));
221
+
222
+ return {
223
+ content: [
224
+ {
225
+ type: 'text',
226
+ text: JSON.stringify(
227
+ {
228
+ total: result.length,
229
+ category: category || 'all',
230
+ components: result,
231
+ },
232
+ null,
233
+ 2
234
+ ),
235
+ },
236
+ ],
237
+ };
238
+ }
239
+
240
+ case 'get_component': {
241
+ const componentName = args?.name as string;
242
+ const component = getComponentByName(componentName);
243
+
244
+ if (!component) {
245
+ return {
246
+ content: [
247
+ {
248
+ type: 'text',
249
+ text: `Component "${componentName}" not found. Use list_components to see available components.`,
250
+ },
251
+ ],
252
+ };
253
+ }
254
+
255
+ // Format component documentation
256
+ let markdown = `# ${component.displayName}\n\n`;
257
+ markdown += `${component.description}\n\n`;
258
+ markdown += `**Category:** ${component.category}\n`;
259
+ markdown += `**Tags:** ${component.tags.join(', ')}\n\n`;
260
+ markdown += `## Import\n\n\`\`\`tsx\n${component.importPath}\n\`\`\`\n\n`;
261
+
262
+ if (component.props.length > 0) {
263
+ markdown += `## Props\n\n`;
264
+ markdown += `| Prop | Type | Default | Description |\n`;
265
+ markdown += `|------|------|---------|-------------|\n`;
266
+ component.props.forEach((prop) => {
267
+ markdown += `| ${prop.name} | \`${prop.type}\` | ${prop.default || '-'} | ${prop.description} |\n`;
268
+ });
269
+ markdown += '\n';
270
+ }
271
+
272
+ if (component.examples.length > 0) {
273
+ markdown += `## Examples\n\n`;
274
+ component.examples.forEach((example) => {
275
+ markdown += `### ${example.title}\n\n`;
276
+ if (example.description) {
277
+ markdown += `${example.description}\n\n`;
278
+ }
279
+ markdown += `\`\`\`tsx\n${example.code}\n\`\`\`\n\n`;
280
+ });
281
+ }
282
+
283
+ if (component.relatedComponents && component.relatedComponents.length > 0) {
284
+ markdown += `## Related Components\n\n`;
285
+ markdown += component.relatedComponents.join(', ') + '\n';
286
+ }
287
+
288
+ return {
289
+ content: [
290
+ {
291
+ type: 'text',
292
+ text: markdown,
293
+ },
294
+ ],
295
+ };
296
+ }
297
+
298
+ case 'search_components': {
299
+ const query = args?.query as string;
300
+ const results = searchComponents(query);
301
+
302
+ return {
303
+ content: [
304
+ {
305
+ type: 'text',
306
+ text: JSON.stringify(
307
+ {
308
+ query,
309
+ total: results.length,
310
+ components: results.map((c) => ({
311
+ name: c.name,
312
+ displayName: c.displayName,
313
+ description: c.description,
314
+ category: c.category,
315
+ import: c.importPath,
316
+ })),
317
+ },
318
+ null,
319
+ 2
320
+ ),
321
+ },
322
+ ],
323
+ };
324
+ }
325
+
326
+ case 'get_design_tokens': {
327
+ const category = (args?.category as string) || 'all';
328
+
329
+ if (category === 'all') {
330
+ return {
331
+ content: [
332
+ {
333
+ type: 'text',
334
+ text: formatTokensAsMarkdown(),
335
+ },
336
+ ],
337
+ };
338
+ }
339
+
340
+ let markdown = '';
341
+ switch (category) {
342
+ case 'colors':
343
+ markdown = '# Color Tokens\n\n';
344
+ colorTokens.forEach((cat) => {
345
+ markdown += `## ${cat.name}\n${cat.description}\n\n`;
346
+ markdown += '| Token | Value | Description |\n|-------|-------|-------------|\n';
347
+ cat.tokens.forEach((t) => {
348
+ markdown += `| \`${t.name}\` | ${t.value} | ${t.description || ''} |\n`;
349
+ });
350
+ markdown += '\n';
351
+ });
352
+ break;
353
+ case 'spacing':
354
+ markdown = `# ${spacingTokens.name}\n\n${spacingTokens.description}\n\n`;
355
+ markdown += '| Token | Value | Description |\n|-------|-------|-------------|\n';
356
+ spacingTokens.tokens.forEach((t) => {
357
+ markdown += `| \`'${t.name}'\` | ${t.value} | ${t.description || ''} |\n`;
358
+ });
359
+ break;
360
+ case 'typography':
361
+ markdown = `# ${typographyTokens.name}\n\n${typographyTokens.description}\n\n`;
362
+ markdown += '| Token | Value | Description |\n|-------|-------|-------------|\n';
363
+ typographyTokens.tokens.forEach((t) => {
364
+ markdown += `| \`${t.name}\` | ${t.value} | ${t.description || ''} |\n`;
365
+ });
366
+ break;
367
+ case 'borderRadius':
368
+ markdown = `# ${borderRadiusTokens.name}\n\n${borderRadiusTokens.description}\n\n`;
369
+ markdown += '| Token | Value | Description |\n|-------|-------|-------------|\n';
370
+ borderRadiusTokens.tokens.forEach((t) => {
371
+ markdown += `| \`'${t.name}'\` | ${t.value} | ${t.description || ''} |\n`;
372
+ });
373
+ break;
374
+ case 'elevation':
375
+ markdown = `# ${elevationTokens.name}\n\n${elevationTokens.description}\n\n`;
376
+ markdown += '| Token | Value | Description |\n|-------|-------|-------------|\n';
377
+ elevationTokens.tokens.forEach((t) => {
378
+ markdown += `| \`'${t.name}'\` | ${t.value} | ${t.description || ''} |\n`;
379
+ });
380
+ break;
381
+ }
382
+
383
+ return {
384
+ content: [
385
+ {
386
+ type: 'text',
387
+ text: markdown,
388
+ },
389
+ ],
390
+ };
391
+ }
392
+
393
+ case 'search_tokens': {
394
+ const query = args?.query as string;
395
+ const results = searchTokens(query);
396
+
397
+ return {
398
+ content: [
399
+ {
400
+ type: 'text',
401
+ text: JSON.stringify(
402
+ {
403
+ query,
404
+ total: results.length,
405
+ tokens: results,
406
+ },
407
+ null,
408
+ 2
409
+ ),
410
+ },
411
+ ],
412
+ };
413
+ }
414
+
415
+ case 'generate_component_code': {
416
+ const componentName = args?.component as string;
417
+ const variant = args?.variant as string | undefined;
418
+ const withState = args?.withState as boolean | undefined;
419
+ const customProps = args?.props as Record<string, unknown> | undefined;
420
+
421
+ const component = getComponentByName(componentName);
422
+ if (!component) {
423
+ return {
424
+ content: [
425
+ {
426
+ type: 'text',
427
+ text: `Component "${componentName}" not found.`,
428
+ },
429
+ ],
430
+ };
431
+ }
432
+
433
+ let code = '';
434
+
435
+ // Add imports
436
+ code += `${component.importPath}\n`;
437
+ if (withState) {
438
+ code += `import { useState } from 'react';\n`;
439
+ }
440
+ code += '\n';
441
+
442
+ // Generate component usage
443
+ if (withState && component.examples[0]) {
444
+ code += `function Example() {\n`;
445
+ code += ` const [value, setValue] = useState('');\n\n`;
446
+ code += ` return (\n`;
447
+ code += ` <${component.name}`;
448
+ if (variant) code += `\n variant="${variant}"`;
449
+ if (customProps) {
450
+ Object.entries(customProps).forEach(([key, val]) => {
451
+ code += `\n ${key}={${JSON.stringify(val)}}`;
452
+ });
453
+ }
454
+ code += `\n value={value}\n onChange={setValue}\n />\n`;
455
+ code += ` );\n`;
456
+ code += `}\n`;
457
+ } else if (component.examples[0]) {
458
+ code += component.examples[0].code;
459
+ }
460
+
461
+ return {
462
+ content: [
463
+ {
464
+ type: 'text',
465
+ text: `## ${component.displayName} Code\n\n\`\`\`tsx\n${code}\n\`\`\``,
466
+ },
467
+ ],
468
+ };
469
+ }
470
+
471
+ case 'get_form_pattern': {
472
+ const fields = args?.fields as Array<{
473
+ name: string;
474
+ type: string;
475
+ label: string;
476
+ required?: boolean;
477
+ }>;
478
+ const includeValidation = args?.includeValidation as boolean;
479
+
480
+ let code = `import { VStack, Input, Select, Checkbox, Textarea, DatePicker, Button, FormControl } from '@urbint/cl';\n`;
481
+ code += `import { useState } from 'react';\n\n`;
482
+ code += `function Form() {\n`;
483
+
484
+ // Generate state
485
+ fields.forEach((field) => {
486
+ const defaultValue = field.type === 'checkbox' ? 'false' : "''";
487
+ code += ` const [${field.name}, set${capitalize(field.name)}] = useState(${defaultValue});\n`;
488
+ });
489
+
490
+ if (includeValidation) {
491
+ code += ` const [errors, setErrors] = useState({});\n`;
492
+ }
493
+ code += '\n';
494
+
495
+ if (includeValidation) {
496
+ code += ` const validate = () => {\n`;
497
+ code += ` const newErrors = {};\n`;
498
+ fields.forEach((field) => {
499
+ if (field.required) {
500
+ code += ` if (!${field.name}) newErrors.${field.name} = '${field.label} is required';\n`;
501
+ }
502
+ });
503
+ code += ` setErrors(newErrors);\n`;
504
+ code += ` return Object.keys(newErrors).length === 0;\n`;
505
+ code += ` };\n\n`;
506
+ }
507
+
508
+ code += ` const handleSubmit = () => {\n`;
509
+ if (includeValidation) {
510
+ code += ` if (!validate()) return;\n`;
511
+ }
512
+ code += ` // Submit form data\n`;
513
+ code += ` console.log({ ${fields.map((f) => f.name).join(', ')} });\n`;
514
+ code += ` };\n\n`;
515
+
516
+ code += ` return (\n`;
517
+ code += ` <VStack space="lg">\n`;
518
+
519
+ fields.forEach((field) => {
520
+ const errorProp = includeValidation
521
+ ? `\n isInvalid={!!errors.${field.name}}\n errorMessage={errors.${field.name}}`
522
+ : '';
523
+
524
+ switch (field.type) {
525
+ case 'text':
526
+ case 'email':
527
+ case 'password':
528
+ code += ` <Input\n`;
529
+ code += ` label="${field.label}"${field.required ? '\n isRequired' : ''}\n`;
530
+ code += ` value={${field.name}}\n`;
531
+ code += ` onChange={set${capitalize(field.name)}}${errorProp}${field.type === 'password' ? '\n isPassword' : ''}\n`;
532
+ code += ` />\n`;
533
+ break;
534
+ case 'select':
535
+ code += ` <Select\n`;
536
+ code += ` label="${field.label}"${field.required ? '\n isRequired' : ''}\n`;
537
+ code += ` value={${field.name}}\n`;
538
+ code += ` onChange={set${capitalize(field.name)}}\n`;
539
+ code += ` options={[/* Add your options */]}${errorProp}\n`;
540
+ code += ` />\n`;
541
+ break;
542
+ case 'checkbox':
543
+ code += ` <Checkbox\n`;
544
+ code += ` label="${field.label}"\n`;
545
+ code += ` isChecked={${field.name}}\n`;
546
+ code += ` onChange={set${capitalize(field.name)}}\n`;
547
+ code += ` />\n`;
548
+ break;
549
+ case 'textarea':
550
+ code += ` <Textarea\n`;
551
+ code += ` label="${field.label}"${field.required ? '\n isRequired' : ''}\n`;
552
+ code += ` value={${field.name}}\n`;
553
+ code += ` onChange={set${capitalize(field.name)}}${errorProp}\n`;
554
+ code += ` />\n`;
555
+ break;
556
+ case 'date':
557
+ code += ` <DatePicker\n`;
558
+ code += ` label="${field.label}"${field.required ? '\n isRequired' : ''}\n`;
559
+ code += ` value={${field.name}}\n`;
560
+ code += ` onChange={set${capitalize(field.name)}}${errorProp}\n`;
561
+ code += ` />\n`;
562
+ break;
563
+ }
564
+ });
565
+
566
+ code += ` <Button onPress={handleSubmit}>Submit</Button>\n`;
567
+ code += ` </VStack>\n`;
568
+ code += ` );\n`;
569
+ code += `}\n`;
570
+
571
+ return {
572
+ content: [
573
+ {
574
+ type: 'text',
575
+ text: `## Generated Form\n\n\`\`\`tsx\n${code}\n\`\`\``,
576
+ },
577
+ ],
578
+ };
579
+ }
580
+
581
+ case 'get_layout_pattern': {
582
+ const pattern = args?.pattern as string;
583
+ let code = '';
584
+
585
+ switch (pattern) {
586
+ case 'card-grid':
587
+ code = `import { Grid, GridItem, Card, CardBody, Heading, Text } from '@urbint/cl';
588
+
589
+ function CardGrid({ items }) {
590
+ return (
591
+ <Grid columns={3} gap="lg">
592
+ {items.map((item) => (
593
+ <GridItem key={item.id}>
594
+ <Card variant="elevated">
595
+ <CardBody>
596
+ <Heading as="h3">{item.title}</Heading>
597
+ <Text variant="caption" color="text.secondary">
598
+ {item.description}
599
+ </Text>
600
+ </CardBody>
601
+ </Card>
602
+ </GridItem>
603
+ ))}
604
+ </Grid>
605
+ );
606
+ }`;
607
+ break;
608
+
609
+ case 'sidebar-layout':
610
+ code = `import { HStack, VStack, Box, Text, Link, Divider } from '@urbint/cl';
611
+
612
+ function SidebarLayout({ children }) {
613
+ return (
614
+ <HStack style={{ minHeight: '100vh' }}>
615
+ {/* Sidebar */}
616
+ <Box w={240} bg="background.secondary" py="lg">
617
+ <VStack space="xs" px="md">
618
+ <Text weight="semiBold" variant="label">Navigation</Text>
619
+ <Divider />
620
+ <Link href="/dashboard">Dashboard</Link>
621
+ <Link href="/settings">Settings</Link>
622
+ <Link href="/profile">Profile</Link>
623
+ </VStack>
624
+ </Box>
625
+
626
+ {/* Main Content */}
627
+ <Box flex={1} p="xl">
628
+ {children}
629
+ </Box>
630
+ </HStack>
631
+ );
632
+ }`;
633
+ break;
634
+
635
+ case 'header-content':
636
+ code = `import { VStack, HStack, Box, Heading, Text, Button, Avatar } from '@urbint/cl';
637
+
638
+ function PageLayout({ title, subtitle, children }) {
639
+ return (
640
+ <VStack space="none" style={{ minHeight: '100vh' }}>
641
+ {/* Header */}
642
+ <Box bg="brand.navy" px="xl" py="md">
643
+ <HStack justifyContent="space-between">
644
+ <Heading as="h1" color="text.inverse">{title}</Heading>
645
+ <HStack space="md">
646
+ <Button variant="ghost">Help</Button>
647
+ <Avatar name="User" size="sm" />
648
+ </HStack>
649
+ </HStack>
650
+ </Box>
651
+
652
+ {/* Content */}
653
+ <Box flex={1} p="xl" bg="background.secondary">
654
+ {subtitle && (
655
+ <Text variant="caption" color="text.secondary" style={{ marginBottom: 16 }}>
656
+ {subtitle}
657
+ </Text>
658
+ )}
659
+ {children}
660
+ </Box>
661
+ </VStack>
662
+ );
663
+ }`;
664
+ break;
665
+
666
+ case 'list-detail':
667
+ code = `import { HStack, VStack, Box, Card, CardBody, Text, Pressable, Heading } from '@urbint/cl';
668
+ import { useState } from 'react';
669
+
670
+ function ListDetailLayout({ items }) {
671
+ const [selected, setSelected] = useState(items[0]);
672
+
673
+ return (
674
+ <HStack space="lg" alignItems="flex-start">
675
+ {/* List */}
676
+ <VStack space="sm" w={300}>
677
+ {items.map((item) => (
678
+ <Pressable key={item.id} onPress={() => setSelected(item)}>
679
+ <Card variant={selected?.id === item.id ? 'elevated' : 'outline'}>
680
+ <CardBody>
681
+ <Text weight="semiBold">{item.title}</Text>
682
+ <Text variant="caption" numberOfLines={2}>
683
+ {item.preview}
684
+ </Text>
685
+ </CardBody>
686
+ </Card>
687
+ </Pressable>
688
+ ))}
689
+ </VStack>
690
+
691
+ {/* Detail */}
692
+ <Card flex={1} variant="elevated">
693
+ <CardBody>
694
+ <Heading as="h2">{selected?.title}</Heading>
695
+ <Text>{selected?.content}</Text>
696
+ </CardBody>
697
+ </Card>
698
+ </HStack>
699
+ );
700
+ }`;
701
+ break;
702
+
703
+ case 'dashboard':
704
+ code = `import { VStack, HStack, Grid, GridItem, Card, CardHeader, CardBody, Heading, Text, Progress, Badge } from '@urbint/cl';
705
+
706
+ function Dashboard() {
707
+ return (
708
+ <VStack space="xl" p="xl">
709
+ {/* Stats Row */}
710
+ <Grid columns={4} gap="lg">
711
+ <GridItem>
712
+ <Card variant="elevated">
713
+ <CardBody>
714
+ <Text variant="caption" color="text.secondary">Total Users</Text>
715
+ <Heading as="h2">1,234</Heading>
716
+ <Badge variant="green">+12%</Badge>
717
+ </CardBody>
718
+ </Card>
719
+ </GridItem>
720
+ <GridItem>
721
+ <Card variant="elevated">
722
+ <CardBody>
723
+ <Text variant="caption" color="text.secondary">Revenue</Text>
724
+ <Heading as="h2">$45,678</Heading>
725
+ <Badge variant="green">+8%</Badge>
726
+ </CardBody>
727
+ </Card>
728
+ </GridItem>
729
+ <GridItem>
730
+ <Card variant="elevated">
731
+ <CardBody>
732
+ <Text variant="caption" color="text.secondary">Active Projects</Text>
733
+ <Heading as="h2">23</Heading>
734
+ </CardBody>
735
+ </Card>
736
+ </GridItem>
737
+ <GridItem>
738
+ <Card variant="elevated">
739
+ <CardBody>
740
+ <Text variant="caption" color="text.secondary">Completion</Text>
741
+ <Heading as="h2">78%</Heading>
742
+ <Progress value={78} size="sm" />
743
+ </CardBody>
744
+ </Card>
745
+ </GridItem>
746
+ </Grid>
747
+
748
+ {/* Main Content */}
749
+ <HStack space="lg" alignItems="flex-start">
750
+ <Card flex={2} variant="elevated">
751
+ <CardHeader>
752
+ <Heading as="h3">Recent Activity</Heading>
753
+ </CardHeader>
754
+ <CardBody>
755
+ {/* Activity content */}
756
+ </CardBody>
757
+ </Card>
758
+ <Card flex={1} variant="elevated">
759
+ <CardHeader>
760
+ <Heading as="h3">Quick Actions</Heading>
761
+ </CardHeader>
762
+ <CardBody>
763
+ {/* Actions content */}
764
+ </CardBody>
765
+ </Card>
766
+ </HStack>
767
+ </VStack>
768
+ );
769
+ }`;
770
+ break;
771
+
772
+ case 'form-layout':
773
+ code = `import { VStack, HStack, Card, CardHeader, CardBody, CardFooter, Heading, Input, Select, Button, Divider } from '@urbint/cl';
774
+
775
+ function FormLayout({ title, onSubmit, onCancel }) {
776
+ return (
777
+ <Card variant="elevated" maxW={600}>
778
+ <CardHeader>
779
+ <Heading as="h2">{title}</Heading>
780
+ </CardHeader>
781
+ <CardBody>
782
+ <VStack space="lg">
783
+ <HStack space="md">
784
+ <Input label="First Name" flex={1} />
785
+ <Input label="Last Name" flex={1} />
786
+ </HStack>
787
+ <Input label="Email" placeholder="email@example.com" />
788
+ <Select
789
+ label="Role"
790
+ options={[
791
+ { value: 'admin', label: 'Administrator' },
792
+ { value: 'user', label: 'User' },
793
+ { value: 'viewer', label: 'Viewer' },
794
+ ]}
795
+ />
796
+ <Divider />
797
+ <Input label="Notes" placeholder="Additional notes..." />
798
+ </VStack>
799
+ </CardBody>
800
+ <CardFooter>
801
+ <HStack space="md" justifyContent="flex-end">
802
+ <Button variant="ghost" onPress={onCancel}>Cancel</Button>
803
+ <Button variant="primary" onPress={onSubmit}>Save</Button>
804
+ </HStack>
805
+ </CardFooter>
806
+ </Card>
807
+ );
808
+ }`;
809
+ break;
810
+
811
+ default:
812
+ return {
813
+ content: [
814
+ {
815
+ type: 'text',
816
+ text: `Unknown pattern: ${pattern}. Available patterns: card-grid, sidebar-layout, header-content, list-detail, dashboard, form-layout`,
817
+ },
818
+ ],
819
+ };
820
+ }
821
+
822
+ return {
823
+ content: [
824
+ {
825
+ type: 'text',
826
+ text: `## ${pattern} Pattern\n\n\`\`\`tsx\n${code}\n\`\`\``,
827
+ },
828
+ ],
829
+ };
830
+ }
831
+
832
+ default:
833
+ return {
834
+ content: [
835
+ {
836
+ type: 'text',
837
+ text: `Unknown tool: ${name}`,
838
+ },
839
+ ],
840
+ };
841
+ }
842
+ });
843
+
844
+ // ============================================================================
845
+ // RESOURCES
846
+ // ============================================================================
847
+
848
+ server.setRequestHandler(ListResourcesRequestSchema, async () => {
849
+ return {
850
+ resources: [
851
+ {
852
+ uri: 'urbint://docs/overview',
853
+ name: 'Overview',
854
+ description: 'Urbint Component Library overview and getting started guide',
855
+ mimeType: 'text/markdown',
856
+ },
857
+ {
858
+ uri: 'urbint://docs/components',
859
+ name: 'Components',
860
+ description: 'Complete list of all available components',
861
+ mimeType: 'text/markdown',
862
+ },
863
+ {
864
+ uri: 'urbint://docs/tokens',
865
+ name: 'Design Tokens',
866
+ description: 'All design tokens (colors, spacing, typography, etc.)',
867
+ mimeType: 'text/markdown',
868
+ },
869
+ {
870
+ uri: 'urbint://docs/installation',
871
+ name: 'Installation',
872
+ description: 'Installation and setup instructions',
873
+ mimeType: 'text/markdown',
874
+ },
875
+ ...componentRegistry.map((c) => ({
876
+ uri: `urbint://component/${c.name.toLowerCase()}`,
877
+ name: c.displayName,
878
+ description: c.description,
879
+ mimeType: 'text/markdown',
880
+ })),
881
+ ],
882
+ };
883
+ });
884
+
885
+ server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
886
+ const { uri } = request.params;
887
+
888
+ if (uri === 'urbint://docs/overview') {
889
+ return {
890
+ contents: [
891
+ {
892
+ uri,
893
+ mimeType: 'text/markdown',
894
+ text: `# Urbint Component Library
895
+
896
+ > Enterprise-ready React Native component library built with Unistyles 3.0
897
+
898
+ ## Overview
899
+
900
+ Urbint Component Library (\`@urbint/cl\`) provides 40+ production-ready components for B2B SAAS applications across iOS, Android, and Web platforms.
901
+
902
+ ## Key Features
903
+
904
+ - **Cross-platform**: iOS, Android, Web
905
+ - **Full TypeScript support**
906
+ - **Light and Dark themes**
907
+ - **Accessible** (WCAG 2.1 compliant)
908
+ - **Built with Unistyles 3.0**
909
+
910
+ ## Tech Stack
911
+
912
+ - React Native 0.81+
913
+ - Unistyles 3.0+
914
+ - TypeScript 5.0+
915
+ - Expo SDK 54+
916
+
917
+ ## Quick Start
918
+
919
+ \`\`\`bash
920
+ npm install @urbint/cl react-native-unistyles
921
+ \`\`\`
922
+
923
+ \`\`\`tsx
924
+ import '@urbint/cl/styles';
925
+ import { Box, Text, Button } from '@urbint/cl';
926
+
927
+ export default function App() {
928
+ return (
929
+ <Box p="lg">
930
+ <Text>Hello World</Text>
931
+ <Button variant="primary">Click Me</Button>
932
+ </Box>
933
+ );
934
+ }
935
+ \`\`\`
936
+
937
+ ## Component Categories
938
+
939
+ - **Layout**: Box, HStack, VStack, Center, Grid, Divider
940
+ - **Typography**: Text, Heading, Link
941
+ - **Form**: Button, Input, Select, DatePicker, Checkbox, Radio, Switch, Textarea, Slider, FormControl
942
+ - **Feedback**: Alert, Toast, Progress, Spinner, Skeleton
943
+ - **Data Display**: Avatar, Badge, Card, Table, Icon
944
+ - **Overlay**: Modal, Drawer, AlertDialog, Popover, Tooltip, Menu, ActionSheet
945
+ - **Action**: Pressable, Fab, Accordion
946
+ - **Utility**: Portal
947
+ `,
948
+ },
949
+ ],
950
+ };
951
+ }
952
+
953
+ if (uri === 'urbint://docs/components') {
954
+ let markdown = '# All Components\n\n';
955
+
956
+ const categories = getAllCategories();
957
+ categories.forEach((category) => {
958
+ const components = getComponentsByCategory(category);
959
+ markdown += `## ${capitalize(category)}\n\n`;
960
+ components.forEach((c) => {
961
+ markdown += `### ${c.displayName}\n`;
962
+ markdown += `${c.description}\n\n`;
963
+ markdown += `\`\`\`tsx\n${c.importPath}\n\`\`\`\n\n`;
964
+ });
965
+ });
966
+
967
+ return {
968
+ contents: [
969
+ {
970
+ uri,
971
+ mimeType: 'text/markdown',
972
+ text: markdown,
973
+ },
974
+ ],
975
+ };
976
+ }
977
+
978
+ if (uri === 'urbint://docs/tokens') {
979
+ return {
980
+ contents: [
981
+ {
982
+ uri,
983
+ mimeType: 'text/markdown',
984
+ text: formatTokensAsMarkdown(),
985
+ },
986
+ ],
987
+ };
988
+ }
989
+
990
+ if (uri === 'urbint://docs/installation') {
991
+ return {
992
+ contents: [
993
+ {
994
+ uri,
995
+ mimeType: 'text/markdown',
996
+ text: `# Installation
997
+
998
+ ## Package Installation
999
+
1000
+ \`\`\`bash
1001
+ # npm
1002
+ npm install @urbint/cl react-native-unistyles
1003
+
1004
+ # yarn
1005
+ yarn add @urbint/cl react-native-unistyles
1006
+
1007
+ # Local development
1008
+ npm link ../path-to-urbint-cl
1009
+ \`\`\`
1010
+
1011
+ ## Babel Configuration
1012
+
1013
+ Add the Unistyles Babel plugin to \`babel.config.js\`:
1014
+
1015
+ \`\`\`javascript
1016
+ module.exports = function (api) {
1017
+ api.cache(true);
1018
+ return {
1019
+ presets: ['babel-preset-expo'],
1020
+ plugins: [
1021
+ ['react-native-unistyles/plugin', { root: 'src' }],
1022
+ ],
1023
+ };
1024
+ };
1025
+ \`\`\`
1026
+
1027
+ ## App Setup
1028
+
1029
+ Import styles in your app entry point before any components:
1030
+
1031
+ \`\`\`tsx
1032
+ // App.tsx
1033
+ import '@urbint/cl/styles';
1034
+ import { Box, Text, Button } from '@urbint/cl';
1035
+
1036
+ export default function App() {
1037
+ return (
1038
+ <Box p="lg">
1039
+ <Text>Hello World</Text>
1040
+ <Button variant="primary">Click Me</Button>
1041
+ </Box>
1042
+ );
1043
+ }
1044
+ \`\`\`
1045
+
1046
+ ## Troubleshooting
1047
+
1048
+ ### Metro bundler issues
1049
+ \`\`\`bash
1050
+ npx expo start --clear
1051
+ \`\`\`
1052
+
1053
+ ### Unistyles not working
1054
+ 1. Ensure Babel plugin is configured
1055
+ 2. Restart Metro bundler
1056
+ 3. Check import order (styles before components)
1057
+ `,
1058
+ },
1059
+ ],
1060
+ };
1061
+ }
1062
+
1063
+ // Handle component-specific resources
1064
+ const componentMatch = uri.match(/^urbint:\/\/component\/(.+)$/);
1065
+ if (componentMatch) {
1066
+ const componentName = componentMatch[1];
1067
+ const component = getComponentByName(componentName);
1068
+
1069
+ if (component) {
1070
+ let markdown = `# ${component.displayName}\n\n`;
1071
+ markdown += `${component.description}\n\n`;
1072
+ markdown += `**Category:** ${component.category}\n`;
1073
+ markdown += `**Tags:** ${component.tags.join(', ')}\n\n`;
1074
+ markdown += `## Import\n\n\`\`\`tsx\n${component.importPath}\n\`\`\`\n\n`;
1075
+
1076
+ if (component.props.length > 0) {
1077
+ markdown += `## Props\n\n`;
1078
+ markdown += `| Prop | Type | Default | Description |\n`;
1079
+ markdown += `|------|------|---------|-------------|\n`;
1080
+ component.props.forEach((prop) => {
1081
+ markdown += `| ${prop.name} | \`${prop.type}\` | ${prop.default || '-'} | ${prop.description} |\n`;
1082
+ });
1083
+ markdown += '\n';
1084
+ }
1085
+
1086
+ if (component.examples.length > 0) {
1087
+ markdown += `## Examples\n\n`;
1088
+ component.examples.forEach((example) => {
1089
+ markdown += `### ${example.title}\n\n`;
1090
+ markdown += `\`\`\`tsx\n${example.code}\n\`\`\`\n\n`;
1091
+ });
1092
+ }
1093
+
1094
+ return {
1095
+ contents: [
1096
+ {
1097
+ uri,
1098
+ mimeType: 'text/markdown',
1099
+ text: markdown,
1100
+ },
1101
+ ],
1102
+ };
1103
+ }
1104
+ }
1105
+
1106
+ return {
1107
+ contents: [
1108
+ {
1109
+ uri,
1110
+ mimeType: 'text/plain',
1111
+ text: `Resource not found: ${uri}`,
1112
+ },
1113
+ ],
1114
+ };
1115
+ });
1116
+
1117
+ // ============================================================================
1118
+ // HELPERS
1119
+ // ============================================================================
1120
+
1121
+ function capitalize(str: string): string {
1122
+ return str.charAt(0).toUpperCase() + str.slice(1);
1123
+ }
1124
+
1125
+ // ============================================================================
1126
+ // START SERVER
1127
+ // ============================================================================
1128
+
1129
+ async function main() {
1130
+ const transport = new StdioServerTransport();
1131
+ await server.connect(transport);
1132
+ console.error('Urbint CL MCP Server running on stdio');
1133
+ }
1134
+
1135
+ main().catch(console.error);
1136
+